博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
connect/select超时, 三次握手就一定不能建立吗? 非也非也
阅读量:4141 次
发布时间:2019-05-25

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

       我们知道, 在非阻塞的socket编程中, connect函数失败, 并不意味着连接失败, 还要进行继续判断。本文先不说这个。

       本文我们来讨论这样一种情况, 在connect的时候, 如果select函数检测到连接超时, 那么三次握手就一定不能建立吗? 非也非也。

       客户端调用connect函数后, 触发协议栈发送syn包, 发起连接请求, 我们为connect函数设置1微秒的超时时间, 也就是说, 如果服务端没有在1微秒内没有回syn/ack包, 那么客户端自然认为超时。 但是, 这并不意味着三次握手不再继续了。 也可能是服务端回的syn/ack包还没有来得及达到客户端呢(正在达到的路上)!  客户端在1微秒内, 没有收到服务端的syn/ack,  但这并不影响服务端syn/ack包的姗姗来迟,  客户端收到syn/ack包后, 会照旧回复ack包, 进行第三次握手。

       所以, 我们看到, 客户端的select超时, 并不表明三次握手没法建立。

      

       我们用实际程序来看看, 服务端程序为:

 

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(){ int sockSrv = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addrSrv; addrSrv.sin_family = AF_INET; addrSrv.sin_addr.s_addr = INADDR_ANY; addrSrv.sin_port = htons(23456); bind(sockSrv, (const struct sockaddr *)&addrSrv, sizeof(struct sockaddr_in)); listen(sockSrv, 5); struct sockaddr_in addrClient; int len = sizeof(struct sockaddr_in); int sockConn = accept(sockSrv, (struct sockaddr *)&addrClient, (socklen_t*)&len); while(1) { char szRecvBuf[50001] = {0}; int iRet = recv(sockConn, szRecvBuf, sizeof(szRecvBuf) - 1, 0); printf("iRet is %d\n", iRet); getchar(); close(sockConn); } while(1); close(sockSrv); return 0;}

       先启动它。

 

 

       客户端的程序为:

 

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[]) // 注意输入参数, 带上ip和port, 带上微秒{ int sockClient = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addrSrv; addrSrv.sin_addr.s_addr = inet_addr(argv[1]); addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(atoi(argv[2])); fcntl(sockClient, F_SETFL, fcntl(sockClient, F_GETFL, 0)|O_NONBLOCK); int iRet = connect(sockClient, ( const struct sockaddr *)&addrSrv, sizeof(struct sockaddr_in)); printf("connect iRet is %d, errmsg:%s\n", iRet, strerror(errno)); // 返回-1不一定是异常 if (iRet != 0) { if(errno != EINPROGRESS) { printf("connect error:%s\n",strerror(errno)); } else { int nTime = atoi(argv[3]); // 微秒 struct timeval tm = {0, nTime}; fd_set wset, rset; FD_ZERO(&wset); FD_ZERO(&rset); FD_SET(sockClient, &wset); FD_SET(sockClient, &rset); int n = select(sockClient + 1, &rset, &wset, NULL, &tm); if(n < 0) { printf("select error, n is %d\n", n); } else if(n == 0) { printf("connect time out\n"); } else if (n == 1) { if(FD_ISSET(sockClient, &wset)) { printf("connect ok!\n"); fcntl(sockClient, F_SETFL, fcntl(sockClient, F_GETFL, 0) & ~O_NONBLOCK); } else { printf("unknow error:%s\n", strerror(errno)); } } else { printf("oh, not care now, n is %d\n", n); } } } printf("I am here!\n"); getchar(); close(sockClient); return 0;}

       我们用./client 10.100.70.140 23456 1 来向服务端发起连接, 结果为:

 

 

xxx$ ./client 10.100.70.140 23456 1connect iRet is -1, errmsg:Operation now in progressconnect time outI am here!

       可以看到, select超时。 看看网络包吧:

 

 

