几种TCP连接中出现RST的情况(转载)

TCP RST 网络 linux

目录[-]

  • 1 端口未打开
  • 2 请求超时
  • 3 提前关闭
  • 4 在一个已关闭的socket上收到数据
  • 总结
  • 参考文献:
  • 应该没有人会质疑,现在是一个网络时代了。应该不少程序员在编程中需要考虑多机、局域网、广域网的各种问题。所以网络知识也是避免不了学习的。而且笔者一直觉得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

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    //server.c

    int main(int argc, char** argv) 

        int listen_fd, real_fd; 

        struct sockaddr_in listen_addr, client_addr; 

        socklen_t len = sizeof(struct sockaddr_in); 

        listen_fd = socket(AF_INET, SOCK_STREAM, 0); 

        if(listen_fd == -1) 

        

            perror("socket failed   "); 

            return -1; 

        

        bzero(&listen_addr,sizeof(listen_addr)); 

        listen_addr.sin_family = AF_INET; 

        listen_addr.sin_addr.s_addr = htonl(INADDR_ANY); 

        listen_addr.sin_port = htons(SERV_PORT); 

        bind(listen_fd,(struct sockaddr *)&listen_addr, len); 

        listen(listen_fd, WAIT_COUNT); 

        while(1) 

        

            real_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &len); 

            if(real_fd == -1) 

            

                perror("accpet fail  "); 

                return -1; 

            

            if(fork() == 0) 

            

                close(listen_fd); 

                char pcContent[4096];

                read(real_fd,pcContent,4096);

                close(real_fd); 

                exit(0);             

            

            close(real_fd); 

        }    

        return 0; 

    }

    这一段是server的最简单的代码。逻辑很简单,监听一个TCP端口然后当有客户端来连接的时候fork一个子进程来处理。注意看的是这一段fork里面的处理:

    ?


    1

    2

    3

    char pcContent[4096];

    read(real_fd,pcContent,4096);

    close(real_fd);

    每次只是读socket的前4096个字节,然后就关闭掉连接。

    然后再看一下client的代码:

    ?


    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    //client.c

    int main(int argc, char** argv) 

        int send_sk; 

        struct sockaddr_in s_addr; 

        socklen_t len = sizeof(s_addr); 

        send_sk = socket(AF_INET, SOCK_STREAM, 0); 

        if(send_sk == -1) 

        

            perror("socket failed  "); 

            return -1; 

        

        bzero(&s_addr, sizeof(s_addr)); 

        s_addr.sin_family = AF_INET; 

        inet_pton(AF_INET,SER_IP,&s_addr.sin_addr); 

        s_addr.sin_port = htons(SER_PORT); 

        if(connect(send_sk,(struct sockaddr*)&s_addr,len) == -1) 

        

            perror("connect fail  "); 

            return -1; 

        

        char pcContent[5000]={0};

        write(send_sk,pcContent,5000);

        sleep(1);

        close(send_sk);

    }

    这段代码更简单,就是打开一个socket然后连接一个服务器并发送5000个字节。刚才我们看服务器的代码,每次只接收4096个字节,那么就是说客户端发送的剩下的4个字节服务端的应用程序没有接收到,服务器端的socket就被关闭掉,这种情况下会发生什么状况呢,还是抓包看一看。

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

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

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

    代码如下:

    客户端:

    ?


    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    int main(int argc, char** argv)  

    {  

        int send_sk;  

        struct sockaddr_in s_addr;  

        socklen_t len = sizeof(s_addr);  

        send_sk = socket(AF_INET, SOCK_STREAM, 0);  

        if(send_sk == -1)  

        {  

            perror("socket failed  ");  

            return -1;  

        }  

        bzero(&s_addr, sizeof(s_addr));  

        s_addr.sin_family = AF_INET;  

        inet_pton(AF_INET,SER_IP,&s_addr.sin_addr);  

        s_addr.sin_port = htons(SER_PORT);  

        if(connect(send_sk,(struct sockaddr*)&s_addr,len) == -1)  

        {  

            perror("connect fail  ");  

            return -1;  

        }  

        char pcContent[4096]={0};

        write(send_sk,pcContent,4096);

        sleep(1);

        write(send_sk,pcContent,4096);

        close(send_sk);

    服务端:

    ?


    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    int main(int argc, char** argv)  

    {  

        int listen_fd, real_fd;  

        struct sockaddr_in listen_addr, client_addr;  

        socklen_t len = sizeof(struct sockaddr_in);  

        listen_fd = socket(AF_INET, SOCK_STREAM, 0);  

        if(listen_fd == -1)  

        {  

            perror("socket failed   ");  

            return -1;  

        }  

        bzero(&listen_addr,sizeof(listen_addr));  

        listen_addr.sin_family = AF_INET;  

        listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);  

        listen_addr.sin_port = htons(SERV_PORT);  

        bind(listen_fd,(struct sockaddr *)&listen_addr, len);  

        listen(listen_fd, WAIT_COUNT);  

        while(1)  

        {  

            real_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &len);  

            if(real_fd == -1)  

            {  

                perror("accpet fail  ");  

                return -1;  

            }  

            if(fork() == 0)  

            {  

                close(listen_fd);  

                char pcContent[4096];

                read(real_fd,pcContent,4096);

                close(real_fd);  

                exit(0);              

            }  

            close(real_fd);  

        }     

        return 0;  

    客户端在服务端已经关闭掉socket之后,仍然在发送数据。这时服务端会产生RST。

    总结

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

    参考文献:

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

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

    时间: 2024-10-29 19:07:35

    几种TCP连接中出现RST的情况(转载)的相关文章

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

    http://my.oschina.net/costaxu/blog/127394 在TCP协议中RST表示复位,用来异常的关闭连接,在TCP的设计中它是不可或缺的.发送RST包关闭连接时,不必等缓冲区的包都发出去,直接就丢弃缓存区的包发送RST包.而接收端收到RST包后,也不必发送ACK包来确认. 其实在网络编程过程中,各种RST错误其实是比较难排查和找到原因的.下面我列出几种会出现RST的情况. 1 端口未打开 服务器程序端口未打开而客户端来连接.这种情况是最为常见和好理解的一种了.去tel

    【NodeJs】Ctrl+C在Linux平台和Windows平台下的TCP连接中的不同表现

    Linux平台:CentOS release 6.5 (Final) Windows平台:Windows 7 旗舰版 服务器端代码如下: var net = require('net'); var server = net.createServer(function(c){ console.log('client connected: ' + c.remoteAddress); c.setNoDelay(true); c.on('data', function(data){ console.lo

    TCP连接:SYN ACK RST UTG PSH FIN

    出处http://bbs.csdn.net/topics/370058169 8楼 TCP连接:SYN ACK RST UTG PSH FIN 三次握手:发送端发送一个SYN=1,ACK=0标志的数据包给接收端,请求进行连接,这是第一次握手:接收端收到请求并且允许连接的话,就会发送一个SYN=1,ACK=1标志的数据包给发送端,告诉它,可以通讯了,并且让发送端发送一个确认数据包,这是第二次握手:最后,发送端发送一个SYN=0,ACK=1的数据包给接收端,告诉它连接已被确认,这就是第三次握手.之后

    TCP连接中time_wait在开发中的影响-搜人以鱼不如授之以渔

    根据TCP协议定义的3次握手断开连接规定,发起socket主动关闭的一方socket将进入TIME_WAIT状态,TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),TIME_WAIT状态下的socket不能被回收使用. 具体现象是对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT状态的socket, 甚至比处于Established状态下的socket多的多,严重影响服务器的处理能力,甚至耗尽可用的

    TCP/IP详解--TCP连接中TIME_WAIT状态过多

    转载自http://blog.csdn.net/yusiguyuan/article/details/21445883 TIMEWAIT状态本身和应用层的客户端或者服务器是没有关系的.仅仅是主动关闭的一方,在使用FIN|ACK|FIN|ACK四分组正常关闭TCP连接的时候会出现这个TIMEWAIT.服务器在处理客户端请求的时候,如果你的程序设计为服务器主动关闭,那么你才有可能需要关注这个TIMEWAIT状态过多的问题.如果你的服务器设计为被动关闭,那么你首先要关注的是CLOSE_WAIT. 原则

    如何在socket编程的Tcp连接中实现心跳协议

    心跳包的发送,通常有两种技术 方法1:应用层自己实现的心跳包 由应用程序自己发送心跳包来检测连接是否正常,大致的方法是:服务器在一个 Timer事件中定时 向客户端发送一个短小精悍的数据包,然后启动一个低级别的线程,在该线程中不断检测客户端的回应, 如果在一定时间内没有收到客户端的回应,即认为客户端已经掉线:同样,如果客户端在一定时间内没 有收到服务器的心跳包,则认为连接不可用. 方法2:TCP的KeepAlive保活机制 因为要考虑到一个服务器通常会连接多个客户端,因此由用户在应用层自己实现心

    TCP 连接中的TIME_WAIT

    原文:http://blog.csdn.net/wangpengqi/article/details/17245349 这就有个细节,一次http请求,谁会先断开TCP连接?什么情况下客户端先断,什么情况下服务端先断? 百度后,找到原因,主要有http1.0和http1.1之间保持连接的差异以及http头中connection.content-length.Transfer-encoding等参数有关: 当然,在nginx中,对于http1.0与http1.1也是支持长连接的.什么是长连接呢?我

    TCP连接中的TIME_WAIT状态

    转自:http://blog.csdn.net/sunnydogzhou/article/details/6572071 1 TCP关闭时的四次握手Tcp连接在关闭的的时候,执行的是一个四次握手的过程,下图是客户端发起的关闭时客户端和服务器的状态转换图 具体过程如下:1. 客户端发送FIN报文段,进入FIN_WAIT_1状态.2. 服务器端收到FIN报文段,发送ACK表示确认,进入CLOSE_WAIT状态.3. 客户端收到FIN的确认报文段,进入FIN_WAIT_2状态.4. 服务器端发送FIN

    WPF中使用定时器 DispatcherTimer 做TCP连接中的心跳 HeartBeat

    开发过程中经常遇到定时触发的需求,如:TCP/IP连接中,使用心跳包保持连接或检测连接是否已经中断. WPF中有多种定时器: 1.using System.Windows.Threading; 代码如下: using System.Windows.Threading; public partial class MainWindow : Window { DispatcherTimer timerHeartBeat = new DispatcherTimer(); public MainWindo