Redis在anet.h和anet.c中封装了底层套接字实现:
1.anetTcpServer,建立网络套接字服务器,完成对socket(),bind(),listen()等操作的封装,返回socket的fd。
int anetTcpServer(char *err, int port, char *bindaddr) { int s; struct sockaddr_in sa; //见1.1结构体 if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR) //AF_INET表示使用IPv4 return ANET_ERR; memset(&sa,0,sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(port); sa.sin_addr.s_addr = htonl(INADDR_ANY); if (bindaddr && inet_aton(bindaddr, &sa.sin_addr) == 0) { anetSetError(err, "invalid bind address"); close(s); return ANET_ERR; } if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa)) == ANET_ERR) return ANET_ERR; return s; }
1.1 结构体sockaddr_in
struct sockaddr_in { short int sin_family; // Address family unsigned short int sin_port; // Port number struct in_addr sin_addr; // Internet address unsigned char sin_zero[8]; // Same size as struct sockaddr };
1.2 创建socket,封装了socket实现
static int anetCreateSocket(char *err, int domain) { int s, on = 1; if ((s = socket(domain, SOCK_STREAM, 0)) == -1) { //创建socket anetSetError(err, "creating socket: %s", strerror(errno)); return ANET_ERR; } /* Make sure connection-intensive things like the redis benchmark * will be able to close/open sockets a zillion of times */ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { //设置选项 anetSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno)); return ANET_ERR; } return s; }
1.3 memset函数
在C中 <string.h>,原型为:void *memset(void *s, int ch, size_t n);
定义函数:unsigned short int htons(unsigned short int hostshort); 函数说明:htons()用来将参数指定的16 位hostshort 转换成网络字符顺序. 返回值:返回对应的网络字符顺序.
定义函数:unsigned long int htonl(unsigned long int hostlong);
函数说明:htonl ()用来将参数指定的32 位hostlong 转换成网络字符顺序.
返回值:返回对应的网络字符顺序.
1.5 监听,封装了bind和listen实现
static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len) { if (bind(s,sa,len) == -1) { //绑定 anetSetError(err, "bind: %s", strerror(errno)); close(s); return ANET_ERR; } /* Use a backlog of 512 entries. We pass 511 to the listen() call because * the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1); * which will thus give us a backlog of 512 entries */ if (listen(s, 511) == -1) { //监听 anetSetError(err, "listen: %s", strerror(errno)); close(s); return ANET_ERR; } return ANET_OK; }
2.tcp连接建立堵塞和非堵塞网络套接字连接。
int anetTcpConnect(char *err, char *addr, int port) { return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONE); } int anetTcpNonBlockConnect(char *err, char *addr, int port) { return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONBLOCK); } //具体实现 #define ANET_CONNECT_NONE 0 #define ANET_CONNECT_NONBLOCK 1 static int anetTcpGenericConnect(char *err, char *addr, int port, int flags) { int s; struct sockaddr_in sa; if ((s = anetCreateSocket(err,AF_INET)) == ANET_ERR) return ANET_ERR; sa.sin_family = AF_INET; sa.sin_port = htons(port); if (inet_aton(addr, &sa.sin_addr) == 0) { struct hostent *he; he = gethostbyname(addr); if (he == NULL) { anetSetError(err, "can't resolve: %s", addr); close(s); return ANET_ERR; } memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr)); } if (flags & ANET_CONNECT_NONBLOCK) { if (anetNonBlock(err,s) != ANET_OK) return ANET_ERR; } if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) { if (errno == EINPROGRESS && flags & ANET_CONNECT_NONBLOCK) return s; anetSetError(err, "connect: %s", strerror(errno)); close(s); return ANET_ERR; } return s; }
2.1 结构体hostent
struct hostent { char *h_name; char **h_aliases; int h_addrtype; int h_length; char **h_addr_list; };
其中,h_name – 地址的正式名称。
h_aliases – 空字节-地址的预备名称的指针。
h_addrtype –地址类型; 通常是AF_INET。
h_length – 地址的比特长度。
h_addr_list – 零字节-主机网络地址指针。网络字节顺序。
h_addr - h_addr_list中的第一地址。
gethostbyname() 成功时返回一个指向结构体 hostent 的指针,或者 是个空 (NULL) 指针。
2.2 设置非堵塞
int anetNonBlock(char *err, int fd) { int flags; /* Set the socket non-blocking. * Note that fcntl(2) for F_GETFL and F_SETFL can't be * interrupted by a signal. */ if ((flags = fcntl(fd, F_GETFL)) == -1) { anetSetError(err, "fcntl(F_GETFL): %s", strerror(errno)); return ANET_ERR; } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno)); return ANET_ERR; } return ANET_OK; }
2.3 文件控制fcntl
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
int flags; /* 设置为非阻塞*/ if (fcntl(socket_descriptor, F_SETFL, flags | O_NONBLOCK) < 0) { /* Handle error */ } /* 设置为阻塞 */ if ((flags = fcntl(sock_descriptor, F_GETFL, 0)) < 0) { /* Handle error */ }
3. tcp接收,在网络套接字上新增连接
int anetTcpAccept(char *err, int s, char *ip, int *port) { int fd; struct sockaddr_in sa; socklen_t salen = sizeof(sa); if ((fd = anetGenericAccept(err,s,(struct sockaddr*)&sa,&salen)) == ANET_ERR) return ANET_ERR; if (ip) strcpy(ip,inet_ntoa(sa.sin_addr)); if (port) *port = ntohs(sa.sin_port); return fd; }
封装了accept函数
static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) { int fd; while(1) { fd = accept(s,sa,len); if (fd == -1) { if (errno == EINTR) continue; else { anetSetError(err, "accept: %s", strerror(errno)); return ANET_ERR; } } break; } return fd; }
4. 其它方法
anetEnableTcpNoDelay:将tcp连接设为非延迟性的,即屏蔽Nagle算法。使用setsockopt方法实现。
anetDisableTcpNoDelay:和上面的方法作用相反。使用setsockopt方法实现。
anetTcpKeepAlive:开启连接检测,避免对方宕机或者网络中断时fd一直堵塞。使用setsockopt方法实现。
anetRead和anetWrite:套接字的读写。
参考资料
Redis源代码分析.pdf----未知来源