unix domain 与本地本地回环在进程间通信中的差异

发布时间 2023-04-20 19:38:33作者: liwen01

前言:

127.0.0.1它是一个私有IP,代表的就是你的本机环回地址,其实本质上是绑定在虚拟网卡loopback上的IP。
在实际应用中,有遇到在使用本地回环做进程间通讯的时候程序阻塞的情况。比如下面代码

(一)本地回环:

客户端数据收发程序:

static int send_recv(char *cmd, int *ret, char* strResult, int nSize)
{
	struct sockaddr_in sin;
	struct sockaddr_in cin;
	int port = PORT_FOR_SYSTEM;
	socklen_t addr_len;
	int s_fd;
	int n;
	ST_CMD_RESULT stCmdResult = {0};
	fd_set rfds;
    ST_CMD stCmd = {0};

    if (0 >= nSize || NULL == strResult)
    	stCmd.bNeedResult = 0;
    else
    	stCmd.bNeedResult = 1;
    strncpy(stCmd.strCmd, cmd, MAX_CMD_LEN);
    
    bzero(&sin, sizeof(sin));
    sin.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr);
    sin.sin_port = htons(port);
    
    s_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(s_fd == -1)
    {
    	printf("create socket failed!\n");
    	return -1;
    }
    
    n = sendto(s_fd, &stCmd, sizeof(stCmd.bNeedResult) + strlen(stCmd.strCmd) + 1, 0, (struct sockaddr *) &sin, sizeof(sin));
    if(n == -1)
    {
    	printf("send failed!\n");
    	close(s_fd);
    	return -1;
    }
    
    FD_ZERO(&rfds);
    FD_SET(s_fd, &rfds);
    
    if(stCmd.bNeedResult)
    {
    	struct timeval tv_out;
    	tv_out.tv_sec = 5;
    	tv_out.tv_usec = 0;
    	n = select(s_fd+1, &rfds, NULL, NULL, &tv_out);    
    }
    else
    {
    	n = select(s_fd+1, &rfds, NULL, NULL, NULL); 
    }
    
    if (n == -1)
    {
    	printf("select error!\n");
    	close(s_fd);
    	return -1;
    }
    else if(n == 0)
    {
    	printf("recvfrom timeout!\n");
    	close(s_fd);
        return -1;
    }
    
    addr_len = sizeof(cin);
    n = recvfrom(s_fd, &stCmdResult, sizeof(stCmdResult), 0, (struct sockaddr *) &cin, &addr_len);
    *ret = stCmdResult.ret;
    strncpy(strResult, stCmdResult.strResult, nSize);
    if(n == -1)
    {
    	printf("recvfrom failed!\n");
    	close(s_fd);
    	return -1;
    }
    
    close(s_fd);
    
    return 0;
}

上面接口主要实现下面几个功能:

  1. 以本地环形地址为目标IP建立一个UDP链接
  2. 往链接中写入数据
  3. 根据是否需要应答设置不同的超时模式
  4. 接收服务端返回的数据

问题现象:

有时候发送数据的时候,该接口会被阻塞

问题解析:

1.当strResult 为空的时候,select 没有设置超时,这里会一直阻塞

根本原因:

为什么在这里select会一直阻塞?

是因为服务端没有返回数据到客户端,所以客户端就被一直阻塞了。

为什么服务端会没有数据返回?

除去服务端软件上的问题,还有一个原因是:因为使用的是本地回环,在一些嵌入式设备中,如果网卡驱动注册太慢,或者是没有注册网卡驱动,本地回环是不能工作的,所以服务端也就是会监听不到客户端的数据

对于这种情况,如果使用Unix domain socket(UDS) 就不会存在这样的问题。

(二)Unix domain socket(UDS)

UNIX Domain Socket是在socket架构上发展起来的用于同一台主机的进程间通讯(IPC),它不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。

UNIX Domain Socket有SOCK_DGRAM或SOCK_STREAM两种工作模式,类似于UDP和TCP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。

UNIX Domain Socket可用于两个没有亲缘关系的进程,是全双工的,是目前使用最广泛的IPC机制,比如X Window服务器和GUI程序之间就是通过UNIX Domain Socket通讯的。

工作流程

UNIX Domain socket与网络socket类似,可以与网络socket对比应用。

上述二者编程的不同如下:

  1. address family为AF_UNIX
  2. 因为应用于IPC,所以UNIXDomain socket不需要IP和端口,取而代之的是文件路径来表示“网络地址”。这点体现在下面两个方面。
  3. 地址格式不同,UNIXDomain socket用结构体sockaddr_un表示,是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。
  4. UNIX Domain Socket客户端一般要显式调用bind函数,而不象网络socket一样依赖系统自动分配的地址。客户端bind的socket文件名可以包含客户端的pid,这样服务器就可以区分不同的客户端。

---------------------------End---------------------------

长按识别二维码
关注 liwen01 公众号