發一個包,RST 了-

前言

大家好,我是盼盼!

有一次工作的時候,突然遇到一個網絡編程的問題,發一個包,對端直接返回 RST 了,感覺莫名奇妙。

學習得知,RST 是 TCP 中的一個標誌,經常抓包的同學應該都會遇到。弄懂 RST 標誌對學習網絡編程很重要。

RST 爲重置報文段,它會導致 TCP 連接的快速拆遷,且不需要 ack 進行確認。

出現 RST 的集中情況

1、端口未打開,C 向 S 發送 SYN,去連接 S 的端口 9820,但是 S 沒有打開 9820 端口,這個時候 S 發送 RST

2、請求超時,C 向 S 發送 SYN,S 回覆 ACK+SYN,如果 C 從發送 SYN 到收到 S 的 ACK+SYN,時間過長,認爲超時,C 發送 RST,表示拒絕進一步發送數據。

3、提前關閉連接,S 端 tcp 協議收到的數據,應用程序沒有接收,S 發送 RST,提前關閉連接。

4、S 端 socket 調用 close 不願再接收數據,但是還是從 C 收到數據,發送 RST

tcpdump 看 RST

  1. 針對不存在的端口的連請求

客戶端:

#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 <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <netinet/tcp.h>
#define MAXLINE 4096
int main()
{
   int sockfd,ret;
   struct sockaddr_in servaddr;
   char sendbuf[32740]={0};
   sockfd=socket(AF_INET,SOCK_STREAM,0);
   bzero(&servaddr,sizeof(servaddr));
   servaddr.sin_family=AF_INET;
   servaddr.sin_port=htons(8888);
   servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
   ret=connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
   printf("ret=%d\n",ret);
   write(sockfd,sendbuf,sizeof(sendbuf)+1);
   getchar();
   close(sockfd);
   return 0;
}

編譯並運行,此時沒有服務端在 8888 端口進行監聽,tcpdump 抓包看。

07:19:32.643476 IP 127.0.0.1.49028 > 127.0.0.1.ddi-tcp-1: Flags [S], seq 1270070893, win 65495, options [mss 65495,sackOK,TS val 3883769366 ecr 0,nop,wscale 7], length 0
07:19:32.643491 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.49028: Flags [R.], seq 0, ack 1270070894, win 0, length 0

客戶端發起連接,但受到一個 RST 包。

  1. 提前關閉

服務端:

#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 <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#define MAXLINE 4096
int main()
{
   int listenfd,acceptfd,n;
   socklen_t  clilen;
   char recvbuf[100]={0};
   struct sockaddr_in cliaddr,servaddr;
   listenfd=socket(AF_INET,SOCK_STREAM,0);
   servaddr.sin_family=AF_INET;
   servaddr.sin_port=htons(8888);
   servaddr.sin_addr.s_addr = INADDR_ANY; 
   bind(listenfd,(struct sockaddr *)&servaddr,sizeof(struct sockaddr_in));
   listen(listenfd,5);
   clilen=sizeof(cliaddr);
   acceptfd=accept(listenfd,(struct sockaddr *)&cliaddr,&clilen);
   n=recv(acceptfd,recvbuf,sizeof(recvbuf)-1,0);
   printf("n=%d\n",n); 
   getchar();
   close(acceptfd);
   close(listenfd);
   return 0;
}

客戶端:

#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 <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <netinet/tcp.h>
#define MAXLINE 4096
int main()
{
   int sockfd,ret;
   struct sockaddr_in servaddr;
   char sendbuf[1000]={0};
   sockfd=socket(AF_INET,SOCK_STREAM,0);
   bzero(&servaddr,sizeof(servaddr));
   servaddr.sin_family=AF_INET;
   servaddr.sin_port=htons(8888);
   servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
   ret=connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
   printf("ret=%d\n",ret);
   write(sockfd,sendbuf,sizeof(sendbuf)+1);
   getchar();
   close(sockfd);
   return 0;
}

先啓動服務端,再啓動客戶端。客戶端每次發送 1001 個字節,而服務端只接收了 99 個字節,還有剩下的字節在接收緩衝區裏面。此時先關閉服務端,用 tcpdump 抓包查看。

