ubuntu实现unix domain socket通信

来源:互联网 发布:动漫小说软件 编辑:程序博客网 时间:2024/05/28 05:18

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时,出现了一个问题:




查看程序发现,这里调用bind函数需要创建一个socket文件,因此需要对目录有写的权限,利用ls命令查看目录发现目录只有读和执行的权限,

因此利用chmod更改目录权限:sudo chmod -R 777 /myfiles/test

改后重新执行程序,成功运行server端程序

接着打开一个新的终端,运行client程序,两者通信成功



在这里发现代码有一些bug,比如当终止client服务时,server端还是会显示,调用recv函数成功。如下所示




查看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时,则显示网络断开即可。





0 0
原创粉丝点击