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。

看两段程序:

 #include<sys/socket.h>
#include<errno.h>
#include<netinet/in.h>
#include<string.h>
#include<stdio.h> #define SERV_PORT 60000
#define WAIT_COUNT 10 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, ); if(listen_fd == -) { perror("socket failed "); return -; } bzero(&listen_addr,sizeof(listen_addr)); listen_addr.sin_family = AF_INET; // listen_addr.sin_addr.s_addr = inet_addr("10.208.170.9");
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() { real_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &len); if(real_fd == -) { perror("accpet fail "); return -; } if(fork() == ) { close(listen_fd); char pcContent[]; read(real_fd,pcContent,); close(real_fd); exit(); } close(real_fd); } return ; }
 

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

         if(fork() == )  

         {  

             close(listen_fd);  

             char pcContent[]; 

             read(real_fd,pcContent,); 

             close(real_fd);  

             exit();              

         } 

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

然后再看一下client的代码:

 #include<sys/socket.h>
#include<errno.h>
#include<netinet/in.h>
#include<string.h>
#include<stdio.h> #define BUF_LEN 1028
#define SERV_PORT 60000
#define SERV_IP "10.208.170.9"
//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, ); if(send_sk == -) {
#include<sys/socket.h>
#include<errno.h>
#include<netinet/in.h>
#include<string.h>
#include<stdio.h> #define BUF_LEN 1028
#define SERV_PORT 60000
#define SERV_IP "10.208.170.9"
//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, ); if(send_sk == -) { perror("socket failed "); return -; } bzero(&s_addr, sizeof(s_addr)); s_addr.sin_family = AF_INET; inet_pton(AF_INET,SERV_IP,&s_addr.sin_addr); s_addr.sin_port = htons(SERV_PORT); if(connect(send_sk,(struct sockaddr*)&s_addr,len) == -) { perror("connect fail "); return -; } char pcContent[]={,,,}; write(send_sk,pcContent,); sleep(); close(send_sk); }
这段代码更简单,就是打开一个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。

代码如下:

客户端:

 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, );   

     if(send_sk == -)   

     {   

         perror("socket failed  ");   

         return -;   

     }   

     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) == -)   

     {   

         perror("connect fail  ");   

         return -;   

     }   

     char pcContent[]={}; 

     write(send_sk,pcContent,); 

     sleep(); 

     write(send_sk,pcContent,); 

     close(send_sk); 

 }  

服务器:

 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, ); if(listen_fd == -) { perror("socket failed "); return -; } 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() { real_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &len); if(real_fd == -) { perror("accpet fail "); return -; } if(fork() == ) { close(listen_fd); char pcContent[]; read(real_fd,pcContent,); close(real_fd); exit(); } close(real_fd); } return ; }
客户端在服务端已经关闭掉socket之后,仍然在发送数据。这时服务端会产生RST。

总结

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

参考文献:

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

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

