红联Linux门户
Linux帮助

ubuntu实现unix domain socket通信

发布时间:2016-09-19 15:07:22来源:linux网站作者:hahachenchen789
socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket。虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。
UNIX Domain Socket是全双工的,API接口语义丰富,相比其它IPC机制有明显的优越性,目前已成为使用最广泛的IPC机制,比如X Window服务器和GUI程序之间就是通过UNIX Domain Socket通讯的。
使用UNIX Domain Socket的过程和网络socket十分相似,也要先调用socket()创建一个socket文件描述符,address family指定为AF_UNIX,type可以选择SOCK_DGRAM或SOCK_STREAM,protocol参数仍然指定为0即可。
unix domain的socket通信和网络socket不同的地方在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。
 
接下来是server端代码和client代码:
 
/****************** server program *****************/  
#include <stdio.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <string.h>  
#include <stdlib.h>  
#include <sys/un.h>  
int main()  
{  
int sockfd,newfd,ret,recv_num,recv_num_total=0;  
char buf[50];  
struct sockaddr_un server_addr;  
remove("/myfiles/test/server.socket");/*不管有没有,先删除一下,否则如果该文件已经存在的的话,bind会失败。*/  
memset(&server_addr,0,sizeof(server_addr));  
server_addr.sun_family=AF_UNIX;  
strcpy(server_addr.sun_path,"/myfiles/test/server.socket");  
sockfd=socket(AF_UNIX,SOCK_STREAM,0);  
if (sockfd<0)  
{  
printf("调用socket函数建立socket描述符出错!\n");  
exit(1);  
}  
printf("调用socket函数建立socket描述符成功!\n");  
ret=bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(server_addr));  
if (ret<0)  
{  
printf("调用bind函数绑定套接字与地址出错!\n");  
exit(2);  
}  
printf("调用bind函数绑定套接字与地址成功!\n");  
ret=listen(sockfd,4);  
if (ret<0)  
{  
printf("调用listen函数出错,无法宣告服务器已经可以接受连接!\n");  
exit(3);  
}  
printf("调用listen函数成功,宣告服务器已经可以接受连接请求!\n");  
newfd=accept(sockfd,NULL,NULL);/*newfd连接到调用connect的客户端*/  
if (newfd<0)  
{  
printf("调用accept函数出错,无法接受连接请求,建立连接失败!\n");  
exit(4);  
}  
printf("调用accept函数成功,服务器与客户端建立连接成功!\n");  
while (1)  
{  
recv_num=recv(newfd,buf,24,0);  
if (recv_num<0)  
printf("调用recv接受失败!\n");  
else  
{  
recv_num_total+=recv_num;  
printf("调用recv函数成功,本次接受到%d个字节,内容为:\"%s\"。共受到%d个字节的数据。\n",recv_num,buf,recv_num_total);  
}  
sleep(2);  
}  
}  
 
/****************** client program *****************/  
#include <stdio.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <string.h>  
#include <stdlib.h>  
#include <sys/un.h>  
int main()  
{  
int sockfd,ret,send_num,send_num_total=0;  
char buf[]="this is my socket data.";  
struct sockaddr_un server_addr;  
memset(&server_addr,0,sizeof(server_addr));  
server_addr.sun_family=AF_UNIX;  
strcpy(server_addr.sun_path,"/myfiles/test/server.socket");  
sockfd=socket(AF_UNIX,SOCK_STREAM,0);  
if (sockfd<0)  
{  
printf("调用socket函数建立socket描述符出错!\n");  
exit(1);  
}  
printf("调用socket函数建立socket描述符成功!\n");  
ret=connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(server_addr));  
if (ret<0)  
{  
printf("调用connect函数失败,客户端连接服务器失败!\n ");  
exit(2);  
}  
printf("调用connect函数成功,客户端连接服务器成功!\n");  
while (1)  
{
send_num=send(sockfd,buf,sizeof(buf),MSG_DONTWAIT);  
if (send_num<0)  
printf("调用send函数失败!");  
else  
{  
send_num_total+=send_num;  
printf("调用send函数成功,本次发送%d个字节,内容为:\"%s\"。目前共发送了%d个字节的数据。\n",send_num,buf,send_num_total);  
}  
sleep(2);  
}  
}
 
两个程序通过gcc编译完成后,运行server时,出现了一个问题:
ubuntu实现unix domain socket通信
 
查看程序发现,这里调用bind函数需要创建一个socket文件,因此需要对目录有写的权限,利用ls命令查看目录发现目录只有读和执行的权限。
因此利用chmod更改目录权限:sudo chmod -R 777 /myfiles/test
改后重新执行程序,成功运行server端程序
接着打开一个新的终端,运行client程序,两者通信成功
ubuntu实现unix domain socket通信
 
在这里发现代码有一些bug,比如当终止client服务时,server端还是会显示,调用recv函数成功。如下所示:
ubuntu实现unix domain socket通信
 
查看recv函数的用法可以发现:
当应用程序调用recv函数时
(1)recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR,
(2)如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,直到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以 在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),
recv函数返回其实际copy的字节数。如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
上述情况属于网络中断,recv返回的是0。因此只要加上一个判断,当recv返回的数据是0时,则显示网络断开即可。
ubuntu实现unix domain socket通信
 
本文永久更新地址:http://www.linuxdiyf.com/linux/24298.html