TCP之控制connect超时

客户端在连接服务器时,可能会出现问题,导致三次握手无法完成,持续重试,表现在客户端程序的行为就是卡在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).


在 CSDN 开发的 C 知道 中,您可以使用一些套接字选项来设置 TCP 连接的超时时间。一种常用的方法是使用 `connect()` 函数之前,使用 `setsockopt()` 函数设置 `SO_RCVTIMEO` 或 `SO_SNDTIMEO` 选项。 以下是一个示例代码片段,展示了如何设置连接超时时间为 5 秒: ```c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> int main() { int sockfd; struct sockaddr_in server_addr; int connect_timeout = 5; // 超时时间为 5 秒 // 创建套接字 sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { perror("socket"); exit(1); } // 设置连接超时选项 struct timeval timeout; timeout.tv_sec = connect_timeout; timeout.tv_usec = 0; if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) { perror("setsockopt"); exit(1); } if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) == -1) { perror("setsockopt"); exit(1); } // 设置服务器地址 server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); // 设置服务器端口号 server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 设置服务器 IP 地址 // 连接服务器 if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { perror("connect"); exit(1); } // 连接成功,进行后续操作 // 关闭套接字 close(sockfd); return 0; } ``` 在上述示例中,我们使用了 `setsockopt()` 函数来设置 `SO_RCVTIMEO` 和 `SO_SNDTIMEO` 选项,将超时时间设置为 5 秒。这样,在调用 `connect()` 函数时,如果连接超时或者连接过程中发生错误,会返回相应的错误码。 请注意,以上代码仅用于演示如何设置连接超时时间,实际应用中可能需要根据具体情况进行适当修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值