~/cpp$ ./connect 192.168.1.234 1234 kkkk

block mode:  ubuntu 14.04 : time used:21.0.001053s

connect 超时时间是大约21秒!

注意:如果connect 127.x.x.x  xxx  kkkk 会立即返回因为127开头的是网卡自身,你可以ping一下,发现都是通的,且等同于127.0.0.1


#include <sys/socket.h> 
     
 
     
 
      #include <arpa/inet.h> 
     
 
     
 
      #include <assert.h> 
     
 
     
 
      #include <stdio.h> 
     
 
     
 
      #include <unistd.h> 
     
 
     
 
      #include <string.h> 
     
 
     
 
      #include <errno.h> 
     
 
     
 
      #include <stdlib.h> 
     
 
     
 
      #include <fcntl.h> 
     
 
     
 
      #include <sys/time.h> 
     
 
     

         
     
 
     
 
      #define BUFFER_SIZE 512 
     
 
     

         
     
 
     
 
      int 
      setnonblocking(  
      int 
      fd ) 
     
 
     
 
      { 
     
 
     
 
       
      int 
      old_option = fcntl( fd, F_GETFL ); 
     
 
     
 
       
      int 
      new_option = old_option | O_NONBLOCK; 
     
 
     
 
       
      fcntl( fd, F_SETFL, new_option ); 
     
 
     
 
       
      return 
      old_option; 
     
 
     
 
      } 
     
 
     
 
      int 
      main(  
      int 
      argc,  
      char 
      * argv[] ) 
     
 
     
 
      { 
     
 
     
 
       
      if 
      ( argc <= 3 ) 
     
 
     
 
       
      { 
     
 
     
 
       
      printf 
      (  
      "usage: %s ip_address port_number send_bufer_size\n" 
      , basename( argv[0] ) ); 
     
 
     
 
       
      return 
      1; 
     
 
     
 
       
      } 
     
 
     
 
       
      const 
      char 
      * ip = argv[1]; 
     
 
     
 
       
      int 
      port =  
      atoi 
      ( argv[2] ); 
     
 
     

         
     
 
     
 
       
      struct 
      sockaddr_in server_address; 
     
 
     
 
       
      bzero( &server_address,  
      sizeof 
      ( server_address ) ); 
     
 
     
 
       
      server_address.sin_family = AF_INET; 
     
 
     
 
       
      inet_pton( AF_INET, ip, &server_address.sin_addr ); 
     
 
     
 
       
      server_address.sin_port = htons( port ); 
     
 
     

         
     
 
     
 
       
      int 
      sock = socket( PF_INET, SOCK_STREAM, 0 ); 
     
 
     
 
       
      assert 
      ( sock >= 0 ); 
     
 
     

         
     
 
     
 
       
      int 
      sendbuf =  
      atoi 
      ( argv[3] ); 
     
 
     
 
       
      int 
      len =  
      sizeof 
      ( sendbuf ); 
     
 
     
 
       
      setsockopt( sock, SOL_SOCKET, SO_SNDBUF, &sendbuf,  
      sizeof 
      ( sendbuf ) ); 
     
 
     
 
       
      getsockopt( sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, ( socklen_t* )&len ); 
     
 
     
 
       
      printf 
      (  
      "the tcp send buffer size after setting is %d\n" 
      , sendbuf ); 
     
 
     

         
     
 
     
 
       
      int 
      old_option = fcntl( sock, F_GETFL ); 
     
 
     
 
       
      printf 
      ( 
      "noblock: %d\n" 
      , old_option & O_NONBLOCK);  
      //0-->block mode 
     
 
     

         
     
 
     
 
       
      //int oldopt = setnonblocking(sock); set nonblock mode! 
     
 
     

         
     
 
     
 
       
      struct  
      timeval tv1, tv2; 
     
 
     
 
       
      gettimeofday(&tv1, NULL); 
     
 
     

         
     
 
     
 
       
      int 
      ret = connect( sock, (  
      struct 
      sockaddr* )&server_address,  
      sizeof 
      ( server_address ) ); 
     
 
     
 
       
      printf 
      ( 
      "connect ret code is: %d\n" 
      , ret); 
     
 
     
 
       
      if 
      (  ret == 0 ) 
     
 
     
 
       
      { 
     
 
     
 
       
      // 
     
 
     
 
       
      printf 
      ( 
      "call getsockname ...\n" 
      ); 
     
 
     
 
       
      struct 
      sockaddr_in local_address; 
     
 
     
 
       
      socklen_t length; 
     
 
     
 
       
      int 
      ret = getpeername(sock, (  
      struct 
      sockaddr* )&local_address, &length); 
     
 
     
 
       
      assert 
      (ret == 0); 
     
 
     
 
       
      char 
      local[INET_ADDRSTRLEN ]; 
     
 
     
 
       
      printf 
      (  
      "local with ip: %s and port: %d\n" 
      , 
     
 
     
 
       
      inet_ntop( AF_INET, &local_address.sin_addr, local, INET_ADDRSTRLEN ), ntohs( local_address.sin_port ) ); 
     
 
     
 
       
      // 
     
 
     

         
     
 
     
 
       
      char 
      buffer[ BUFFER_SIZE ]; 
     
 
     
 
       
      memset 
      ( buffer,  
      'a' 
      , BUFFER_SIZE ); 
     
 
     
 
       
      send( sock, buffer, BUFFER_SIZE, 0 ); 
     
 
     
 
       
      } 
     
 
     
 
       
      else 
      if 
      (ret == -1) 
     
 
     
 
       
      { 
     
 
     
 
       
      gettimeofday(&tv2, NULL); 
     
 
     
 
       
      suseconds_t msec = tv2.tv_usec - tv1.tv_usec; 
     
 
     
 
       
      time_t 
      sec = tv2.tv_sec - tv1.tv_sec; 
     
 
     
 
       
      printf 
      ( 
      "time used:%d.%fs\n" 
      , sec, ( 
      double 
      )msec / 1000000 ); 
     
 
     

         
     
 
     
 
       
      printf 
      ( 
      "connect failed...\n" 
      ); 
     
 
     
 
       
      if 
      ( 
      errno 
      == EINPROGRESS) 
     
 
     
 
       
      { 
     
 
     
 
       
      printf 
      ( 
      "unblock mode ret code...\n" 
      ); 
     
 
     
 
       
      } 
     
 
     
 
       
      } 
     
 
     
 
       
      else 
     
 
     
 
       
      { 
     
 
     
 
       
      printf 
      ( 
      "ret code is: %d\n" 
      , ret); 
     
 
     
 
       
      } 
     
 
     

         
     
 
     
 
       
      printf 
      ( 
      "after connected!\n" 
      ); 
     
 
     

         
     
 
     
 
       
      close( sock ); 
     
 
     
 
       
      return 
      0; 
     
 
     
 
      } 
     
 
    
 
 
  
