AF_UNIX实现linux本地socket通信的笔记

阅读更多

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);

 

 

 

你可能感兴趣的:(AF_UNIX实现linux本地socket通信的笔记)