几种TCP连接中出现RST的情况
2016-02-25 23:16
531 查看
在TCP协议中RST表示复位,用来异常的关闭连接,在TCP的设计中它是不可或缺的。发送RST包关闭连接时,不必等缓冲区的包都发出去,直接就丢弃缓存区的包发送RST包。而接收端收到RST包后,也不必发送ACK包来确认。
其实在网络编程过程中,各种RST错误其实是比较难排查和找到原因的。下面我列出几种会出现RST的情况。
服务器程序端口未打开而客户端来连接。这种情况是最为常见和好理解的一种了。去telnet一个未打开的TCP的端口可能会出现这种错误。这个和操作系统的实现有关。在某些情况下,操作系统也会完全不理会这些发到未打开端口请求。
比如在下面这种情况下,主机241向主机114发送一个SYN请求,表示想要连接主机114的40000端口,但是主机114上根本没有打开40000这个端口,于是就向主机241发送了一个RST。这种情况很常见。特别是服务器程序core dump之后重启之前连续出现RST的情况会经常发生。
当然在某些操作系统的主机上,未必是这样的表现。比如向一台WINDOWS7的主机发送一个连接不存在的端口的请求,这台主机就不会回应。
曾经遇到过这样一个情况:一个客户端连接服务器,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拒绝进一步发送数据。
关于TCP,我想我们在教科书里都读到过一句话,'TCP是一种可靠的连接'。 而这可靠有这样一种含义,那就是操作系统接收到的来自TCP连接中的每一个字节,我都会让应用程序接收到。如果应用程序不接收怎么办?你猜对了,RST。
看两段程序:
这一段是server的最简单的代码。逻辑很简单,监听一个TCP端口然后当有客户端来连接的时候fork一个子进程来处理。注意看的是这一段fork里面的处理:
每次只是读socket的前4096个字节,然后就关闭掉连接。
然后再看一下client的代码:
这段代码更简单,就是打开一个socket然后连接一个服务器并发送5000个字节。刚才我们看服务器的代码,每次只接收4096个字节,那么就是说客户端发送的剩下的4个字节服务端的应用程序没有接收到,服务器端的socket就被关闭掉,这种情况下会发生什么状况呢,还是抓包看一看。
前三行就是TCP的3次握手,从第四行开始看,客户端的49660端口向服务器的9877端口发送了5000个字节的数据,然后服务器端发送了一个ACK进行了确认,紧接着服务器向客户端发送了一个RST断开了连接。和我们的预期一致。
如果某个socket已经关闭,但依然收到数据也会产生RST。
代码如下:
客户端:
服务端:
客户端在服务端已经关闭掉socket之后,仍然在发送数据。这时服务端会产生RST。
总结,本文讲了几种TCP连接中出现RST的情况。实际上肯定还有无数种的RST发生,我以后会慢慢收集把更多的例子加入这篇文章。
1 从TCP协议的原理来谈谈RST攻击 http://russelltao.iteye.com/blog/1405349
2 TCP客户-服务器程序例子/article/2012122.html
原文:http://www.360doc.com/content/13/0702/10/1073512_297069771.shtml
其实在网络编程过程中,各种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。看两段程序:
01 | //server.c |
02 |
03 | int main( int argc, char ** argv) |
04 | { |
05 | int listen_fd, real_fd; |
06 | struct sockaddr_in listen_addr, client_addr; |
07 | socklen_t len = sizeof ( struct sockaddr_in); |
08 | listen_fd = socket(AF_INET, SOCK_STREAM, 0); |
09 | if (listen_fd == -1) |
10 | { |
11 | perror ( "socket failed " ); |
12 | return -1; |
13 | } |
14 | bzero(&listen_addr, sizeof (listen_addr)); |
15 | listen_addr.sin_family = AF_INET; |
16 | listen_addr.sin_addr.s_addr |
17 | listen_addr.sin_port |
18 | bind(listen_fd,( struct sockaddr |
19 | listen(listen_fd, |
20 | while (1) |
21 | { |
22 | real_fd = accept(listen_fd, ( struct sockaddr*)&client_addr, |
23 | if (real_fd == -1) |
24 | { |
25 | perror ( "accpet fail " ); |
26 | return -1; |
27 | } |
28 | if (fork() == 0) |
29 | { |
30 | close(listen_fd); |
31 | char pcContent[4096]; |
32 | read(real_fd,pcContent,4096); |
33 | close(real_fd); |
34 | exit (0); |
35 | } |
36 | close(real_fd); |
37 | } |
38 | return 0; |
39 | } |
1 | char pcContent[4096]; |
2 | read(real_fd,pcContent,4096); |
3 | close(real_fd); |
然后再看一下client的代码:
01 | //client.c |
02 | int main( int argc, char ** argv) |
03 | { |
04 | int send_sk; |
05 | struct sockaddr_in s_addr; |
06 | socklen_t len = sizeof (s_addr); |
07 | send_sk |
08 | if (send_sk == -1) |
09 | { |
10 | perror ( "socket failed " ); |
11 | return -1; |
12 | } |
13 | bzero(&s_addr, sizeof (s_addr)); |
14 | s_addr.sin_family = AF_INET; |
15 |
16 | inet_pton(AF_INET,SER_IP,&s_addr.sin_addr); |
17 | s_addr.sin_port |
18 | if (connect(send_sk,( struct sockaddr*)&s_addr,len) == -1) |
19 | { |
20 | perror ( "connect fail " ); |
21 | return -1; |
22 | } |
23 | char pcContent[5000]={0}; |
24 | write(send_sk,pcContent,5000); |
25 | sleep(1); |
26 | close(send_sk); |
27 | } |
前三行就是TCP的3次握手,从第四行开始看,客户端的49660端口向服务器的9877端口发送了5000个字节的数据,然后服务器端发送了一个ACK进行了确认,紧接着服务器向客户端发送了一个RST断开了连接。和我们的预期一致。
4 在一个已关闭的socket上收到数据
如果某个socket已经关闭,但依然收到数据也会产生RST。代码如下:
客户端:
01 | int main( int argc, char ** argv) |
02 | { |
03 | int send_sk; |
04 | struct sockaddr_in s_addr; |
05 | socklen_t len = sizeof (s_addr); |
06 | send_sk |
07 | if (send_sk == -1) |
08 | { |
09 | perror ( "socket failed " ); |
10 | return -1; |
11 | } |
12 | bzero(&s_addr, sizeof (s_addr)); |
13 | s_addr.sin_family = AF_INET; |
14 |
15 | inet_pton(AF_INET,SER_IP,&s_addr.sin_addr); |
16 | s_addr.sin_port |
17 | if (connect(send_sk,( struct sockaddr*)&s_addr,len) == -1) |
18 | { |
19 | perror ( "connect fail " ); |
20 | return -1; |
21 | } |
22 | char pcContent[4096]={0}; |
23 | write(send_sk,pcContent,4096); |
24 | sleep(1); |
25 | write(send_sk,pcContent,4096); |
26 | close(send_sk); |
27 | } |
01 | int main( int argc, char ** argv) |
02 | { |
03 | int listen_fd, real_fd; |
04 | struct sockaddr_in listen_addr, client_addr; |
05 | socklen_t len = sizeof ( struct sockaddr_in); |
06 | listen_fd = socket(AF_INET, SOCK_STREAM, 0); |
07 | if (listen_fd == -1) |
08 | { |
09 | perror ( "socket failed " ); |
10 | return -1; |
11 | } |
12 | bzero(&listen_addr, sizeof (listen_addr)); |
13 | listen_addr.sin_family = AF_INET; |
14 | listen_addr.sin_addr.s_addr = htonl(INADDR_ANY); |
15 | listen_addr.sin_port = htons(SERV_PORT); |
16 | bind(listen_fd,( struct sockaddr *)&listen_addr, len); |
17 | listen(listen_fd, WAIT_COUNT); |
18 | while (1) |
19 | { |
20 | real_fd = accept(listen_fd, ( struct sockaddr*)&client_addr, &len); |
21 | if (real_fd == -1) |
22 | { |
23 | perror ( "accpet fail " ); |
24 | return -1; |
25 | } |
26 | if (fork() == 0) |
27 | { |
28 | close(listen_fd); |
29 | char pcContent[4096]; |
30 | read(real_fd,pcContent,4096); |
31 | close(real_fd); |
32 | exit (0); |
33 | } |
34 | close(real_fd); |
35 | } |
36 | return 0; |
37 | } |
总结
总结,本文讲了几种TCP连接中出现RST的情况。实际上肯定还有无数种的RST发生,我以后会慢慢收集把更多的例子加入这篇文章。
参考文献:
1 从TCP协议的原理来谈谈RST攻击 http://russelltao.iteye.com/blog/14053492 TCP客户-服务器程序例子/article/2012122.html
原文:http://www.360doc.com/content/13/0702/10/1073512_297069771.shtml
相关文章推荐
- http和https的区别
- “一键”知道自己的IP地址和网络供应商
- 网络---大文件的下载(NSURLConnection)
- 网络---大文件的下载(NSURLSession)
- A/V data用TCP来接收(rtp overrtsp),可以收到数据,但解析不对。
- TCP三次握手/四次握手
- 【深度学习介绍系列之二】——深度强化学习:卷积神经网络
- ACM_模板_网络流
- 网络---小文件的下载
- 连接网络设备的交叉线和直通线的区别
- 有上下界的网络流问题
- iOS之HTTP网络编程
- TCP/IP 三次握手,温故知新
- liunx centos5.8 下配置apache+svn的http访问
- 贝叶斯网络--概率推理
- poj 3281 Dining 拆点网络流
- 李望 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-10000290
- HDU1532 - Drainage Ditches(网络流)
- 网络编程_UDP编程原理_发送类型_cs与bs区别JAVA187-188
- HTTP协议