UNIX网络编程--非阻塞connect的实现

本文详细探讨了《UNIX网络编程》中关于非阻塞connect的实现,包括其用途、细节问题及处理方法。在非阻塞模式下,connect会立即返回EINPROGRESS错误,连接操作在后台进行。通过select或特定的错误处理策略来检查连接状态。文章还讨论了非阻塞connect在不同平台上的移植性问题,并提供了针对Linux环境的解决方案,例如再次调用connect来判断连接是否成功。最后,列举了Linux下常见的socket错误码。
摘要由CSDN通过智能技术生成

一、《UNIX网络编程》-非阻塞connect

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

非阻塞connect有三种用途:
    1.我们可以在三路握手的同时做一些其它的处理.connect操作要花一个往返时间完成,而且可以是在任何地方,从几个毫秒的局域网到几百毫秒或几秒的广域网.在这段时间内我们可能有一些其他的处理想要执行;
    2.可以用这种技术同时建立多个连接.在Web浏览器中很普遍;
    3.由于我们使用select来等待连接的完成,因此我们可以给select设置一个时间限制,从而缩短connect的超时时间.在大多数实现中,connect的超时时间在75秒到几分钟之间.有时候应用程序想要一个更短的超时时间,使用非阻塞connect就是一种方法;
非阻塞connect听起来虽然简单,但是仍然有一些细节问题要处理:
   1.即使套接口是非阻塞的,如果连接的服务器在同一台主机上,那么在调用connect建立连接时,连接通常会立即建立成功.我们必须处理这种情况;
   2.源自Berkeley的实现(和Posix.1g)有两条与select和非阻塞IO相关的规则:
       A:当连接建立成功时,套接口描述符变成可写;
       B:当连接出错时,套接口描述符变成既可读又可写;
  注意:当一个套接口出错时,它会被select调用标记为既可读又可写;

当发现套接口描述符可读或可写时,可进一步判断是连接成功还是出错。这里必须将B)和另外一种连接正常的情况区分开,就是连接建立好了之后,服务器端发送了数据给客户端,此时select同样会返回非阻塞socket描述符既可读又可写。

    因此,仅从socket可读或可写无法判断socket连接的状态。

非阻塞connect有这么多好处,但是处理非阻塞connect时会遇到很多可移植性问题;

        在处理非阻塞connect时,在不同的套接口实现的平台中存在的移植性问题,首先,有可能在调用select之前,连接就已经建立成功,而且对方的数据已经到来.在这种情况下,连接成功时套接口将既可读又可写.这和连接失败时是一样的.这个时候我们还得通过getsockopt来读取错误值;这是第二个可移植性问题;

移植性问题总结:
     1.对于出错的套接口描述符,getsockopt的返回值源自Berkeley的实现是返回0,待处理的错误值存储在errno中;而源自Solaris的实现是返回0,待处理的错误存储在errno中;(套接口描述符出错时调用getsockopt的返回值不可移植)
     2.有可能在调用select之前,连接就已经建立成功,而且对方的数据已经到来,在这种情况下,套接口描述符是既可读又可写;这与套接口描述符出错时是一样的;(怎样判断连接是否建立成功的条件不可移植)

这样的话,在我们判断连接是否建立成功的条件不唯一时,我们可以有以下的方法来解决这个问题:
     1.调用getpeername代替getsockopt.如果调用getpeername失败,getpeername返回ENOTCONN,表示连接建立失败,我们必须以SO_ERROR调用getsockopt得到套接口描述符上的待处理错误; 此为《Unix Network Programming》一书中提供的方法,该方法在Linux环境上测试,发现是无效的
     2.调用read,读取长度为0字节的数据.如果read调用失败,则表示连接建立失败,而且read返回的errno指明了连接失败的原因.如果连接建立成功,read应该返回0;
     3.再调用一次connect.它应该失败,如果错误errno是EISCONN,就表示套接口已经建立,而且第一次连接是成功的;否则,连接就是失败的;

被中断的connect(这是说明使用非阻塞connect的必要性):
如果在一个阻塞式套接口上调用connect,在TCP的三路握手操作完成之前被中断了,比如说,被捕获的信号中断,将会发生什么呢?假定connect不会自动重启,它将返回EINTR.那么,这个时候,我们就不能再调用connect等待连接建立完成了,如果再次调用connect来等待连接建立完成的话,connect将会返回错误值EADDRINUSE.在这种情况下,应该做的是调用select,就像在非阻塞式connect中所做的一样.然后,select在连接建立成功(使套接口描述符可写)或连接建立失败(使套接口描述符既可读又可写)时返回。

上述是书中的内容。

二、非阻塞connect的实现

        在一个TCP套接口设置为非阻塞后,调用connect,connect会在系统提供的errno变量中返回一个EINRPOCESS错误,此时TCP的三路握手继续进行。之后可以用select函数检查这个连接是否建立成功。以下实验基于unix网络编程和网络上给出的普遍示例,在经过大量测试之后,发现其中有很多方法,在linux中,并不适用。
我先给出了重要源码的逐步分析,在最后给出完整的connect非阻塞源码。
        1.首先填写套接字结构,包括远程的ip,通信端口如下: */

struct sockaddr_in serv_addr;
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(9999);
serv_addr.sin_addr.s_addr = inet_addr("58.31.231.255"); //inet_addr转换为网络字节序
bzero(&(serv_addr.sin_zero),8);
// 2.建立socket套接字:
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket creat error");
return 1;
}

// 3.将socket建立为非阻塞,此时socket被设置为非阻塞模式
flags = fcntl(sockfd,F_GETFL,0);//获取建立的sockfd的当前状态(非阻塞)
fcntl(sockfd,F_SETFL,flags|O_NONBLOCK);//将当前sockfd设置为非阻塞

4. 建立connect连接,此时socket设置为非阻塞,connect调用后,无论连接是否建
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值