Chinaunix首页 | 论坛 | 博客
  • 博客访问: 527451
  • 博文数量: 28
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 3824
  • 用 户 组: 普通用户
  • 注册时间: 2013-07-27 00:06
个人简介

一直从事高性能高并发服务器研究

文章分类

全部博文(28)

文章存档

2013年(28)

分类: LINUX

2013-09-11 14:35:09

客户端在连接服务器时,可能会出现问题,导致三次握手无法完成,持续重试,表现在客户端程序的行为就是卡在connect调用上无法返回,这样的客户端是非常不友好的。
下面是我以前做开发工程师的时候常常在项目中使用的可控制超时的connect源码,3秒超时,粘贴即可使用。

正确程序:

#include  

#include  

#include  

#include  

#include  

#include     

#include  

#include  

#include  

#include  

 

int   main(int   argc,   char   *argv[])

{

int fd, retval;

struct sockaddr_in addr;

struct   timeval   timeo =   {3,0};

socklen_t len = sizeof(timeo);

fd_set set;

 

fd =   socket(AF_INET,   SOCK_STREAM,   0);

if   (argc   ==   4)   timeo.tv_sec   =   atoi(argv[3]);

int   savefl   =   fcntl(fd,F_GETFL);

fcntl(fd,   F_SETFL,   savefl   |   O_NONBLOCK);

addr.sin_family   =   AF_INET;

addr.sin_addr.s_addr   =   inet_addr(argv[1]);

addr.sin_port   =   htons(atoi(argv[2]));

printf( "%d\n ",   time(NULL));

if   (connect(fd,   (struct   sockaddr*)&addr,   sizeof(addr))   ==   0){

close(fd);

printf( "connected..1\n ");

return   0;
}

 

if   (errno   !=   EINPROGRESS){

close(fd);

perror( "connect..2 ");

return   -1;

}

FD_ZERO(&set);

FD_SET(fd,   &set);

retval = select(fd + 1,   NULL,   &set,   NULL,   &timeo);

if   (retval   ==   -1)

{

close(fd);

perror( "select ");

return   -1;

}

else   if(retval   ==   0)

{

close(fd);

fprintf(stderr,   "timeout\n ");

printf( "%d\n ",   time(NULL));

return   0;

}

 

if(FD_ISSET   (fd,&set))

{

int   error   =   0; 

socklen_t   len   =   sizeof   (error);

if(getsockopt(fd,   SOL_SOCKET,   SO_ERROR,   &error,   &len)   <   0)
{

printf   ( "getsockopt  fail,connected  fail\n ");

return   -1;


}


 

if   (error   ==   ETIMEDOUT)

{

printf   ( "connected   timeout\n ");

}

 

if(error   ==   ECONNREFUSED)

{

printf( "No   one   listening   on   the   remote   address.\n ");

return   -1;

}

}

printf   ( "connected   ..   3\n ");

fcntl(fd,   F_SETFL,   savefl);

close   (fd);

 

return   0;

}

 

 

 

 

 

有问题的程序:

#include  

#include  

#include  

#include  

#include  

#include  

#include  

#include  

#include  

#include  

 

int   main(int   argc,   char   *argv[])

{

int   fd,   retval;

struct   sockaddr_in   addr;

struct   timeval   timeo   =   {3,   0};

socklen_t   len   =   sizeof(timeo);

fd_set   set;

 

fd   =   socket(AF_INET,   SOCK_STREAM,   0);

if   (argc   ==   4)   timeo.tv_sec   =   atoi(argv[3]);

int   savefl   =   fcntl(fd,F_GETFL);

fcntl(fd,   F_SETFL,   savefl   |   O_NONBLOCK);

addr.sin_family   =   AF_INET;

addr.sin_addr.s_addr   =   inet_addr(argv[1]);

addr.sin_port   =   htons(atoi(argv[2]));

printf( "%d\n ",   time(NULL));

if   (connect(fd,   (struct   sockaddr*)&addr,   sizeof(addr))   ==   0)

{

close(fd);

printf( "connected..1\n ");

return   0;

}

 

if   (errno   !=   EINPROGRESS)

{

close(fd);

perror( "connect..2 ");

return   -1;

}

FD_ZERO(&set);

FD_SET(fd,   &set);

retval   =   select(fd   +   1,   NULL,   &set,   NULL,   &timeo);

if   (retval   ==   -1)

{

close(fd);

perror( "select ");

return   -1;

}

else   if(retval   ==   0)

{

close(fd);

fprintf(stderr,   "timeout\n ");

printf( "%d\n ",   time(NULL));

return   0;

}

printf( "connected   ..   3\n ");

fcntl(fd,   F_SETFL,savefl);

close(fd);

return   0;

}

 

注意:

1.两个程序连远程主机都没问题

2.有问题的程序连本地任何端口都不会报错

3.差别在于好程序比有问题的程序多了一个SO_ERROR的检测,这个检测在man connect中被指定了要检测

EINPROGRESS

The socket is non-blocking and the connection cannot be completed immediately.  It is possible to select(2) or poll(2) for completion by selecting the socket for writing.  After select(2) indicates writability, use getsockopt(2) to read the SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error codes listed here, explaining the reason for the failure).

阅读(7020) | 评论(0) | 转发(4) |
给主人留下些什么吧!~~