Redis源码学习(二)-网络通信(anet.c)

有了上个博客(网络编程addrinfo结构体与getaddrinfo函数_qq_20853741的博客-CSDN博客)的铺垫,理解Redis网络通信源码就简单多了!

    anet.c是redis对网络通信(socket)的简单封装和一些状态设置的封装。状态设置主要包括socket连接的阻塞性、tcp的保活定时器的设置、设置发送缓冲区、tcp的Nagle算法设置、设置发送超时时间、地址重用的设置和设置

一、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算法是否启用。

/* 设置TCP连接是否NODelay没有延迟 */
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;
}

二、监听套接字

/* 参数解释:

addr:传入服务器地址信息,其中内容将用于创建监听套接字

source_addr:相当于客户端地址信息

flags:客户设置的标记*/

static int anetTcpGenericConnect(char *err, const char *addr, int port,
                                 const char *source_addr, int flags)
{
    int s = ANET_ERR, rv;
    char portstr[6];  /* strlen("65535") + 1; */
    struct addrinfo hints, *servinfo, *bservinfo, *p, *b;

    snprintf(portstr,sizeof(portstr),"%d",port);  //按照format的格式格式化为字符串,然后再将其拷贝至str中
    memset(&hints,0,sizeof(hints));
    hints.ai_family = AF_UNSPEC;                   //AF_UNSPEC则意味着函数返回的是适用于指定主机名和服务名且适合任何协议族的地址
    hints.ai_socktype = SOCK_STREAM;               //面向连接的双向字节流

    if ((rv = getaddrinfo(addr,portstr,&hints,&servinfo)) != 0) 
    {
        anetSetError(err, "%s", gai_strerror(rv));
        return ANET_ERR;
    }
    for (p = servinfo; p != NULL; p = p->ai_next) 
    {
        /* Try to create the socket and to connect it.
         * If we fail in the socket() call, or on connect(), we retry with
         * the next entry in servinfo. */
        if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
            continue;
        if (anetSetReuseAddr(err,s) == ANET_ERR) goto error;
        if (flags & ANET_CONNECT_NONBLOCK && anetNonBlock(err,s) != ANET_OK)
            goto error;
        if (source_addr) 
        {
            int bound = 0;
            /* Using getaddrinfo saves us from self-determining IPv4 vs IPv6 */
            if ((rv = getaddrinfo(source_addr, NULL, &hints, &bservinfo)) != 0)
            {
                anetSetError(err, "%s", gai_strerror(rv));
                goto error;
            }
            for (b = bservinfo; b != NULL; b = b->ai_next) 
            {
                if (bind(s,b->ai_addr,b->ai_addrlen) != -1) 
                {
                    bound = 1;
                    break;
                }
            }
            freeaddrinfo(bservinfo);
            if (!bound) 
            {
                anetSetError(err, "bind: %s", strerror(errno));
                goto error;
            }
        }
        if (connect(s,p->ai_addr,p->ai_addrlen) == -1) 
        {
            /* If the socket is non-blocking, it is ok for connect() to
             * return an EINPROGRESS error here. */
            if (errno == EINPROGRESS && flags & ANET_CONNECT_NONBLOCK)
                goto end;
            close(s);
            s = ANET_ERR;
            continue;
        }

        /* If we ended an iteration of the for loop without errors, we
         * have a connected socket. Let's return to the caller. */
        goto end;
    }
    if (p == NULL)
        anetSetError(err, "creating socket: %s", strerror(errno));

error:
    if (s != ANET_ERR) {
        close(s);
        s = ANET_ERR;
    }

end:
    freeaddrinfo(servinfo);

    /* Handle best effort binding: if a binding address was used, but it is
     * not possible to create a socket, try again without a binding address. */
    if (s == ANET_ERR && source_addr && (flags & ANET_CONNECT_BE_BINDING)) 
    {
        return anetTcpGenericConnect(err,addr,port,NULL,flags);
    } 
    else 
    {
        return s;
    }
}

  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语言系统网络库的又一次简答的封装,因为里面都是直接调用库方法的函数实现。作者根据自己业务的需要在上面做了小小的封装。比如说非阻塞的设置;

/* anet设置非阻塞的方法 */  
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;  
    }  
    //调用fcntl方法设置非阻塞方法  
    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;  
}  

fcntl方法是直接起作用的方法。在整个网络操作文件的中,让我感觉稍有亮点的还是有一些地方的
(1).能设置BLOCK连接还是NONE_BLOCKED方式的connect;

/* TCP的默认连接 */  
int anetTcpConnect(char *err, char *addr, int port)  
{  
    return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONE);  
}  
  
/* TCP的非阻塞连接 */  
int anetTcpNonBlockConnect(char *err, char *addr, int port)  
{  
    return anetTcpGenericConnect(err,addr,port,ANET_CONNECT_NONBLOCK);  
}  

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

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

/* anetGenericResolve() is called by anetResolve() and anetResolveIP() to 
* do the actual work. It resolves the hostname "host" and set the string 
* representation of the IP address into the buffer pointed by "ipbuf". 
* 
* If flags is set to ANET_IP_ONLY the function only resolves hostnames 
* that are actually already IPv4 or IPv6 addresses. This turns the function 
* into a validating / normalizing function. */  
/* 解析的泛型方法,可以根据条件解析host主机名或IP地址 */  
int anetGenericResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len, int flags)  
{  
    struct addrinfo hints, *info;  
    int rv;  
  
    memset(&hints,0,sizeof(hints));  
    if (flags & ANET_IP_ONLY) hints.ai_flags = AI_NUMERICHOST;  
    hints.ai_family = AF_UNSPEC;  
    hints.ai_socktype = SOCK_STREAM;  /* specify socktype to avoid dups */  
  
    //解析hostName  
    if ((rv = getaddrinfo(host, NULL, &hints, &info)) != 0) {  
        anetSetError(err, "%s", gai_strerror(rv));  
        return ANET_ERR;  
    }  
      
    //根据类型解析ipV4的地址还是ipV6的地址  
    if (info->ai_family == AF_INET) 
    {  
        struct sockaddr_in *sa = (struct sockaddr_in *)info->ai_addr;  
        inet_ntop(AF_INET, &(sa->sin_addr), ipbuf, ipbuf_len);  
    }
    else 
    {  
        struct sockaddr_in6 *sa = (struct sockaddr_in6 *)info->ai_addr;  
        inet_ntop(AF_INET6, &(sa->sin6_addr), ipbuf, ipbuf_len);  
    }  
  
    freeaddrinfo(info);  
    return ANET_OK;  
}  
还有一些常见的方法,与我们平时写代码时用的手法基本一样,比如accept()的方法:
/* socket连接操作 */  
static int anetGenericAccept(char *err, int s, struct sockaddr *sa, socklen_t *len) 
{  
    int fd;  
    while(1) 
    {  
        //通过while循环等待连接  
        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;  
}  

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;          //结构体

    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:套接字的读写。

int anetUnixGenericConnect(char *err, const char *path, int flags)
{
    int s;
    struct sockaddr_un sa;

    if ((s = anetCreateSocket(err,AF_LOCAL)) == ANET_ERR)
        return ANET_ERR;

    sa.sun_family = AF_LOCAL;
    strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
    if (flags & ANET_CONNECT_NONBLOCK) 
    {
        if (anetNonBlock(err,s) != ANET_OK) 
        {
            close(s);
            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;
}

你可能感兴趣的:(网络编程,多线程,c++,开发语言)