redis anet网络通信的源码分析

ANET是Redis对网络通信的简化封装,涉及TCP连接阻塞、保活定时器、Nagle算法、发送缓冲区、超时时间、地址重用等设置。本文深入分析了相关源码和配置选项,如TCP_NODELAY、发送缓冲区大小和SO_REUSEADDR的使用。
摘要由CSDN通过智能技术生成

        anet是redis对网络通信(socket)的简单封装和一些状态设置的封装。状态设置主要包括ocket连接的阻塞性、tcp的保活定时器的设置、设置发送缓冲区、tcp的Nagle算法设置、设置发送超时时间、地址重用的设置和设置socket只能发送和接收ipv6。

一、socket的状态设置

      socket的状态选项设置主要通过setsockopt设置。

int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);

level主要包括SOL_SOCKET:通用套接字选项.、IPPROTO_IP:IP选项、IPPROTO_TCP:TCP选项三个层次,optname就是具体的状态选项,而redis只用到了这三个层次部分的选项。

1、socket连接的阻塞性

int anetSetBlock(char *err, int fd, int non_block) {
    int flags;
    if ((flags = fcntl(fd, F_GETFL)) == -1) {
        anetSetError(err, "fcntl(F_GETFL): %s", strerror(errno));
        return ANET_ERR;
    }
    if (non_block)
        flags |= O_NONBLOCK;//设置socket连接非阻塞
    else
        flags &= ~O_NONBLOCK;//设置socket连接阻塞
    if (fcntl(fd, F_SETFL, flags) == -1) {
        anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno));
        return ANET_ERR;
    }
    return ANET_OK;
}

2、tcp的保活定时器的设置

//启用保活定时器
int anetTcpKeepAlive(char *err, int fd)
{
    int yes = 1;//yes为1表示启用TCP保活定时器,yes为0表示关闭保活定时器
    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == -1) {
        anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno));
        return ANET_ERR;
    }
    return ANET_OK;
}//启用保活定时器,设置启用保活定时器的具体选项
int anetKeepAlive(char *err, int fd, int interval)
{
    int val = 1;//启用保活定时器
    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1)
    {
        anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno));
        return ANET_ERR;
    }
    val = interval;
    //设置对一个连接进行有效性探测之前运行的最大非活跃时间间隔,默认值为 14400(即2个小时)
    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
        anetSetError(err, "setsockopt TCP_KEEPIDLE: %s\n", strerror(errno));
        return ANET_ERR;
    }
    val = interval/3;
    if (val == 0) val = 1;//设置两个探测的时间间隔,默认值为150即75秒
    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
        anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno));
        return ANET_ERR;
    }
    val = 3;//设置关闭一个非活跃连接之前进行探测的最大次数
    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
        anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno));
        return ANET_ERR;
    }
    return ANET_OK;
}

3、tcp的Nagle算法设置

      Nagle算法通过将未确认的数据存入缓冲区直到蓄足一个包一起发送的方法,来减少主机发送的零碎小数据包的数目。这样一来势必会造成通信的延迟,TCP_NODELAY可以用来设置Nagle算法是否启用。

static int anetSetTcpNoDelay(char *err, int fd, int val)
{//val为1启用Nagle算法,val为0禁止Nagle算法
    if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) == -1)
    {
        anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno));
        return ANET_ERR;
    }
    return ANET_OK;
}

4、设置发送缓冲区

int (char *err, int fd, int buffsize)
{//buffsize为缓冲区大小,SNDBUF表示发送缓冲区,相对SO_RCVBUF表示接收缓冲区
    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1)
    {
        anetSetError(err, "setsockopt SO_SNDBUF: %s", strerror(errno));
        return ANET_ERR;
    }
    return ANET_OK;
}

5、设置发送超时时间

int anetSendTimeout(char *err, int fd, long long ms) {
    struct timeval tv;//tv为超时时间
    tv.tv_sec = ms/1000;
    tv.tv_usec = (ms%1000)*1000;
     //SO_SNDTIMEO为发送超时时间,SO_RCVTIMEO为接收超时时间
    if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
        anetSetError(err, "setsockopt SO_SNDTIMEO: %s", strerror(errno));
        return ANET_ERR;
    }
    return ANET_OK;
}

