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()的简单封装