zoukankan      html  css  js  c++  java
  • TCP 中出现RST的情况

    http://www.360doc.com/content/13/0702/10/1073512_297069771.shtml

    原 几种TCP连接中出现RST的情况

    发表于1年前(2013-05-04 11:40)   阅读(9145) | 评论(4
    22人收藏此文章, 我要收藏
    2

     

    应该没有人会质疑,现在是一个网络时代了。应该不少程序员在编程中需要考虑多机、局域网、广域网的各种问题。所以网络知识也是避免不了学习的。而且笔者一直觉得TCP/IP网络知识在一个程序员知识体系中必需占有一席之地的。

    在TCP协议中RST表示复位,用来异常的关闭连接,在TCP的设计中它是不可或缺的。发送RST包关闭连接时,不必等缓冲区的包都发出去,直接就丢弃缓存区的包发送RST包。而接收端收到RST包后,也不必发送ACK包来确认。

    其实在网络编程过程中,各种RST错误其实是比较难排查和找到原因的。下面我列出几种会出现RST的情况。

    1 端口未打开

    服务器程序端口未打开而客户端来连接。这种情况是最为常见和好理解的一种了。去telnet一个未打开的TCP的端口可能会出现这种错误。这个和操作系统的实现有关。在某些情况下,操作系统也会完全不理会这些发到未打开端口请求。

    比如在下面这种情况下,主机241向主机114发送一个SYN请求,表示想要连接主机114的40000端口,但是主机114上根本没有打开40000这个端口,于是就向主机241发送了一个RST。这种情况很常见。特别是服务器程序core dump之后重启之前连续出现RST的情况会经常发生。

    当然在某些操作系统的主机上,未必是这样的表现。比如向一台WINDOWS7的主机发送一个连接不存在的端口的请求,这台主机就不会回应。

    2 请求超时

    曾经遇到过这样一个情况:一个客户端连接服务器,connect返回-1并且error=EINPROGRESS。 直接telnet发现网络连接没有问题。ping没有出现丢包。用抓包工具查看,客户端是在收到服务器发出的SYN之后就莫名其妙的发送了RST。

    比如像下面这样:

    有89、27两台主机。主机89向主机27发送了一个SYN,表示希望连接8888端口,主机27回应了主机89一个SYN表示可以连接。但是主机27却很不友好,莫名其妙的发送了一个RST表示我不想连接你了。

    后来经过排查发现,在主机89上的程序在建立了socket之后,用setsockopt的SO_RCVTIMEO选项设置了recv的超时时间为100ms。而我们看上面的抓包结果表示,从主机89发出SYN到接收SYN的时间多达110ms。(从15:01:27.799961到15:01:27.961886, 小数点之后的单位是微秒)。因此主机89上的程序认为接收超时,所以发送了RST拒绝进一步发送数据。

    3 提前关闭

    关于TCP,我想我们在教科书里都读到过一句话,'TCP是一种可靠的连接'。 而这可靠有这样一种含义,那就是操作系统接收到的来自TCP连接中的每一个字节,我都会让应用程序接收到。如果应用程序不接收怎么办?你猜对了,RST。

    看两段程序:

     1 #include<sys/socket.h>
     2 #include<errno.h>
     3 #include<netinet/in.h>
     4 #include<string.h>
     5 #include<stdio.h>
     6 
     7 #define SERV_PORT  60000
     8 #define WAIT_COUNT 10
     9 
    10 int main(int argc, char** argv)
    11 
    12 {
    13 
    14     int listen_fd, real_fd;
    15 
    16     struct sockaddr_in listen_addr, client_addr;
    17 
    18     socklen_t len = sizeof(struct sockaddr_in);
    19 
    20     listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    21 
    22     if(listen_fd == -1)
    23 
    24     {
    25 
    26         perror("socket failed   ");
    27 
    28         return -1;
    29 
    30     }
    31 
    32     bzero(&listen_addr,sizeof(listen_addr));
    33 
    34     listen_addr.sin_family = AF_INET;
    35 
    36 //    listen_addr.sin_addr.s_addr = inet_addr("10.208.170.9");
    37     listen_addr.sin_addr.s_addr = htonl (INADDR_ANY);
    38 
    39 
    40     listen_addr.sin_port = htons(SERV_PORT);
    41 
    42     bind(listen_fd,(struct sockaddr *)&listen_addr, len);
    43 
    44     listen(listen_fd, WAIT_COUNT);
    45 
    46     while(1)
    47 
    48     {
    49 
    50         real_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &len);
    51 
    52         if(real_fd == -1)
    53 
    54         {
    55 
    56             perror("accpet fail  ");
    57 
    58             return -1;
    59 
    60         }
    61 
    62         if(fork() == 0)
    63 
    64         {
    65 
    66             close(listen_fd);
    67 
    68             char pcContent[4096];
    69 
    70             read(real_fd,pcContent,4096);
    71 
    72             close(real_fd);
    73 
    74             exit(0);
    75 
    76         }
    77 
    78         close(real_fd);
    79 
    80     }
    81 
    82     return 0;
    83 
    84 }
     
    这一段是server的最简单的代码。逻辑很简单,监听一个TCP端口然后当有客户端来连接的时候fork一个子进程来处理。注意看的是这一段fork里面的处理:
     1         if(fork() == 0)  
     2 
     3         {  
     4 
     5             close(listen_fd);  
     6 
     7             char pcContent[4096]; 
     8 
     9             read(real_fd,pcContent,4096); 
    10 
    11             close(real_fd);  
    12 
    13             exit(0);              
    14 
    15         } 
    每次只是读socket的前4096个字节,然后就关闭掉连接。

    然后再看一下client的代码:

     1 #include<sys/socket.h>  
     2 #include<errno.h>  
     3 #include<netinet/in.h>  
     4 #include<string.h>  
     5 #include<stdio.h>  
     6 
     7 #define BUF_LEN 1028  
     8 #define SERV_PORT 60000 
     9 #define SERV_IP "10.208.170.9"
    10 //client.c 
    11 
    12 int main(int argc, char** argv)  
    13 
    14 {  
    15 
    16     int send_sk;  
    17 
    18     struct sockaddr_in s_addr;  
    19 
    20     socklen_t len = sizeof(s_addr);  
    21 
    22     send_sk = socket(AF_INET, SOCK_STREAM, 0);  
    23 
    24     if(send_sk == -1)  
    25 
    26     {   
    27 #include<sys/socket.h>
    28 #include<errno.h>
    29 #include<netinet/in.h>
    30 #include<string.h>
    31 #include<stdio.h>
    32 
    33 #define BUF_LEN 1028  
    34 #define SERV_PORT 60000 
    35 #define SERV_IP "10.208.170.9"
    36 //client.c 
    37 
    38 int main(int argc, char** argv)
    39 
    40 {
    41 
    42     int send_sk;
    43 
    44     struct sockaddr_in s_addr;
    45 
    46     socklen_t len = sizeof(s_addr);
    47 
    48     send_sk = socket(AF_INET, SOCK_STREAM, 0);
    49 
    50     if(send_sk == -1)
    51 
    52     {
    53 
    54         perror("socket failed  ");  
    55 
    56         return -1;  
    57 
    58     }   
    59 
    60     bzero(&s_addr, sizeof(s_addr));  
    61 
    62     s_addr.sin_family = AF_INET;  
    63 
    64 
    65 
    66     inet_pton(AF_INET,SERV_IP,&s_addr.sin_addr);
    67 
    68     s_addr.sin_port = htons(SERV_PORT);
    69 
    70     if(connect(send_sk,(struct sockaddr*)&s_addr,len) == -1)
    71 
    72     {
    73 
    74         perror("connect fail  ");
    75 
    76         return -1;
    77 
    78     }
    79 
    80     char pcContent[5000]={7,7,8,8};
    81 
    82     write(send_sk,pcContent,5000);
    83 
    84     sleep(1);
    85 
    86     close(send_sk);
    87 
    88 }
    这段代码更简单,就是打开一个socket然后连接一个服务器并发送5000个字节。刚才我们看服务器的代码,每次只接收4096个字节,那么就是说客户端发送的剩下的4个字节服务端的应用程序没有接收到,服务器端的socket就被关闭掉,这种情况下会发生什么状况呢,还是抓包看一看。
     
    我自己抓的
     
    -bash-3.2# tcpdump -i eth0 port 60000
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
    22:55:13.816982 IP 10.208.170.10.55914 > 10.208.170.9.60000: S 1890505779:1890505779(0) win 5840 <mss 1460,sackOK,timestamp 3653881104 0,nop,wscale 7>
    22:55:13.821485 IP 10.208.170.9.60000 > 10.208.170.10.55914: S 3237333984:3237333984(0) ack 1890505780 win 5792 <mss 1460,sackOK,timestamp 1499086014 3653881104,nop,wscale 6>
    22:55:13.817360 IP 10.208.170.10.55914 > 10.208.170.9.60000: . ack 1 win 46 <nop,nop,timestamp 3653881105 1499086014>
    22:55:13.817362 IP 10.208.170.10.55914 > 10.208.170.9.60000: . 1:2897(2896) ack 1 win 46 <nop,nop,timestamp 3653881105 1499086014>
    22:55:13.817398 IP 10.208.170.9.60000 > 10.208.170.10.55914: . ack 2897 win 136 <nop,nop,timestamp 1499086014 3653881105>
    22:55:13.817657 IP 10.208.170.10.55914 > 10.208.170.9.60000: . 2897:4345(1448) ack 1 win 46 <nop,nop,timestamp 3653881105 1499086014>
    22:55:13.817673 IP 10.208.170.9.60000 > 10.208.170.10.55914: . ack 4345 win 181 <nop,nop,timestamp 1499086015 3653881105>
    22:55:13.817658 IP 10.208.170.10.55914 > 10.208.170.9.60000: P 4345:5001(656) ack 1 win 46 <nop,nop,timestamp 3653881105 1499086014>
    22:55:13.817694 IP 10.208.170.9.60000 > 10.208.170.10.55914: . ack 5001 win 272 <nop,nop,timestamp 1499086015 3653881105>
    22:55:13.817845 IP 10.208.170.9.60000 > 10.208.170.10.55914: R 1:1(0) ack 5001 win 272 <nop,nop,timestamp 1499086015 3653881105>

    前三行就是TCP的3次握手,从第四行开始看,客户端的49660端口向服务器的9877端口发送了5000个字节的数据,然后服务器端发送了一个ACK进行了确认,紧接着服务器向客户端发送了一个RST断开了连接。和我们的预期一致。

    4 在一个已关闭的socket上收到数据

    如果某个socket已经关闭,但依然收到数据也会产生RST。

    代码如下:

    客户端:

     1 int main(int argc, char** argv)   
     2 
     3 {   
     4 
     5     int send_sk;   
     6 
     7     struct sockaddr_in s_addr;   
     8 
     9     socklen_t len = sizeof(s_addr);   
    10 
    11     send_sk = socket(AF_INET, SOCK_STREAM, 0);   
    12 
    13     if(send_sk == -1)   
    14 
    15     {   
    16 
    17         perror("socket failed  ");   
    18 
    19         return -1;   
    20 
    21     }   
    22 
    23     bzero(&s_addr, sizeof(s_addr));   
    24 
    25     s_addr.sin_family = AF_INET;   
    26 
    27   
    28 
    29     inet_pton(AF_INET,SER_IP,&s_addr.sin_addr);   
    30 
    31     s_addr.sin_port = htons(SER_PORT);   
    32 
    33     if(connect(send_sk,(struct sockaddr*)&s_addr,len) == -1)   
    34 
    35     {   
    36 
    37         perror("connect fail  ");   
    38 
    39         return -1;   
    40 
    41     }   
    42 
    43     char pcContent[4096]={0}; 
    44 
    45     write(send_sk,pcContent,4096); 
    46 
    47     sleep(1); 
    48 
    49     write(send_sk,pcContent,4096); 
    50 
    51     close(send_sk); 
    52 
    53 }  

    服务器:

     1 int main(int argc, char** argv)   
     2 {   
     3 
     4     int listen_fd, real_fd;   
     5 
     6     struct sockaddr_in listen_addr, client_addr;   
     7 
     8     socklen_t len = sizeof(struct sockaddr_in);   
     9 
    10     listen_fd = socket(AF_INET, SOCK_STREAM, 0);   
    11 
    12     if(listen_fd == -1)   
    13 
    14     {   
    15 
    16         perror("socket failed   ");   
    17 
    18         return -1;   
    19 
    20     }   
    21 
    22     bzero(&listen_addr,sizeof(listen_addr));   
    23 
    24     listen_addr.sin_family = AF_INET;   
    25 
    26     listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);   
    27 
    28     listen_addr.sin_port = htons(SERV_PORT);   
    29 
    30     bind(listen_fd,(struct sockaddr *)&listen_addr, len);   
    31 
    32     listen(listen_fd, WAIT_COUNT);   
    33 
    34     while(1)   
    35 
    36     {   
    37 
    38         real_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &len);   
    39 
    40         if(real_fd == -1)   
    41 
    42         {   
    43 
    44             perror("accpet fail  ");   
    45 
    46             return -1;   
    47 
    48         }   
    49 
    50         if(fork() == 0)   
    51 
    52         {   
    53 
    54             close(listen_fd);   
    55 
    56             char pcContent[4096]; 
    57 
    58             read(real_fd,pcContent,4096); 
    59 
    60             close(real_fd);   
    61 
    62             exit(0);               
    63 
    64         }   
    65 
    66         close(real_fd);   
    67 
    68     }      
    69 
    70     return 0;   
    71 
    72 }  
    客户端在服务端已经关闭掉socket之后,仍然在发送数据。这时服务端会产生RST。

    总结

    总结,本文讲了几种TCP连接中出现RST的情况。实际上肯定还有无数种的RST发生,我以后会慢慢收集把更多的例子加入这篇文章。

    参考文献:

    1 从TCP协议的原理来谈谈RST攻击 http://russelltao.iteye.com/blog/1405349

    2 TCP客户-服务器程序例子http://blog.csdn.net/youkuxiaobin/article/details/6917880

  • 相关阅读:
    如何在android studio上加入OpenCV库
    c++ overload 、override、overwrite
    学习笔记:linux之文件空洞
    windows 编译安装PROJ.4
    RDD:基于内存的集群计算容错抽象
    用Scala语言轻松开发多线程、分布式以及集群式程序
    scala 读雷达数据文件,生成png
    linux下virtualBox挂载物理磁盘,启动第二块硬盘中的系统
    shell检查网络出现异常、僵尸进程、内存过低后,自动重启
    cordova 5.0版本说明
  • 原文地址:https://www.cnblogs.com/diegodu/p/3951243.html
Copyright © 2011-2022 走看看