16:28:06.149336 IP 127.0.0.1.49192 > 127.0.0.1.ddi-tcp-1: Flags [S], seq 3096824100, win 65495, options [mss 65495,sackOK,TS val 3916682872 ecr 0,nop,wscale 7], length 0
16:28:06.149354 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.49192: Flags [S.], seq 1491431840, ack 3096824101, win 65483, options [mss 65495,sackOK,TS val 3916682872 ecr 3916682872,nop,wscale 7], length 0
16:28:06.149372 IP 127.0.0.1.49192 > 127.0.0.1.ddi-tcp-1: Flags [.], ack 1, win 512, options [nop,nop,TS val 3916682872 ecr 3916682872], length 0
16:28:06.149461 IP 127.0.0.1.49192 > 127.0.0.1.ddi-tcp-1: Flags [P.], seq 1:1002, ack 1, win 512, options [nop,nop,TS val 3916682872 ecr 3916682872], length 1001
16:28:06.149491 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.49192: Flags [.], ack 1002, win 528, options [nop,nop,TS val 3916682872 ecr 3916682872], length 0
16:28:07.699933 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.49192: Flags [R.], seq 1, ack 1002, win 528, options [nop,nop,TS val 3916684423 ecr 3916682872], length 0

服務端沒有將數據全部接收完成,然後就關閉了,所以服務端產生了一個 RST。

  1. 在一個已關閉的 socket 上發到數據

客戶端:

#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 <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#define MAXLINE 4096
int main()
{
   int listenfd,acceptfd,n;
   socklen_t  clilen;
   char recvbuf[100]={0};
   struct sockaddr_in cliaddr,servaddr;
   listenfd=socket(AF_INET,SOCK_STREAM,0);
   servaddr.sin_family=AF_INET;
   servaddr.sin_port=htons(8888);
   servaddr.sin_addr.s_addr = INADDR_ANY; 
   bind(listenfd,(struct sockaddr *)&servaddr,sizeof(struct sockaddr_in));
   listen(listenfd,5);
   clilen=sizeof(cliaddr);
   acceptfd=accept(listenfd,(struct sockaddr *)&cliaddr,&clilen);
   getchar();
   close(acceptfd);
   close(listenfd);
   return 0;
}

服務端:

#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 <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <netinet/tcp.h>
#define MAXLINE 4096
int main()
{
   int sockfd,ret;
   struct sockaddr_in servaddr;
   char sendbuf[1000]={0};
   sockfd=socket(AF_INET,SOCK_STREAM,0);
   bzero(&servaddr,sizeof(servaddr));
   servaddr.sin_family=AF_INET;
   servaddr.sin_port=htons(8888);
   servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
   ret=connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
   printf("ret=%d\n",ret);
   getchar();
   write(sockfd,sendbuf,sizeof(sendbuf)+1);
   getchar();
   close(sockfd);
   return 0;
}

先打開服務端,在打開客戶端。然後關閉服務端,在客戶端按下回車鍵鍵,用 tcpdump 抓包查看結果。

16:44:16.226353 IP 127.0.0.1.49194 > 127.0.0.1.ddi-tcp-1: Flags [S], seq 3249455833, win 65495, options [mss 65495,sackOK,TS val 3917652949 ecr 0,nop,wscale 7], length 0
16:44:16.226370 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.49194: Flags [S.], seq 1092997986, ack 3249455834, win 65483, options [mss 65495,sackOK,TS val 3917652949 ecr 3917652949,nop,wscale 7], length 0
16:44:16.226387 IP 127.0.0.1.49194 > 127.0.0.1.ddi-tcp-1: Flags [.], ack 1, win 512, options [nop,nop,TS val 3917652949 ecr 3917652949], length 0
16:44:18.402946 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.49194: Flags [F.], seq 1, ack 1, win 512, options [nop,nop,TS val 3917655126 ecr 3917652949], length 0
16:44:18.403887 IP 127.0.0.1.49194 > 127.0.0.1.ddi-tcp-1: Flags [.], ack 2, win 512, options [nop,nop,TS val 3917655127 ecr 3917655126], length 0
16:44:20.376861 IP 127.0.0.1.49194 > 127.0.0.1.ddi-tcp-1: Flags [P.], seq 1:1002, ack 2, win 512, options [nop,nop,TS val 3917657100 ecr 3917655126], length 1001
16:44:20.376874 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.49194: Flags [R], seq 1092997988, win 0, length 0

客戶端和服務端建立連接之後,服務端就關閉了。此時客戶端再向服務端發送數據,此時服務端返回 RST。

總結

學會抓包,理解網絡編程中 TCP 的各種標誌,對學習網絡編程都有很大的好處 !

計算機網絡和網絡編程也是面試中的重點,工作中也會經常用到。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/jjv-YFkV338PNijbvgJLqQ