AF_UNIX与AF_LOCAL是一样的,只是历史遗留原因。
有SOCK_STREAM、SOCK_DGRAM、SOCK_SEQPACKET三种工作模式
1、SOCK_STREAM
流字节套,类似TCP,由于socket发送缓冲区的缘故,多次write数据会被缓冲区整合为一次底层send。禁用TCP Nagle算法的方式对AF_UNIX无效。
#include// for IPPROTO_TCP #include // for TCP_NODELAY int nodelay = 1; ret = setsockopt(mSocket, IPPROTO_TCP, TCP_NODELAY,(void*)&nodelay,sizeof(nodelay)); if (ret == -1) { printf("Couldn't setsockopt(TCP_NODELAY)\n"); }
2、SOCK_DGRAM
报文字节套,类似UDP的报文方式,理论上会导致错乱、丢失等风险,只是AF_UNIX性能很高,风险概率较小。SOCK_DGRAM的应用场合很少,因为流式套接字在本地的连接时间可以忽略,而SOCK_DGRAM发送接收都需要携带对方的本地地址,所以效率并没有提高。
3、SOCK_SEQPACKET
SOCK_SEQPACKET提供一个顺序确定的,可靠的,双向基于连接的socket endpoint. 与SOCK_STREAM不同的是,它保留消息边界。(表明发送两个数据包,只能分两次读入)使用SOCK_SEQPACKET工作模式,就可以保证每次write都会发起底层send。
mSocket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
但这样接收端延迟非常严重。
4、设置发送端缓冲区容量
int ret = 0; int snd_buf_size = 0; socklen_t opt_size = sizeof(snd_buf_size); /* 获得sndbuf的长度 */ ret = getsockopt(mSocket, SOL_SOCKET, SO_SNDBUF, &snd_buf_size, &opt_size); printf("====================%d\n", snd_buf_size); /* 设置sndbuf的长度 */ int m = 100; // 无法低于系统默认,比如这里虽然设100,实际最小只能到2048 setsockopt(mSocket, SOL_SOCKET, SO_SNDBUF, (const char*)&m, sizeof(m)); if (ret == -1) { printf("====================Couldn't setsockopt(SO_SNDBUF)\n"); } ret = getsockopt(mSocket, SOL_SOCKET, SO_SNDBUF, &snd_buf_size, &opt_size); printf("====================%d\n", snd_buf_size);
所以仍然会有多次write一次send的现象。
5,屏蔽中断
如server已关闭,client继续write就会导致SIGPIPE中断,从而进程中断。
// 当write已关闭socket时会触发SIGPIPE,需要屏蔽 sigset_t signal_mask; sigemptyset(&signal_mask); /* 初始化信号集,并清除signal_mask中的所有信号 */ sigaddset(&signal_mask, SIGPIPE); /* 将signo添加到信号集中 */ sigprocmask(SIG_BLOCK, &signal_mask, NULL); /* 这个进程屏蔽掉signo信号 */
6,命名socket与非命名socket
命名socket必须具备“/tmp/xxx”的操作权限,因为会在该路径创建链路文件,直到unlink解除。
/* 服务器端 */ int server_len; sockaddr_un s_un; s_un.sun_family = AF_UNIX; strcpy(s_un.sun_path, "/tmp/xxx"); server_len = sizeof(struct sockaddr_un); bind(mSocket, (sockaddr*)&s_un, server_len); // 解除旧链路 unlink("/tmp/xxx"); /* 客户端 */ sockaddr_un mSockAddr.sun_family = AF_LOCAL; strcpy(mSockAddr.sun_path, "/tmp/xxx"); int mSockAddrLen = sizeof(sockaddr_un); connect(mSocket, (sockaddr*)&mSockAddr, mSockAddrLen);
而非命名socket没有这个限制,但要求必须以@开头声明链路且s_un.sun_path[0]=0。
#include// for offsetof #define SERVER_NAME "@socket_server" /* 服务器端 */ int server_len; sockaddr_un s_un; s_un.sun_family = AF_UNIX; strcpy(s_un.sun_path, SERVER_NAME); s_un.sun_path[0]=0; int server_len = strlen(SERVER_NAME) + offsetof(struct sockaddr_un, sun_path); bind(mSocket, (sockaddr*)&s_un, server_len); /* 客户端 */ sockaddr_un mSockAddr.sun_family = AF_LOCAL; mSockAddr.sun_path[0]=0; int mSockAddrLen = strlen(SERVER_NAME) + offsetof(struct sockaddr_un, sun_path); connect(mSocket, (sockaddr*)&mSockAddr, mSockAddrLen);