6、地址重用的设置

      SO_REUSEADDR这个选项的设置主要是为了可以使TCP状态位于 TIME_WAIT的地址马上可以投入使用。

static int anetSetReuseAddr(char *err, int fd) {
    int yes = 1;//yes为1表示允许地址重用,为0表示不允许地址重用
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
        anetSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno));
        return ANET_ERR;
    }
    return ANET_OK;
}

7、设置socket只能发送和接收ipv6

static int anetV6Only(char *err, int s) {
    int yes = 1;//1表示只能发送和接收ipv6
    if (setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,&yes,sizeof(yes)) == -1) {
        anetSetError(err, "setsockopt: %s", strerror(errno));
        close(s);
        return ANET_ERR;
    }
    return ANET_OK;
}

二、函数汇总

int anetTcpConnect(char *err, char *addr, int port); //进行TCP的阻塞连接 
int anetTcpNonBlockConnect(char *err, char *addr, int port); //进行TCP的非阻塞连接 
int anetTcpNonBlockBindConnect(char *err, char *addr, int port,char *source_addr);
int anetTcpNonBlockBestEffortBindConnect(char *err, char *addr, int port,char *source_addr);
int anetUnixConnect(char *err, char *path); //进行Unix域的阻塞连接 
int anetUnixNonBlockConnect(char *err, char *path); //进行Unix域的非阻塞连接 
static int anetTcpGenericConnect(char *err, char *addr, int port,char *source_addr, int flags);//封装了socket连接操作
int anetTcpServer(char *err, int port, char *bindaddr, int backlog);//创建socket的server
int anetTcp6Server(char *err, int port, char *bindaddr, int backlog);//创建只能发送和接收ipv6的socket的server
int anetUnixServer(char *err, char *path, mode_t perm, int backlog);//创建unix域的的socket的server
static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backlog);//封装了socket的监听连接的操作
int anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port);//获得tcp连接请求并建立连接
int anetUnixAccept(char *err, int serversock);//获得unix域连接请求并建立连接
static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len);//获得连接请求并建立连接
//地址解析函数
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len); /* 解析所有的东西 */
int anetResolveIP(char *err, char *host, char *ipbuf, size_t ipbuf_len); /* 单单解析IP的地址 */
int anetGenericResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,int flags);//根据host解析地址
/*-------状态设置函数-------*/
int anetNonBlock(char *err, int fd);
int anetBlock(char *err, int fd);
int anetSetBlock(char *err, int fd, int non_block);//设置socket是否阻塞
int anetEnableTcpNoDelay(char *err, int fd); /* 启用TCP连接没有延迟 */
int anetDisableTcpNoDelay(char *err, int fd); /* 禁用TCP连接没有延迟 */
static int anetSetTcpNoDelay(char *err, int fd, int val);//设置tcp连接是否延迟
static int anetV6Only(char *err, int s)//设置socket只能发送和接收ipv6
int anetTcpKeepAlive(char *err, int fd); /* 设置TCP启用心跳机制 */
int anetPeerToString(int fd, char *ip, size_t ip_len, int *port);//获取对端fd的地址(ip+port)
int anetSockName(int fd, char *ip, size_t ip_len, int *port);//获取本端fd的地址(ip+port)
int anetKeepAlive(char *err, int fd, int interval); /* 设置tcp心跳机制的TCP_KEEPCNT,TCP_KEEPINTVL,TCP_KEEPIDLE*/
int anetSendTimeout(char *err, int fd, long long ms);//发送超时设置
static int anetSetReuseAddr(char *err, int fd);//地址重用设置
int anetSetSendBuffer(char *err, int fd, int buffsize);//发送缓冲区大小设置
//读写操作的简单封装
int anetRead(int fd, char *buf, int count); //对read()的简单封装
int anetWrite(int fd, char *buf, int count); //对write()的简单封装





 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值