16:38:58.988614 IP 10.100.70.139.39077 > 10.100.70.140.aequus: Flags [S], seq 3890616217, win 14280, options [mss 1428,sackOK,TS val 1577168992 ecr 0,nop,wscale 8], length 0        0x0000:  4500 003c a7f9 4000 4006 f0e3 0a64 468b  E..<..@.@....dF.        0x0010:  0a64 468c 98a5 5ba0 e7e6 1799 0000 0000  .dF...[.........        0x0020:  a002 37c8 a20d 0000 0204 0594 0402 080a  ..7.............        0x0030:  5e01 b060 0000 0000 0103 0308 0000 0000  ^..`............        0x0040:  0000 0000 0000 0000 0000 0000            ............16:38:58.993283 IP 10.100.70.140.aequus > 10.100.70.139.39077: Flags [S.], seq 3012010132, ack 3890616218, win 14160, options [mss 1428,sackOK,TS val 1577167729 ecr 1577168992,nop,wscale 8], length 0        0x0000:  4500 003c 0000 4000 4006 98dd 0a64 468c  E..<..@.@....dF.        0x0010:  0a64 468b 5ba0 98a5 b387 a094 e7e6 179a  .dF.[...........        0x0020:  a012 3750 0f28 0000 0204 0594 0402 080a  ..7P.(..........        0x0030:  5e01 ab71 5e01 b060 0103 0308 0000 0000  ^..q^..`........        0x0040:  0000 0000 0000 0000 0000 0000            ............16:38:58.993310 IP 10.100.70.139.39077 > 10.100.70.140.aequus: Flags [.], ack 1, win 56, options [nop,nop,TS val 1577168993 ecr 1577167729], length 0        0x0000:  4500 0034 a7fa 4000 4006 f0ea 0a64 468b  E..4..@.@....dF.        0x0010:  0a64 468c 98a5 5ba0 e7e6 179a b387 a095  .dF...[.........        0x0020:  8010 0038 a205 0000 0101 080a 5e01 b061  ...8........^..a        0x0030:  5e01 ab71 0000 0000 0000 0000 0000 0000  ^..q............        0x0040:  0000 0000                                ....

       可以看到, 第二次握手的syn/ack包, 确实在1微秒之后才到达。 也就是说, 在1微秒时, 客户端判定超时, 但syn/ack还在长途奔袭, 还在路上。 

 

       此刻, 服务端syn/ack的心情如何?  我替syn/ack包起了那句歌词: 我来到你的城市, 走过你来时的路。 可惜你耐心不够, 没等我达到, 你就先走(超时)哭大哭快哭了

 

       最后三次握手正常建立, 这也在告诉我们, 在连接的时候, 设置的时间不能太短, 否则容易欺骗客户端程序员。 还有个疑问, 为啥没有看到客户端的超时重传呢? 大家可以思考一下。

 

       好,  打开音响, 听听陈奕迅的歌------《好久不见》

 

我来到 你的城市

走过你来时的路
想像着 没我的日子
你是怎样的孤独
拿着你 给的照片
熟悉的那一条街
只是没了你的画面
我们回不到那天
你会不会忽然的出现
在街角的咖啡店
我会带着笑脸 挥手寒暄
和你 坐着聊聊天我多么想和你见一面
看看你最近改变
不再去说从前 只是寒暄
对你说一句 只是说一句
好久不见
拿着你 给的照片
熟悉的那一条街
只是没了你的画面
我们回不到那天
你会不会忽然的出现
在街角的咖啡店
我会带着笑脸 挥手寒暄
和你 坐着聊聊天
我多么想和你见一面
看看你最近改变
不再去说从前 只是寒暄
对你说一句 只是说一句
好久不见

 

 

 

转载地址:http://wnwti.baihongyu.com/

你可能感兴趣的文章
Linux 操作指令收集
查看>>
CentOS 7下卸载MySQL方式(转)
查看>>
CentOS7 安装MySQL 5.6.43
查看>>
使用Java 导入/导出 Excel ----Jakarta POI
查看>>
本地tomcat 服务器内存不足
查看>>
IntelliJ IDAE 2018.2 汉化
查看>>
基于S5PV210的uboot移植中遇到的若干问题记录(一)DM9000网卡移植
查看>>
Openwrt源码下载与编译
查看>>
我和ip_conntrack不得不说的一些事
查看>>
Linux 查看端口使用情况
查看>>
文件隐藏
查看>>
两个linux内核rootkit--之二:adore-ng
查看>>
两个linux内核rootkit--之一:enyelkm
查看>>
关于linux栈的一个深层次的问题
查看>>
rootkit related
查看>>
配置文件的重要性------轻化操作
查看>>
又是缓存惹的祸!!!
查看>>
为什么要实现程序指令和程序数据的分离?
查看>>
我对C++ string和length方法的一个长期误解------从protobuf序列化说起(没处理好会引起数据丢失、反序列化失败哦!)
查看>>
一起来看看protobuf中容易引起bug的一个细节
查看>>