非阻塞 socket整理

来源:互联网 发布:js设置标签属性值 编辑:程序博客网 时间:2024/06/05 06:05

非阻塞 connect:  在一个 TCP 套接字被设置为非阻塞之后调用 connect ,connect 会立即返回 EINPROGRESS 错误,表示连接操作正在进行中,但是仍未完成,与此同时 TCP 三次握手操作会同时进行。在这之后,我们可以通过调用 select 来检查这个链接是否建立成功。

非阻塞 connect 有三种用途:
1.我们可以在 TCP 三次握手的同时做一些其它的处理。connect 操作需要一个往返时间才能完成,从几个毫秒(局域网)到几百毫秒或几秒(广域网)。在这段时间内我们可能有一些其他的处理想要同时执行;
2.可以用这种技术同时建立多个连接。在 Web 浏览器中很普遍;
3.由于我们使用 select 来等待连接的完成,因此我们可以给 select 设置一个时间限制,从而缩短 connect 的超时时间。在大多数实现中,connect 的超时时间在75 秒到几分钟之间。有时候应用程序想要一个更短的超时时间,使用非阻塞 connect 就是一种方法。

非阻塞 connect 听起来虽然简单,但是仍然有一些细节问题要处理:
1.即使套接字是非阻塞的,如果连接的服务器在同一台主机上,那么在调用 connect 建立连接时,连接通常会立即建立成功。我们必须处理这种情况;
2.源自 Berkeley 的实现有两条与 select 和非阻塞 I/O 相关的规则:
     A)当连接建立成功时,套接口描述符变成 可写
     B)当连接建立出错时,套接口描述符变成 既可读又可写

注意:当一个套接口出错时,它会被 select 调用标记为既可读又可写。
非阻塞 connect 有这么多好处,但是处理非阻塞 connect 时会遇到很多【可移植性问题】。

处理非阻塞 connect 的步骤:
第一步,创建 socket,返回套接字描述符;
第二步,调用 fcntl 或 ioctlsocket 把套接口描述符设置成非阻塞;
第三步,调用 connect 开始建立连接;
第四步,判断连接是否成功建立:
     A)如果 connect 返回 0 ,表示连接成功(服务器和客户端在同一台机器上时就有可能发生这种情况);
     B)调用 select 来判定连接建立的是否成功;
       如果 select 返回 0 ,则表示在 select 的超时时间内未能成功建立连接;我们需要返回超时错误给用户,同时关闭连接,以防止TCP三次握手继续进行下去;
       如果 select 返回大于 0 的值,则说明检测到可读或可写或异常的套接字描述符存在;此时我们可以通过调用 getsockopt 来检测集合中的套接口上是否存在待处理的错误,如果连接建立是成功的,则通过 getsockopt(sockfd,SOL_SOCKET,SO_ERROR,(char *)&error,&len) 获取的 error 值将是 0 ,如果建立连接时遇到错误,则 error 的值是连接错误所对应的 errno 值,比如ECONNREFUSED,ETIMEDOUT等。

实例代码:

BOOL ConnectServer(){    SOCKET sClient;    int ret;    struct sockaddr_in server;    sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);    if (sClient == INVALID_SOCKET)    {        return FALSE;    }    // set Recv and Send time out    int TimeOut = 15000; // 设置发送超时15秒    if (setsockopt(sClient, SOL_SOCKET, SO_SNDTIMEO, (char *)&TimeOut, sizeof(TimeOut)) == SOCKET_ERROR)    {        closesocket(sClient);        return FALSE;    }    if (setsockopt(sClient, SOL_SOCKET, SO_RCVTIMEO, (char *)&TimeOut, sizeof(TimeOut)) == SOCKET_ERROR)    {        closesocket(sClient);        return FALSE;    }    unsigned long ul = 1;    ret = ioctlsocket(sClient, FIONBIO, (unsigned long *)&ul);    if (ret == SOCKET_ERROR)    {        closesocket(sClient);        return FALSE;    }    server.sin_family = AF_INET;    server.sin_port = htons(80);    server.sin_addr.s_addr = inet_addr(“192.168.100.22”);    if (server.sin_addr.s_addr == INADDR_NONE)    {        closesocket(sClient);        return FALSE;    }    BOOL bConnRe = FALSE;    if (connect(sClient, (struct sockaddr *)&server, sizeof(server)) == -1)    {        struct timeval timeout_val;        fd_set set;        FD_ZERO(&set);        FD_SET(sClient, &set);        timeout_val.tv_sec = 5;        timeout_val.tv_usec = 0;        if(select(0, NULL, &set, NULL, &timeout_val) > 0)        {             bConnRe = TRUE;        }        else            bConnRe = FALSE;    }    else        bConnRe = TRUE;    if (!bConnRe)    {        closesocket(sClient);        return FALSE;    }    unsigned long ull = 0;    ret = ioctlsocket(sClient, FIONBIO, (unsigned long *)&ull);    if (ret == SOCKET_ERROR)    {        closesocket(sClient);        return FALSE;    }        m_sock = sClient;    return TRUE;}

 

1、阻塞模式与非阻塞模式下recv的返回值各代表什么意思?有没有区别?(就我目前了解阻塞与非阻塞recv返回值没有区分,都是
<0:出错,=0:连接关闭,>0接收到数据大小,特别:返回值 <0时并且(errno == EINTR || errno ==
EWOULDBLOCK || errno ==
EAGAIN)的情况下认为连接是正常的,继续接收。只是阻塞模式下recv会阻塞着接收数据,非阻塞模式下如果没有数据会返回,不会阻塞着读,因此需要循环读取)。


2、阻塞模式与非阻塞模式下write的返回值各代表什么意思?有没有区别?(就我目前了解阻塞与非阻塞write返回值没有区分,都是
<0:出错,=0:连接关闭,>0发送数据大小,特别:返回值 <0时并且(errno == EINTR || errno ==
EWOULDBLOCK || errno ==
EAGAIN)的情况下认为连接是正常的,继续发送。只是阻塞模式下write会阻塞着发送数据,非阻塞模式下如果暂时无法发送数据会返回,不会阻塞着
write,因此需要循环发送)。

原创粉丝点击