TCP 中出现RST的情况的更多相关文章

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

    TCP RST 网络 linux 目录[-] 1 端口未打开 2 请求超时 3 提前关闭 4 在一个已关闭的socket上收到数据 总结 参考文献: 应该没有人会质疑,现在是一个网络时代了.应该不少程 ...

  2. TCP中的RST复位信号

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

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

    http://blog.chinaunix.net/uid-24517549-id-3991141.html http://blog.chinaunix.net/uid-24517549-id-399 ...

  4. TCP中的RST标志(Reset)详解

    在谈RST攻击前,必须先了解TCP:如何通过三次握手建立TCP连接.四次握手怎样把全双工的连接关闭掉.滑动窗口是怎么传输数据的.TCP的flag标志位里RST在哪些情况下出现.下面我会画一些尽量简化的 ...

  5. TCP中异常关闭的情况记录

    1.当TCP连接的对端进程已经关闭了Socket的情况下,本端进程再发送数据时,第一包可以发送成功(但会导致对端发送一个RST包过来):之后如果再继续发送数据会失败,错误码为“10053: An es ...

  6. 转 tcp协议里rst字段讲解

    TCP协议的原理来谈谈rst复位攻击 http://russelltao.iteye.com/blog/1405349 几种TCP连接中出现RST的情况 https://blog.csdn.net/c ...

  7. /proc/net/tcp中各项参数说明

    /proc/net/tcp中的内容由tcp4_seq_show()函数打印,该函数中有三种打印形式,我们这里这只列出状态是TCP_SEQ_STATE_LISTENING或TCP_SEQ_STATE_E ...

  8. TCP/IP源码(59)——TCP中的三个接收队列

    http://blog.chinaunix.net/uid-23629988-id-3482647.html TCP/IP源码(59)——TCP中的三个接收队列  作者:gfree.wind@gmai ...

  9. 三十天学不会TCP,UDP/IP网络编程 -- TCP中的智慧之连续ARQ

    突然发现上一篇文章贴图有问题,关键我怎么调也调不好,为了表达歉意,我再贴一篇gitbook上的吧,虽然违背了我自己的隔一篇在这里发一次的潜规则~其余完整版可以去gitbook(https://www. ...

随机推荐

  1. 【插头DP】BZOJ1187- [HNOI2007]神奇游乐园

    [题目大意] 在n*m的网格中选一条回路,使权值和最大. [思路] 和之前裸的插头DP差不多,只不过现在回路不需要经过所有的格子.所以有以下几个注意点(具体看注释): (1)left和up插头相等的时 ...

  2. hdu 3586 树形dp+二分

    题目大意:给定n个敌方据点,1为司令部,其他点各有一条边相连构成一棵 树,每条边都有一个权值cost表示破坏这条边的费用,叶子节点为前线.现要切断前线和司令部的联系,每次切断边的费用不能超过上限lim ...

  3. Ubuntu 无线驱动问题

    最近一次更新系统后开机发现电脑只能连有线网络连不上无线网络.我平时都是连无线网络,没有买网线.所以查了下无线驱动相关问题资料,发现是由于更新系统造成无线网卡驱动莫名不见了.刚开始还以为是电脑将无线禁用 ...

  4. 25.最小生成树(kruskal算法)

    时间限制: 1 s 空间限制: 128000 KB 题目等级 : 白银 Silver 题解 查看运行结果 题目描述 Description 农民约翰被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立 ...

  5. noip200806火柴棒等式

    试题描述: 给你n根火柴棍,你可以拼出多少个形如“A+B=C”的等式?等式中的A.B.C是用火柴棍拼出的整数(若该数非零,则最高位不能是0).用火柴棍拼数字0-9的拼法如图所示: 注意: 1)加号与等 ...

  6. hihocoder1322 树结构判定(161周)

    hihocoder1322 : 树结构判定(161周) 题目链接 思路: 无向图中判断是不是一棵树. 并查集判断.判断是不是只有一个连通分量.并且该联通分量中没有环.没有环的判定很简单就是看边的数目和 ...

  7. 典型案例收集-使用OpenVPN连通多个机房内网(iptables+静态路由)

    说明: 1.这个方案是我最初实现的方案,目的在于OpenVPN连通后使其能访问各自的子网,实现互通. 2.主要以iptables为主,这个是关键点,并且这种方式配置iptables十分复杂,最后加入了 ...

  8. Papilio Pro Boards

    http://papilio.cc/index.php?n=Papilio.PapilioOne The Papilio is an Open Source FPGA development boar ...

  9. python笔记23-unittest单元测试之mock

    什么是mock unittest.mock是一个用于在Python中进行单元测试的库,Mock翻译过来就是模拟的意思,顾名思义这个库的主要功能是模拟一些东西. 它的主要功能是使用mock对象替代掉指定 ...

  10. Latex作者单位的写法—AND 首页脚注

    IEEE会议的模板 以四个作者为例 正常: 作者单位如果名字较短,可以直接写在作者对应的下面,邮箱可以对应写在再接下来的下面. 一 如果邮箱较长,可以用\thanks{ }命令将其变为脚注.例如: ~ ...