zoukankan      html  css  js  c++  java
  • 非阻塞socket的连接

    引用自:http://blog.csdn.net/cccallen/article/details/6619994

    连接套接字,阻塞的套接字超时时间很长无法接受,而是用非阻塞套接字时使用的方案也有多种。后者是个比较好的方法 
    方案1:不断重试,直到连接上或者超时:

     

    1. int connect_socket_timeout(int sockfd,char *dest_host, int port, int timeout)  
    2. {  
    3.   struct sockaddr_in address;  
    4.   struct in_addr inaddr;  
    5.   struct hostent *host;  
    6.   int  err, noblock=1 , connect_ok=0, begin_time=time(NULL);  
    7.   log_debug("connect_socket to %s:%dn",dest_host,port);  
    8.   if (inet_aton(dest_host, &inaddr))  
    9.   {  
    10.          log_debug("inet_aton ok now gethostbyaddr %sn",dest_host);  
    11.          memcpy(&address.sin_addr, &inaddr, sizeof(address.sin_addr));  
    12.   }  
    13.   else  
    14.   {  
    15.           log_debug("inet_aton fail now gethostbyname %s n",dest_host);  
    16.           host = gethostbyname(dest_host);  
    17.           if (!host) {  
    18.          /* We can't find an IP number */  
    19.            log_error("error looking up host  %s : %dn",dest_host,errno);  
    20.             return -1;  
    21.            }  
    22.             memcpy(&address.sin_addr, host->h_addr_list[0], sizeof(address.sin_addr));  
    23.   }  
    24.   address.sin_family = AF_INET;  
    25.   address.sin_port = htons(port);  
    26.   /* Take the first IP address associated with this hostname */  
    27.   ioctl(sockfd,FIONBIO,&noblock);  
    28.   /** connect until timeout */  
    29.   /* 
    30.   EINPROGRESS   A nonblocking socket connection cannot be completed immediately. 
    31.   EALREADY     The socket is nonblocking and a  previous connection attempt has not been completed. 
    32.   EISCONN      The socket is already connected. 
    33.   */  
    34.   if (connect(sockfd, (struct sockaddr *) &address, sizeof(address)) < 0)  
    35.   {  
    36.   err = errno;  
    37.   if (err != EINPROGRESS)  
    38.   {  
    39.          log_error("connect = %d connecting to host %sn", err,dest_host);  
    40.   }  
    41.   else  
    42.   {  
    43.   //                                log_notice("connect pending, return %d n", err);  
    44.     while (1) /* is noblocking connect, check it until ok or timeout */  
    45.     {  
    46.             connect(sockfd, (struct sockaddr *) &address, sizeof(address));  
    47.             err = errno;  
    48.             switch (err)  
    49.             {  
    50.                   case EISCONN:   /* connect ok */  
    51.                        connect_ok = 1;  
    52.                         break;  
    53.                  case EALREADY:  /* is connecting, need to check again */  
    54.                       //log_info("connect again return EALREADY check again...n");  
    55.                      usleep(50000);  
    56.                       break;  
    57.               default:   /* failed, retry again ? */  
    58.                      log_error("connect fail err=%d n",err);  
    59.                       connect_ok = -1;  
    60.                       break;  
    61.              }  
    62.           if (connect_ok==1)  
    63.            {  
    64.                 //log_info ("connect ok try time =%d n", (time(NULL) - begin_time) );  
    65.                 break;  
    66.            }  
    67.           if (connect_ok==-1)  
    68.           {  
    69.                log_notice ("connect failed try time =%d n", (time(NULL) - begin_time) );  
    70.          
    71.           }  
    72.          if ( (timeout>0) && ((time(NULL) - begin_time)>timeout) )  
    73.           {  
    74.               log_notice("connect failed, timeout %d secondsn", (time(NULL) - begin_time));  
    75.                break;  
    76.           }  
    77.         }  
    78.     }  
    79.   }  
    80.   else        /* Connect successful immediately        */  
    81.   {  
    82.    // log_info("connect immediate success to host %sn", dest_host);  
    83.        connect_ok = 1;  
    84.   }  
    85.   /** end of try connect */  
    86.   return ((connect_ok==1)?sockfd:-1);  
    87.   }  

     

    方案2:
      补充关于select在异步(非阻塞)connect中的应用,刚开始搞socket编程的时候我一直都用阻塞式的connect,非阻塞connect的问题是由于当时搞proxy scan
      而提出的呵呵,通过在网上与网友们的交流及查找相关FAQ,总算知道了怎么解决这一问题.同样用select可以很好地解决这一问题.大致过程是这样的:
      1.将打开的socket设为非阻塞的,可以用fcntl(socket, F_SETFL, O_NDELAY)完成(有的系统用FNEDLAY也可).
      2.发connect调用,这时返回-1,但是errno被设为EINPROGRESS,意即connect仍旧在进行还没有完成.
      3.将打开的socket设进被监视的可写(注意不是可读)文件集合用select进行监视,如果可写,用getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, sizeof(int));
      来得到error的值,如果为零,则connect成功.
      在许多unix版本的proxyscan程序你都可以看到类似的过程,另外在solaris精华区->编程技巧中有一个通用的带超时参数的connect模块.
       我们知道,缺省状态下的套接字都是阻塞方式的,这意味着一个套接口的调用不能立即完成时,进程将进入睡眠状态,并等待操作完成。对于某些应用,需要及时 可控的客户响应,而阻塞的方式可能会导致一个较长的时间段内,连接没有响应。造成套接字阻塞的操作主要有recv, send, accept, connect.
      下面主要以connect为例,讲讲非阻塞的connect的工作原理。当一个TCP套接字设置为非阻塞后,调用 connect,会立刻返回一个EINPROCESS的错误。但TCP的三路握手继续进行,我们将用select函数检查这个连接是否建立成功。建立非阻 塞的connect有下面三个用途:
      1.可以在系统做三路握手的时候做些其它事情,这段时间你可以为所欲为。
      2 可以用这个技术同时建立多个连接,在web应用中很普遍。
      3.可以缩短connect的超时时间,多数实现中,connect的超时在75秒到几分钟之间,累傻小子呢?
      虽然非阻塞的conncet实现起来并不复杂,但我们必须注意以下的细节:
      * 即使套接字是非阻塞的,如果连接的服务器是在同一台主机,connect通常会立刻建立。(connect 返回 0 而不是 EINPROCESS)
      * 当连接成功建立时,描述字变成可写
      * 当连接出错时,描述字变成可读可写

     

    1. int connect_nonb(int sockfd, const SA *saptr, socklen_t salen, int nsec)  
    2.  {  
    3.   int flags, n, error;  
    4.   socklen_t len;  
    5.   fd_set rset, wset;  
    6.   struct timeval tval;  
    7.   // 获取当前socket的属性, 并设置 noblocking 属性  
    8.   flags = fcntl(sockfd, F_GETFL, 0);  
    9.   fcntl(sockfd, F_SETFL, flags | O_NOBLOCK);  
    10.   errno = 0;  
    11.   if ( (n = connect(sockfd, saptr, salen)) < 0)  
    12.   if (errno != EINPROGRESS)  
    13.        return (-1);  
    14.   // 可以做任何其它的操作  
    15.   if (n == 0)  
    16.      goto done; // 一般是同一台主机调用,会返回 0  
    17.   FD_ZERO(&rset);  
    18.   FD_SET(sockfd, &rset);  
    19.   wset = rset;  // 这里会做 block copy  
    20.   tval.tv_sec = nsec;  
    21.   tval.tv_usec = 0;  
    22.   // 如果nsec 为0,将使用缺省的超时时间,即其结构指针为 NULL  
    23.   // 如果tval结构中的时间为0,表示不做任何等待,立刻返回  
    24.   if ((n = select(sockfd+1, &rset, &west, NULL,nsec ?tval:NULL)) == 0) {  
    25.         close(sockfd);  
    26.          errno = ETIMEOUT;  
    27.           return (-1);  
    28.   }  
    29.   if(FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &west)) {  
    30.        len = sizeof(error);  
    31.   // 如果连接成功,此调用返回 0  
    32.       if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)  
    33.            return (-1);  
    34.   }  
    35.   else     
    36.        err_quit(“select error: sockfd  not set”);  
    37.   done:  
    38.        fcntl(sockfd, F_SETFL, flags); // 恢复socket 属性  
    39.       if (error) {  
    40.          close(sockfd);  
    41.          errno = error;  
    42.          return (-1);  
    43.       }  
    44.       return (0);  
    45.   }  

    现在在网络服务器编程中使用epoll比较普遍,创建一个socket,设为异步socket(fcntl)

    connect到远端(此时connect调用返回非0,但errno为EINPROGRESS,表示正在建立连接中)

    由epoll负责监听fd的状态,epoll_wait之捕获到EPOLLOUT事件,收到EPOLLOUT也不能认为是

     

    TCP层次上connect(2)已经成功,要调用getsockopt看SOL_SOCKET的SO_ERROR是否为0。若为0,

    才表明真正的TCP层次上connect成功。至于应用层次的server是否收/发数据,那是另一回事了。

  • 相关阅读:
    vue+element-ui中的表单验证(电话等等)
    什么是 RegExp?
    C中的指针和字符串
    getchar与scanf区别
    回显程序
    Python基础之容器1----字符串和列表
    Python基础之语句2
    Python基础之语句1
    Python基础之数据基本运算
    python初识
  • 原文地址:https://www.cnblogs.com/langqi250/p/2801407.html
Copyright © 2011-2022 走看看