Redis源码分析(二十一)--- anet网络通信的封装

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介:

昨天非常轻松的分析完Redis的事件驱动模型之后,今天我来看看anet的代码,anet是Redis对于Client/Server的网络操作的一个小小封装。代码中对此文件的官方解释为:

  1. /* anet.c -- Basic TCP socket stuff made a bit less boring  
  2. * 基于简单的基本TCP的socket连接  
复制代码

后面的made a bit less boring这在这里表示啥意思,就让我有点费解了,不过前面的是重点,Basic TCP Socket,基于的是TCP 协议的socket连接。anet.h的API如下:
  1. int anetTcpConnect(char *err, char *addr, int port); /* TCP的默认连接 */  
  2. int anetTcpNonBlockConnect(char *err, char *addr, int port); /* TCP的非阻塞连接 */  
  3. int anetUnixConnect(char *err, char *path); /* anet的Unix方式的默认连接方式 */  
  4. int anetUnixNonBlockConnect(char *err, char *path); /* anet的Unix方式的非阻塞连接方式 */  
  5. int anetRead(int fd, char *buf, int count); /* anet网络读取文件到buffer中操作 */  
  6. int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len); /* 解析所有的东西 */  
  7. int anetResolveIP(char *err, char *host, char *ipbuf, size_t ipbuf_len); /* 单单解析IP的地址 */  
  8. int anetTcpServer(char *err, int port, char *bindaddr, int backlog);  
  9. int anetTcp6Server(char *err, int port, char *bindaddr, int backlog);  
  10. int anetUnixServer(char *err, char *path, mode_t perm, int backlog);  
  11. int anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port);  
  12. int anetUnixAccept(char *err, int serversock);  
  13. int anetWrite(int fd, char *buf, int count); /* anet通过网络从buffer中写入文件操作 */  
  14. int anetNonBlock(char *err, int fd); /* anet设置非阻塞的方法 */  
  15. int anetEnableTcpNoDelay(char *err, int fd); /* 启用TCP没有延迟 */  
  16. int anetDisableTcpNoDelay(char *err, int fd); /* 禁用TCP连接没有延迟 */  
  17. int anetTcpKeepAlive(char *err, int fd); /* 设置TCP保持活跃连接状态。适用于所有系统 */  
  18. int anetPeerToString(int fd, char *ip, size_t ip_len, int *port);  
  19. int anetKeepAlive(char *err, int fd, int interval); /* 设置TCP连接一直存活,用来检测已经死去的结点,interval选项只适用于Linux下的系统 */  
  20. int anetSockName(int fd, char *ip, size_t ip_len, int *port);  
复制代码

我们还是能够看到很多熟悉的方法的,read,write,accept.connect等在任何编程语言中都会看到的一些方法。看完这个anet,最直观的感觉就是作者编写的这个网络操作库就是对于C语言系统网络库的又一次简答的封装,因为里面都是直接调用库方法的函数实现。作者根据自己业务的需要在上面做了小小的封装。比如说非阻塞的设置;
  1. /* anet设置非阻塞的方法 */  
  2. int anetNonBlock(char *err, int fd)  
  3. {  
  4.     int flags;  
  5.   
  6.     /* Set the socket non-blocking. 
  7.      * Note that fcntl(2) for F_GETFL and F_SETFL can't be 
  8.      * interrupted by a signal. */  
  9.     if ((flags = fcntl(fd, F_GETFL)) == -1) {  
  10.         anetSetError(err, "fcntl(F_GETFL): %s", strerror(errno));  
  11.         return ANET_ERR;  
  12.     }  
  13.     //调用fcntl方法设置非阻塞方法  
  14.     if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {  
  15.         anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno));  
  16.         return ANET_ERR;  
  17.     }  
  18.     return ANET_OK;  
  19. }  
复制代码

fcntl方法是直接起作用的方法。在整个网络操作文件的中,让我感觉稍有亮点的还是有一些地方的

(1).能设置BLOCK连接还是NONE_BLOCKED方式的connect;

  1. /* TCP的默认连接 */  
  2. int anetTcpConnect(char *err, char *addr, int port)  
  3. {  
  4.     return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONE);  
  5. }  
  6.   
  7. /* TCP的非阻塞连接 */  
  8. int anetTcpNonBlockConnect(char *err, char *addr, int port)  
  9. {  
  10.     return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONBLOCK);  
  11. }  
复制代码

(2).能设置连接的Delay的延时与否。:
  1. /* 设置TCP连接是否NODelay没有延迟 */  
  2. static int anetSetTcpNoDelay(char *err, int fd, int val)  
  3. {  
  4.     if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) == -1)  
  5.     {  
  6.         anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno));  
  7.         return ANET_ERR;  
  8.     }  
  9.     return ANET_OK;  
  10. }  
  11.   
  12. /* 启用TCP没有延迟 */  
  13. int anetEnableTcpNoDelay(char *err, int fd)  
  14. {  
  15.     return anetSetTcpNoDelay(err, fd, 1);  
  16. }  
  17.   
  18. /* 禁用TCP连接没有延迟 */  
  19. int anetDisableTcpNoDelay(char *err, int fd)  
  20. {  
  21.     return anetSetTcpNoDelay(err, fd, 0);  
  22. }  
