zoukankan      html  css  js  c++  java
  • 深入redis内部---网络编程

    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);

    作用:将s中前n个字节 (typedef unsigned int size_t)用 ch 替换并返回 s 。
    memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体数组进行清零操作的一种最快方法。
    1.4 网络转头文件:
    #include <netinet/in.h>
    定义函数: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----未知来源

  • 相关阅读:
    HDU1698(线段树入门题)
    POJ2528(离散化+线段树区间更新)
    POJ3630(Trie树)
    HDU1251(字典树)
    HDU1247(经典字典树)
    POJ2513(字典树+图的连通性判断)
    POJ1363
    UVa11624(逃离火焰问题)
    HDOJ1495(倒水BFS)
    poj3414Pots(倒水BFS)
  • 原文地址:https://www.cnblogs.com/davidwang456/p/3516489.html
Copyright © 2011-2022 走看看