【网络编程】非阻塞connect详解

来源:互联网 发布:武汉光谷相关数据 编辑:程序博客网 时间:2024/05/19 00:54

一、为什么使用非阻塞connect

    TCP连接的建立涉及一个在三路握手过程,阻塞的connect一直等到客户收到自己的SYN的ACK才返回,这需要至少一个RTT时间,RTT时间波动很大从几毫秒到几秒。而且在没有响应时,会等待数秒再次发送,(详见TCPv2第828页),总共75秒到几分钟的connect超时时间。为了缩短超时时间,将非阻塞connect与select配合使用


二、connect的处理细节

1、当服务器和客户端在同一个主机上时,connect会立刻建立;

2、建立成功select监测到描述符为可写(详见TCPv2第531页);

3、建立失败select检测到描述符为可读可写(详见TCPv2第530页);

4、当可写返回时,通过写成功与否来判断,网络建立成功或失败。


三、代码详解

int MyConnect(int fd, char *addr, int port, int timeout)

{
    struct sockaddr_in server_addr;
    int ioFlags = 0;
    int ret;
    char temp_addr[IP_LEN];

    //设置非阻塞读写,使connect成为非阻塞的
    ioFlags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, ioFlags|O_NONBLOCK);

    //设置服务器地址
    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    memset(temp_addr, 0, IP_LEN);
    strcpy(temp_addr,addr);

    if (inet_aton(temp_addr, &server_addr.sin_addr) == 0)

    {
        //再次设置非阻塞读写
        fcntl(fd, F_SETFL, ioFlags);

       return -1;

    }

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);

    //连接服务器
    ret = connect(fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
    if( ret == 0 )    //连接成功,服务器与客户端立即建立连接
    {
      
    }
    else
    {
        if( errno != EINPROGRESS )    //连接失败,EINPROGRESS代表正在进行三次握手

        {
            //再次设置非阻塞读写
            fcntl(fd, F_SETFL, ioFlags);

            return -1;
        }
        else //(errno==EINPROGRESS)    //正在进行三次握手
        {
            fd_set rSet, wSet;
            struct timeval to;

            FD_ZERO(&rSet);
            FD_SET(fd, &rSet);
            wSet = rSet;
            to.tv_sec = timeout;
            to.tv_usec= 0;

            ret = select(fd+1, &rSet, &wSet, NULL, &to);//监听网络
            if(ret<0) //select返回错误,连接失败

            {
                //再次设置非阻塞读写
                fcntl(fd, F_SETFL, ioFlags);

                return -1;
            }
            else if(ret==0)  //连接超时
            {
               //再次设置非阻塞读写
                fcntl(fd, F_SETFL, ioFlags);
                return -1;
            }
            else
            {
                if( FD_ISSET(fd, &wSet) )
                {
                    errno = 0;
                    char t = 0;

                    ret = send(fd, &t, 0, 0);
                    if( (ret==0)&&(errno==0) )  //连接成功
                    {
                      
                    }
                    else
                    {
                         //再次设置非阻塞读写
                       fcntl(fd, F_SETFL, ioFlags);
                       return -1;
                    }
                }
                else
                {
                     //再次设置非阻塞读写
                     fcntl(fd, F_SETFL, ioFlags);
                     return -1;
                }
            }
        }
    }

    //再次设置非阻塞读写
    fcntl(fd, F_SETFL, ioFlags);

    return 0;
}
原创粉丝点击