tcp连接超时, 那么可能会出现两次握手后, 客户端发RST包

来源:互联网 发布:php为什么要用到apache 编辑:程序博客网 时间:2024/06/03 19:38

        先看服务端:

#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netdb.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <errno.h>#include <malloc.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/ioctl.h>#include <stdarg.h>#include <fcntl.h>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 <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netdb.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <errno.h>#include <malloc.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/ioctl.h>#include <stdarg.h>#include <fcntl.h>#include <time.h>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..<TG@.@.D..dF.        0x0010:  0a64 468c c838 5ba0 3885 828a 0000 0000  .dF..8[.8.......        0x0020:  a002 37c8 a20d 0000 0204 0594 0402 080a  ..7.............        0x0030:  5cfd d6cd 0000 0000 0103 0308 0000 0000  \...............        0x0040:  0000 0000 0000 0000 0000 0000            ............21:43:40.898426 IP 10.100.70.140.aequus > 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,  不多说。


原创粉丝点击