非阻塞模式:

 
 
 
   #include <sys/types.h> 
  
 
  
 
   #include <sys/socket.h> 
  
 
  
 
   #include <netinet/in.h> 
  
 
  
 
   #include <arpa/inet.h> 
  
 
  
 
   #include <stdlib.h> 
  
 
  
 
   #include <assert.h> 
  
 
  
 
   #include <stdio.h> 
  
 
  
 
   #include <time.h> 
  
 
  
 
   #include <errno.h> 
  
 
  
 
   #include <fcntl.h> 
  
 
  
 
   #include <sys/ioctl.h> 
  
 
  
 
   #include <unistd.h> 
  
 
  
 
   #include <string.h> 
  
 
  

      
  
 
  
 
   #define BUFFER_SIZE 1023 
  
 
  

      
  
 
  
 
   int 
   setnonblocking(  
   int 
   fd ) 
  
 
  
 
   { 
  
 
  
 
    
   int 
   old_option = fcntl( fd, F_GETFL ); 
  
 
  
 
    
   int 
   new_option = old_option | O_NONBLOCK; 
  
 
  
 
    
   fcntl( fd, F_SETFL, new_option ); 
  
 
  
 
    
   return 
   old_option; 
  
 
  
 
   } 
  
 
  

      
  
 
  
 
   int 
   unblock_connect(  
   const 
   char 
   * ip,  
   int 
   port,  
   int 
   time 
   ) 
  
 
  
 
   { 
  
 
  
 
    
   int 
   ret = 0; 
  
 
  
 
    
   struct 
   sockaddr_in address; 
  
 
  
 
    
   bzero( &address,  
   sizeof 
   ( address ) ); 
  
 
  
 
    
   address.sin_family = AF_INET; 
  
 
  
 
    
   inet_pton( AF_INET, ip, &address.sin_addr ); 
  
 
  
 
    
   address.sin_port = htons( port ); 
  
 
  

      
  
 
  
 
    
   int 
   sockfd = socket( PF_INET, SOCK_STREAM, 0 ); 
  
 
  
 
    
   int 
   fdopt = setnonblocking( sockfd ); 
  
 
  
 
    
   ret = connect( sockfd, (  
   struct 
   sockaddr* )&address,  
   sizeof 
   ( address ) ); 
  
 
  
 
    
   printf 
   ( 
   "connect ret code = %d\n" 
   , ret); 
  
 
  
 
    
   if 
   ( ret == 0 ) 
  
 
  
 
    
   { 
  
 
  
 
    
   printf 
   (  
   "connect with server immediately\n" 
   ); 
  
 
  
 
    
   fcntl( sockfd, F_SETFL, fdopt );    
   //set old optional back 
  
 
  
 
    
   return 
   sockfd; 
  
 
  
 
    
   } 
  
 
  
 
    
   //unblock mode --> connect return immediately! ret = -1 & errno=EINPROGRESS 
  
 
  
 
    
   else 
   if 
   (  
   errno 
   != EINPROGRESS )  
  
 
  
 
    
   { 
  
 
  
 
    
   printf 
   ( 
   "ret = %d\n" 
   , ret); 
  
 
  
 
    
   printf 
   (  
   "unblock connect failed!\n" 
   ); 
  
 
  
 
    
   return 
   -1; 
  
 
  
 
    
   } 
  
 
  
 
    
   else 
   if 
   ( 
   errno 
   == EINPROGRESS) 
  
 
  
 
    
   { 
  
 
  
 
    
   printf 
   (  
   "unblock mode socket is connecting...\n" 
   ); 
  
 
  
 
    
   } 
  
 
  

      
  
 
  
 
    
   //use select to check write event, if the socket is writable, then  
  
 
  
 
    
   //connect is complete successfully! 
  
 
  
 
    
   fd_set readfds; 
  
 
  
 
    
   fd_set writefds; 
  
 
  
 
    
   struct 
   timeval timeout; 
  
 
  

      
  
 
  
 
    
   FD_ZERO( &readfds ); 
  
 
  
 
    
   FD_SET( sockfd, &writefds ); 
  
 
  

      
  
 
  
 
    
   timeout.tv_sec =  
   time 
   ;  
   //timeout is 10 minutes 
  
 
  
 
    
   timeout.tv_usec = 0; 
  
 
  

      
  
 
  
 
    
   ret = select( sockfd + 1, NULL, &writefds, NULL, &timeout ); 
  
 
  
 
    
   if 
   ( ret <= 0 ) 
  
 
  
 
    
   { 
  
 
  
 
    
   printf 
   (  
   "connection time out\n" 
   );  
  
 
  
 
    
   close( sockfd ); 
  
 
  
 
    
   return 
   -1; 
  
 
  
 
    
   } 
  
 
  

      
  
 
  
 
    
   if 
   ( ! FD_ISSET( sockfd, &writefds  ) ) 
  
 
  
 
    
   { 
  
 
  
 
    
   printf 
   (  
   "no events on sockfd found\n" 
   ); 
  
 
  
 
    
   close( sockfd ); 
  
 
  
 
    
   return 
   -1; 
  
 
  
 
    
   } 
  
 
  

      
  
 
  
 
    
   int 
   error = 0; 
  
 
  
 
    
   socklen_t length =  
   sizeof 
   ( error ); 
  
 
  
 
    
   if 
   ( getsockopt( sockfd, SOL_SOCKET, SO_ERROR, &error, &length ) < 0 ) 
  
 
  
 
    
   { 
  
 
  
 
    
   printf 
   (  
   "get socket option failed\n" 
   ); 
  
 
  
 
    
   close( sockfd ); 
  
 
  
 
    
   return 
   -1; 
  
 
  
 
    
   } 
  
 
  

      
  
 
  
 
    
   if 
   ( error != 0 ) 
  
 
  
 
    
   { 
  
 
  
 
    
   printf 
   (  
   "connection failed after select with the error: %d \n" 
   , error ); 
  
 
  
 
    
   close( sockfd ); 
  
 
  
 
    
   return 
   -1; 
  
 
  
 
    
   } 
  
 
  
 
     
  
 
  
 
    
   //connection successful! 
  
 
  
 
    
   printf 
   (  
   "connection ready after select with the socket: %d \n" 
   , sockfd ); 
  
 
  
 
    
   fcntl( sockfd, F_SETFL, fdopt );  
   //set old optional back 
  
 
  
 
    
   return 
   sockfd; 
  
 
  
 
   } 
  
 
  

      
  
 
  
 
   int 
   main(  
   int 
   argc,  
   char 
   * argv[] ) 
  
 
  
 
   { 
  
 
  
 
    
   if 
   ( argc <= 2 ) 
  
 
  
 
    
   { 
  
 
  
 
    
   printf 
   (  
   "usage: %s ip_address port_number\n" 
   , basename( argv[0] ) ); 
  
 
  
 
    
   return 
   1; 
  
 
  
 
    
   } 
  
 
  
 
    
   const 
   char 
   * ip = argv[1]; 
  
 
  
 
    
   int 
   port =  
   atoi 
   ( argv[2] ); 
  
 
  

      
  
 
  
 
    
   int 
   sockfd = unblock_connect( ip, port, 10 ); 
  
 
  
 
    
   if 
   ( sockfd < 0 ) 
  
 
  
 
    
   { 
  
 
  
 
    
   printf 
   ( 
   "sockfd error! return -1\n" 
   ); 
  
 
  
 
    
   return 
   1; 
  
 
  
 
    
   } 
  
 
  
 
    
   //shutdown( sockfd, SHUT_WR ); //disable read and write 
  
 
  
 
    
   printf 
   (  
   "send data out\n" 
   ); 
  
 
  
 
    
   send( sockfd,  
   "abc" 
   , 3, 0 ); 
  
 
  
 
    
   shutdown( sockfd, SHUT_WR );  
   //disable read and write 
  
 
  
 
    
   close(sockfd); 
  
 
  
 
    
   return 
   0; 
  
 
  
 
   }


建立socket后默认connect()函数为阻塞连接状态,在大多数实现中,connect的超时时间在75s至几分钟之间,想要缩短超时时间,可解决问题的两种方法:方法一、将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的实现是返回-1,待处理的错误存储在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在连接建立成功(使套接口描述符可写)或连接建立失败(使套接口描述符既可读又可写)时返回;

 

方法二、定义信号处理函数:

sigset(SIGALRM, u_alarm_handler);
alarm(2);
code = connect(socket_fd, (struct sockaddr*)&socket_st, sizeof(struct sockaddr_in));
alarm(0);
sigrelse(SIGALRM);

 

首先定义一个中断信号处理函数u_alarm_handler,用于超时后的报警处理,然后定义一个2秒的定时器,执行connect,当系统connect成功,则系统正常执行下去;如果connect不成功阻塞在这里,则超过定义的2秒后,系统会产生一个信号,触发执行u_alarm_handler函数, 当执行完u_alarm_handler后,程序将继续从connect的下面一行执行下去。
其中,处理函数可以如下定义,也可以加入更多的错误处理。

 

void u_alarm_handler()
{
}