复制代码

也许在有些情况下对延时要求比较高,就不能有延时。

(3).对ip地址有ipv4和ipv6地址不同的处理方法。这个作者想得还是非常全面的。在对地址做resolve解析的时候就考虑到了这个问题:

  1. /* anetGenericResolve() is called by anetResolve() and anetResolveIP() to 
  2. * do the actual work. It resolves the hostname "host" and set the string 
  3. * representation of the IP address into the buffer pointed by "ipbuf". 

  4. * If flags is set to ANET_IP_ONLY the function only resolves hostnames 
  5. * that are actually already IPv4 or IPv6 addresses. This turns the function 
  6. * into a validating / normalizing function. */  
  7. /* 解析的泛型方法,可以根据条件解析host主机名或IP地址 */  
  8. int anetGenericResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len,  
  9.                        int flags)  
  10. {  
  11.     struct addrinfo hints, *info;  
  12.     int rv;  
  13.   
  14.     memset(&hints,0,sizeof(hints));  
  15.     if (flags & ANET_IP_ONLY) hints.ai_flags = AI_NUMERICHOST;  
  16.     hints.ai_family = AF_UNSPEC;  
  17.     hints.ai_socktype = SOCK_STREAM;  /* specify socktype to avoid dups */  
  18.   
  19.     //解析hostName  
  20.     if ((rv = getaddrinfo(host, NULL, &hints, &info)) != 0) {  
  21.         anetSetError(err, "%s", gai_strerror(rv));  
  22.         return ANET_ERR;  
  23.     }  
  24.       
  25.     //根据类型解析ipV4的地址还是ipV6的地址  
  26.     if (info->ai_family == AF_INET) {  
  27.         struct sockaddr_in *sa = (struct sockaddr_in *)info->ai_addr;  
  28.         inet_ntop(AF_INET, &(sa->sin_addr), ipbuf, ipbuf_len);  
  29.     } else {  
  30.         struct sockaddr_in6 *sa = (struct sockaddr_in6 *)info->ai_addr;  
  31.         inet_ntop(AF_INET6, &(sa->sin6_addr), ipbuf, ipbuf_len);  
  32.     }  
  33.   
  34.     freeaddrinfo(info);  
  35.     return ANET_OK;  
  36. }  
复制代码

还有一些常见的方法,与我们平时写代码时用的手法基本一样,比如accept()的方法:

  1. /* socket连接操作 */  
  2. static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) {  
  3.     int fd;  
  4.     while(1) {  
  5.         //通过while循环等待连接  
  6.         fd = accept(s,sa,len);  
  7.         if (fd == -1) {  
  8.             if (errno == EINTR)  
  9.                 continue;  
  10.             else {  
  11.                 anetSetError(err, "accept: %s", strerror(errno));  
  12.                 return ANET_ERR;  
  13.             }  
  14.         }  
  15.         break;  
  16.     }  
  17.     return fd;  
  18. }  





本文作者:HarLock
本文来自云栖社区合作伙伴rediscn,了解相关信息可以关注redis.cn网站。
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
3月前
|
NoSQL Redis
Redis原理之网络通信协议笔记
1. RESP协议 ​2. 自定义Socket连接Redis
|
3月前
|
NoSQL Linux Redis
Redis原理之网络模型笔记
Redis采用单线程模型,这意味着一个Redis服务器在任何时刻都只会处理一个请求。Redis的网络模型涉及到阻塞I/O(Blocking I/O)、非阻塞I/O(Non-blocking I/O)、I/O多路复用(I/O Multiplexing)、信号驱动I/O(Signal-driven I/O)以及异步I/O(Asynchronous I/O)。
|
3月前
|
缓存 NoSQL 应用服务中间件
2.2.2 redis,memcached,nginx网络组件
2.2.2 redis,memcached,nginx网络组件
|
3月前
|
NoSQL Java 应用服务中间件
4.网络设计与redis、memcached、nginx组件(二)
4.网络设计与redis、memcached、nginx组件(二)
28 0
|
3月前
|
存储 NoSQL 应用服务中间件
4.网络设计与redis、memcached、nginx组件(一)
4.网络设计与redis、memcached、nginx组件(一)
73 0
|
3月前
|
NoSQL Linux Redis
Redis 的网络框架是实现了 Reactor 模型吗?
Redis 的网络框架是实现了 Reactor 模型吗?
|
4月前
|
NoSQL PHP Redis
PHP Redis 封装
PHP Redis 封装
17 0
|
5月前
|
NoSQL 关系型数据库 MySQL
Docker-compose封装mysql和redis并初始化数据
Docker-compose封装mysql和redis并初始化数据
123 0
|
3月前
|
NoSQL Go Redis
Golang实现redis系列-(3)封装RESP协议
Golang实现redis系列-(3)封装RESP协议
33 0
|
27天前
|
缓存 NoSQL Java
springboot中集成redis,二次封装成工具类
springboot中集成redis,二次封装成工具类
156 0