9-调用connect连接超时

  简单来说,连接超时就是当客户端调用connect函数跟服务端建立连接,等待一段时间后,最后connect函数返回ETIMEDOUT错误,建立连接失败。那么连接超时具体是怎么出现的呢?一般是客户端调用connect发送的SYN报文在网络传输过程中发生网络拥塞,导致报文丢失或服务端收到SYN,但未及时响应。

  而这种情况一般发生在服务端的可能性比较大,因为服务端所处的网络流量环境负载通常都很高,如果发生网络拥塞,又或者服务器被D-Dos攻击了,那么是极有可能出现连接超时这种情况的。


  为了模拟这种情况,通过ifconfig命令可以查看网卡信息,我们把服务端eth3网卡的所有SYN包都过滤掉:
iptables -F
iptables -I INPUT -p tcp --syn -i eth3 -j DROP

服务端程序:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>

#define SERV_PORT 10001
#define SERV_IP "192.168.0.107"

int main(void) {
	int sfd, cfd;
	int len, i;
	//BUFSIZ是系统内嵌的一个宏,用来指定buf大小
	char buf[BUFSIZ], clie_IP[BUFSIZ];
	struct sockaddr_in serv_addr, clie_addr;
	socklen_t clie_addr_len;
	sfd = socket(AF_INET, SOCK_STREAM, 0);
	bzero(&serv_addr, sizeof(serv_addr));      
	serv_addr.sin_family = AF_INET;           
	inet_pton(AF_INET , SERV_IP , &serv_addr.sin_addr.s_addr);
	serv_addr.sin_port = htons(SERV_PORT);              

	bind(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));

	listen(sfd, 64);
	printf("wait for client connect ...\n");
	clie_addr_len = sizeof(clie_addr);
	//阻塞,等待客户端发起连接
	cfd = accept(sfd, (struct sockaddr *)&clie_addr, &clie_addr_len);
	//打印客户端的ip地址和端口号
	printf("client IP:%s\tport:%d\n", 
			inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_IP, sizeof(clie_IP)), 
			ntohs(clie_addr.sin_port));

	while (1) {
		//循环读取客户端的数据请求
		len = read(cfd, buf, sizeof(buf));
        //read返回0说明对端已经关闭
		if(len == 0)
		{
			break;
		}
		write(STDOUT_FILENO, buf, len);

		//处理客户端数据,小写转大写
		for (i = 0; i < len; i++){
			buf[i] = toupper(buf[i]);
		}
		write(cfd, buf, len);
	}
	close(sfd);
	close(cfd);
	return 0;
}

客户端程序:

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

#define SERV_IP "192.168.0.107"
#define SERV_PORT 10001

int main(void) {
	int sfd, len,ret;
	struct sockaddr_in serv_addr;
	char buf[BUFSIZ];
	sfd = socket(AF_INET, SOCK_STREAM, 0);
	bzero(&serv_addr, sizeof(serv_addr));                       
	serv_addr.sin_family = AF_INET;                         
	inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); 
	serv_addr.sin_port = htons(SERV_PORT);
    //建立连接                      
	ret = connect(sfd, (struct sockaddr *)&serv_addr ,  sizeof(serv_addr));
	if(ret < 0){
		perror("connect error:");
		exit(0);
	}
	//循环读写数据
	while (1) {
		fgets(buf, sizeof(buf), stdin);
		//将数据写给服务器
		write(sfd, buf, strlen(buf)); 
		//从服务器读取转换后数据
		len = read(sfd, buf, sizeof(buf));
		write(STDOUT_FILENO, buf, len);

		if(buf[0] == 'Q'){
			break;
		}
	}
	close(sfd);
	return 0;
}

./server先启动服务端,然后./client再启动客户端,通过tcpdump抓取到的报文如下:

这里写图片描述

  客户端总共向服务端发送了6个YSN报文,后面5个SYN是重传的,每一次重传SYN包的间隔时间分别是1s,2s,4s,8s,16s,这些时间累积加起来总共为31s,也就是说客户端的SYN超时重传的时间间隔采用了指数退避算法增长的,而在重传最后一个报文其实还等待了32s,也就是说总共等待了63s才超时。

  换句话说,客户端在第一次发送SYN报文时就会启动一个计时器,如果在该计时器的时间内还未收到对端的ACK,那么将超时重传。当然这个超时重传也是有次数的,不会一直重传,从tcpdump抓取到的数据包来看,客户端一旦重传超过5次,那么客户端tcp将会关闭这条连接。


这里写图片描述
最后connect函数返回失败,然后退出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值