zoukankan      html  css  js  c++  java
  • socket

    在一个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调用标记为既可读又可写;

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

    处理非阻塞connect的步骤:
    第一步:创建socket,返回套接口描述符;
    第二步:调用fcntl把套接口描述符设置成非阻塞;
    第三步:调用connect开始建立连接;
    第四步:判断连接是否成功建立;
           A:如果connect返回0,表示连接简称成功(服务器可客户端在同一台机器上时就有可能发生这种情况);
           B:调用select来等待连接建立成功完成;
             如果select返回0,则表示建立连接超时;我们返回超时错误给用户,同时关闭连接,以防止三路握手操作继续进行下去;
             如果select返回大于0的值,则需要检查套接口描述符是否可读或可写;如果套接口描述符可读或可写,则我们可以通过调用getsockopt来得到套 接口上待处理的错误(SO_ERROR),如果连接建立成功,这个错误值将是0,如果建立连接时遇到错误,则这个值是连接错误所对应的errno值(比 如:ECONNREFUSED,ETIMEDOUT等).
    "读取套接口上的错误"是遇到的第一个可移植性问题;如果出现问 题,getsockopt源自Berkeley的实现是返回0,等待处理的错误在变量errno中返回;但是Solaris会让getsockopt返回 -1,errno置为待处理的错误;我们对这两种情况都要处理;

    这样,在处理非阻塞connect时,在不同的套接口实现的平台中存在的移植性问题,首先,有可能在调用 select之前,连接就已经建立成功,而且对方的数据已经到来.在这种情况下,连接成功时套接口将既可读又可写.这和连接失败时是一样的.这个时候我们 还得通过getsockopt来读取错误值;这是第二个可移植性问题;
    移植性问题总结:
    1.对于出错的套接口描述符,getsockopt的返回值源自Berkeley的实现是返回0,待处理的错误值存储在errno中;而源自Solaris的实现是返回0,待处理的错误存储在errno中;(套接口描述符出错时调用getsockopt的返回值不可移植)
    2.有可能在调用select之前,连接就已经建立成功,而且对方的数据已经到来,在这种情况下,套接口描述符是既可读又可写;这与套接口描述符出错时是一样的;(怎样判断连接是否建立成功的条件不可移植)

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

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

      • int conn_nonb(int sockfd, const struct sockaddr_in *saptr, socklen_t salen, int nsec)  
      • {  
      • int flags, n, error, code;  
      • socklen_t len;  
      • fd_set wset;  
      • struct timeval tval;  
      • flags = fcntl(sockfd, F_GETFL, 0);  
      • fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);  
      • error = 0;  
      • if ((n == connect(sockfd, saptr, salen)) == 0) {  
      • goto done;  
      • } else if (n < 0 && errno != EINPROGRESS){  
      • return (-1);  
      • }  
      • /* Do whatever we want while the connect is taking place */  
      • FD_ZERO(&wset);  
      • FD_SET(sockfd, &wset);  
      • tval.tv_sec = nsec;  
      • tval.tv_usec = 0;  
      • if ((n = select(sockfd+1, NULL, &wset,   
      • NULL, nsec ? &tval : NULL)) == 0) {  
      • close(sockfd);  /* timeout */  
      • errno = ETIMEDOUT;  
      • return (-1);  
      • }  
      • if (FD_ISSET(sockfd, &wset)) {  
      • len = sizeof(error);  
      • code = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);  
      • /* 如果发生错误,Solaris实现的getsockopt返回-1, 
      • * 把pending error设置给errno. Berkeley实现的 
      • * getsockopt返回0, pending error返回给error.  
      • * 我们需要处理这两种情况 */  
      • if (code < 0 || error) {  
      • close(sockfd);  
      • if (error)   
      • errno = error;  
      • return (-1);  
      • }  
      • } else {  
      • fprintf(stderr, "select error: sockfd not set");  
      • exit(0);  
      • }  
      • done:  
      • fcntl(sockfd, F_SETFL, flags);  /* restore file status flags */  
      • return (0);  
  • 相关阅读:
    【python】python读写文件,都不乱码
    【python】python读取文件报错UnicodeDecodeError: 'gbk' codec can't decode byte 0xac in position 2: illegal multibyte sequence
    【python】python编码方式,chardet编码识别库
    【IntelliJ IDEA】idea设置UTF-8的位置
    【IntelliJ IDEA】idea或者JetBrains公司所有编辑器,设置其软件的字体样式
    【java】判断某段字符串的编码方式,并按照新的编码方式输出
    editplus多行合并成一行
    java解决手机上传竖拍照片旋转90180270度问题
    利用exif.js解决手机上传竖拍照片旋转90180270度问题
    win10下搭建storm环境
  • 原文地址:https://www.cnblogs.com/csun/p/6228449.html
Copyright © 2011-2022 走看看