博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
9-调用connect连接超时
阅读量:2044 次
发布时间:2019-04-28

本文共 2818 字,大约阅读时间需要 9 分钟。

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

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

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

服务端程序:

#include 
#include
#include
#include
#include
#include
#include
#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 
#include
#include
#include
#include
#include
#include
#define SERV_IP "192.168.0.107"#define SERV_PORT 10001int 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函数返回失败,然后退出。

你可能感兴趣的文章
Go语言学习Part4-1:方法和接口
查看>>
Leetcode Go 《精选TOP面试题》20200628 69.x的平方根
查看>>
Leetcode C++ 剑指 Offer 09. 用两个栈实现队列
查看>>
Leetcode C++《每日一题》20200707 112. 路径总和
查看>>
Leetcode C++ 《第198场周赛-2》 1519. 子树中标签相同的节点数
查看>>
Leetcode C++ 《第199场周赛》
查看>>
Leetcode C++ 《第200场周赛》
查看>>
Leetcode C++ 《第201场周赛》
查看>>
云原生 第十章 应用存储和持久化数据卷:存储快照和拓扑调度
查看>>
云原生 第十一章 应用健康
查看>>
Leetcode C++ 《第202场周赛》
查看>>
云原生 第十二章 可观测性:监控与日志
查看>>
Leetcode C++ 《第203场周赛》
查看>>
云原生 第十三章 Kubernetes网络概念及策略控制
查看>>
《redis设计与实现》 第一部分:数据结构与对象 || 读书笔记
查看>>
《redis设计与实现》 第二部分(第9-11章):单机数据库的实现
查看>>
Leetcode C++《热题 Hot 100-70》23.合并K个升序链表
查看>>
《redis设计与实现》第二部分 (第12章:事件)
查看>>
《redis设计与实现》第二部分 (第13章 客户端)
查看>>
《redis设计与实现》第二部分 (第14章 服务器)
查看>>