设为首页 收藏本站
查看: 932|回复: 0

[经验分享] 深入redis内部---网络编程

[复制链接]

尚未签到

发表于 2015-7-22 07:26:27 | 显示全部楼层 |阅读模式
  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中 ,原型为:void *memset(void *s, int ch, size_t n);


作用:将s中前n个字节 (typedef unsigned int size_t)用 ch 替换并返回 s 。

memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法。

1.4 网络转头文件:

#include




定义函数:unsigned short int htons(unsigned short int hostshort);
函数说明:htons()用来将参数指定的16 位hostshort 转换成网络字符顺序.
返回值:返回对应的网络字符顺序.

定义函数:unsigned long int htonl(unsigned long int hostlong);

函数说明:htonl ()用来将参数指定的32 位hostlong 转换成网络字符顺序.

返回值:返回对应的网络字符顺序.



定义函数:int inet_aton(const char *string, struct in_addr*addr);


参数描述:


1 输入参数string包含ASCII表示的IP地址。


2 输出参数addr是将要用新的IP地址更新的结构。


返回值:


如果这个函数成功,函数的返回值非零,如果输入地址不正确则会返回零。使用这个函数并没有错误码存放在errno中,所以它的值会被忽略

  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);
  int fcntl(int fd, int cmd, long arg);
  int fcntl(int fd, int cmd, struct flock *lock);

fcntl()针对(文件)描述符提供控制.实例:




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----未知来源

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-89197-1-1.html 上篇帖子: redis set 下篇帖子: redis 笔记一
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表