connect超时设置

socket编程中,当客户端用connect()去连接一个服务端是,如果服务器或网络繁忙,或连接了一个未开机的主机时,客户端发送的SYN包得不到响应,connect函数会很久不返回(阻塞模式下)。具体的超时时间与系统相关,有的可能设置为75秒,有的可能设置为120秒等等。在有的应用中,阻塞太久不是程序想要的行为?

connect超时时间与/proc/sys/net/ipv4/tcp_syn_retries的值有关,这个值决定了重发SYN包的次数,另一方面重发SYN包的间隔时间系列是:1, 2, 4, 8, 16, 32, 64, 120, 120... ...这个时间系列是固定的,所以tcp_syn_retries的值越大,connect超时时间就越长。这是一个系统设置,我们通过改这个值来缩短connect的超时时间显然不太合理。(可以搜索sysctl查看如何修改)

下面我们看看如何用其它方式来修改connect超时时间
方法一:
步骤
  1. 建立socket
  2. 把socket设置为非阻塞模式
  3. 调用connect
  4. 使用select检查该socket描述符是否可写(注意,是可写)
  5. 根据select返回结果判断connect结果
  6. 将socket恢复为阻塞模式
代码示例
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>


int connect_timeout(int sockfd, struct sockaddr *serv_addr, int addrlen, int timeout);

int open_clientfd(const char *server, int port)
{
	struct sockaddr_in serverAddr;
	struct hostent *hostp;

	int clientfd = socket(AF_INET, SOCK_STREAM, 0);
	if (clientfd < 0)
	{
		perror("socket: ");
		return -1;
	}
	if ( (hostp = gethostbyname(server)) == NULL)
	{
		perror("gethostbyname: ");
		close(clientfd);
		return -1;
	}

	bzero((char *)&serverAddr, sizeof(serverAddr));
	memcpy((void *)&(serverAddr.sin_addr), (void *)hostp->h_addr_list[0],hostp->h_length);

	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = ntohs(port);

	int n = connect_timeout(clientfd, (sockaddr *)&serverAddr, sizeof(serverAddr), 10000);
	if (n<0)
	{
		printf("error\n");
		close(clientfd);
		return -1;
	}
	else if (n==0)
	{
		printf("time out\n");
		close(clientfd);
		return -1;
	}
	else
		printf("connect sucessed\n");

	return clientfd;
}

/*
	超时版的connect,timeout单位是毫秒
*/
int connect_timeout(int sockfd, struct sockaddr *serv_addr, int addrlen, int timeout)
{
	int flags = fcntl(sockfd, F_GETFL, 0); 
	fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

	int n = connect(sockfd, serv_addr, sizeof(*serv_addr));
	if(n < 0) 
	{  
		/*
		EINPROGRESS表示connect正在尝试连接 
		#define EWOULDBLOCK EAGAIN Operation would block 
		*/
		if(errno != EINPROGRESS && errno != EWOULDBLOCK) 
			return -1; 

		struct timeval tv; 
		tv.tv_sec = timeout/1000; 
		tv.tv_usec = (timeout - tv.tv_sec*1000)*1000; 
		fd_set wset; 
		FD_ZERO(&wset); 
		FD_SET(sockfd, &wset); 
		n = select(sockfd+1, NULL, &wset, NULL, &tv); 
		if(n < 0) 
		{ 
			// select出错 		
			return -1; 
		} 
		else if (0 == n) 
		{ 
			// 超时 				
			return 0; 
		} 		
	} 
	fcntl(fd,F_SETFL,flags & ~O_NONBLOCK);  // 恢复为阻塞模式 
	return 1;
}

int main(int argc, char **argv)
{
	int sk = open_clientfd(argv[1], atoi(argv[2]));
	printf("socket: %d\n", sk);
	if (sk>0)
		close(sk);
	return 0;
}

方法二(转):
linux内核中,connect的实现用到的超时参数是sndtimeo
net/ipv4/af_inet.c
	timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);

	if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
		int writebias = (sk->sk_protocol == IPPROTO_TCP) &&
				tcp_sk(sk)->fastopen_req &&
				tcp_sk(sk)->fastopen_req->data ? 1 : 0;

		/* Error code is set above */
		if (!timeo || !inet_wait_for_connect(sk, timeo, writebias))
			goto out;

		err = sock_intr_errno(timeo);
		if (signal_pending(current))
			goto out;
	}
这意味着:在Linux平台下,可以通过在connect之前设置SO_SNDTIMO来达到控制连接超时的目的。
示例代码如下:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>

int main(int argc, char *argv[])
{
	int fd;
	struct sockaddr_in addr;
	struct timeval timeo = {3, 0};
	socklen_t len = sizeof(timeo);

	fd = socket(AF_INET, SOCK_STREAM, 0);
	if (argc == 4)
		timeo.tv_sec = atoi(argv[3]);
	setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, len);
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(argv[1]);
	addr.sin_port = htons(atoi(argv[2]));
	if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
		if (errno == EINPROGRESS) {
			fprintf(stderr, "timeout\n");
			return -1;
		}
		perror("connect");
		return 0;
	}
	printf("connected\n");

	return 0;
}



  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Linux平台下,可以通过设置超时时间来控制connect操作的超时判断。而在Windows平台上,默认的connect超时时间大约是16秒。 在一些特定的情况下,SYN重传次数也会影响connect操作的超时时间。当重传次数为6时,超时时间会按照指数增长的方式进行计算,即1、2、4、8、16、32、64,最终总共为127秒。 在BSD系统中,可以使用setsockopt函数来设置connect超时时间。例如,可以通过设置TCP_CONNECTIONTIMEOUT选项来设置超时时间为30秒。具体的代码示例如下: ``` int connectTimeout = 30; setsockopt(sock, IPPROTO_TCP, TCP_CONNECTIONTIMEOUT, (char *)&connectTimeout, sizeof(connectTimeout)); ``` 这样就能够将connect超时时间设置为30秒。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [windows socket 设置connect超时(附Linux平台connect超时设置)](https://blog.csdn.net/v6543210/article/details/102704756)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [设置socket connect超时时间的几种方法](https://blog.csdn.net/tjcwt2011/article/details/115939647)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值