本文共 4103 字,大约阅读时间需要 13 分钟。
先看服务端:
#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(8765); 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;}
启动它。
再来看一个带超时时间的connect函数的客户端程序:
#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;}
服务端在10.100.70.140机器上, 且正在监听8765端口, 我们来看看客户端的log和握手过程(connect函数的超时为1微秒):
xxxxxx$ ./client 10.100.70.140 8765 1connect iRet is -1, errmsg:Operation now in progressconnect time outI am here!
xxxxxx$ sudo tcpdump -iany port 23456 -Xnlps0tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes21:43:40.898224 IP 10.100.70.139.51256 > 10.100.70.140.aequus: Flags [S], seq 948273802, win 14280, options [mss 1428,sackOK,TS val 1560139469 ecr 0,nop,wscale 8], length 0 0x0000: 4500 003c 5447 4000 4006 4496 0a64 468b E..10.100.70.139.51256: Flags [S.], seq 802450158, ack 948273803, win 14160, options [mss 1428,sackOK,TS val 1560138227 ecr 1560139469,nop,wscale 8], length 0 0x0000: 4500 003c 0000 4000 4006 98dd 0a64 468c E..<..@.@....dF. 0x0010: 0a64 468b 5ba0 c838 2fd4 6aee 3885 828b .dF.[..8/.j.8... 0x0020: a012 3750 9277 0000 0204 0594 0402 080a ..7P.w.......... 0x0030: 5cfd d1f3 5cfd d6cd 0103 0308 0000 0000 \...\........... 0x0040: 0000 0000 0000 0000 0000 0000 ............21:43:40.898450 IP 10.100.70.139.51256 > 10.100.70.140.aequus: Flags [R], seq 948273803, win 0, length 0 0x0000: 4500 0028 04e5 4000 4006 940c 0a64 468b E..(..@.@....dF. 0x0010: 0a64 468c c838 5ba0 3885 828b 0000 0000 .dF..8[.8....... 0x0020: 5004 0000 2f18 0000 0000 0000 0000 0000 P.../........... 0x0030: 0000 0000 0000 0000 ........
看到RST了。
分析下, syn/ack包是在1微秒之后到达的, 所以客户端的select会超时, 代码走入超时逻辑, 等syn/ack到达时, 客户端代码中的超时逻辑中, 执行了close socket的操作。 于是可以这么认为, 经历两次握手后, 客户端调用close socket, 此时发送了RST.
OK, 不多说。
转载地址:http://gjwti.baihongyu.com/