1. 程式人生 > >【網路程式設計】非阻塞connect詳解

【網路程式設計】非阻塞connect詳解

一、為什麼使用非阻塞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;
}