• 网络知识点大杂烩


      本文是个大杂烩,集中记述个人平时学习或遇到的关于网络的知识点。

    SYN Flood

       我们先来看一下TCP/IP三次握手的过程:

       wKiom1NOC4CxKFKNAAA3O0qX9bY741.gif

      1)Host A 发送一个TCP SYNchronize 包到 Host B
      2)Host B 收到 Host A的SYN
      3)Host B 发送一个 SYNchronize-ACKnowledgement
      4)Host A 接收到Host B的 SYN-ACK
      5)Host A 发送ACKnowledge
      6)Host B 接收到ACK
      7)TCP socket 连接建立ESTABLISHED

      在三次握手过程中,Host B发送SYN-ACK之后,收到Host A的ACK之前的TCP连接称为半连接(half-open connect)。此时Host B处于SYN_RECV状态。当收到ACK后,Host B转入ESTABLISHED状态。
      SYN攻击就是攻击端Host A在短时间内伪造大量不存在的伪IP地址,向Host B不断地发送SYN包,Host B回复确认包,并等待Host A的确认。由于源地址是不存在的,Host B需要不断的重发包直至超时。这些伪造的SYN包将长时间占用未连接队列,正常的SYN请求被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。
      SYN flood攻击是一种典型的DDos攻击。检测SYN攻击非常的方便,当我们在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。

      更详细的请参考:

      Linux下分析SYN flood攻击案例

      TCP洪水攻击(SYN Flood)的诊断和处理

      TCP SYN Flood攻击的原理机制/检测与防范及防御方法

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

      这部分摘自博文几种TCP连接中出现RST的情况。 

      在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表示可以连接。但是主机89却很不友好,莫名其妙的发送了一个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。

      看两段程序:

     1 //server.c
     2  
     3 int main(int argc, char** argv)  
     4 {  
     5     int listen_fd, real_fd;  
     6     struct sockaddr_in listen_addr, client_addr;  
     7     socklen_t len = sizeof(struct sockaddr_in);  
     8     listen_fd = socket(AF_INET, SOCK_STREAM, 0);  
     9     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 = htonl(INADDR_ANY);  
    17     listen_addr.sin_port = htons(SERV_PORT);  
    18     bind(listen_fd,(struct sockaddr *)&listen_addr, len);  
    19     listen(listen_fd, WAIT_COUNT);  
    20     while(1)  
    21     {  
    22         real_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &len);  
    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 }
    server.c

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

    1 char pcContent[4096];
    2 read(real_fd,pcContent,4096);
    3 close(real_fd);

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

      然后再看一下client的代码:

     1 //client.c
     2 int main(int argc, char** argv)  
     3 {  
     4     int send_sk;  
     5     struct sockaddr_in s_addr;  
     6     socklen_t len = sizeof(s_addr);  
     7     send_sk = socket(AF_INET, SOCK_STREAM, 0);  
     8     if(send_sk == -1)  
     9     {  
    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 = htons(SER_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 }
    client.c

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

      

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

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

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

      服务端和客户端代码如下:

     1 int main(int argc, char** argv)  
     2 {  
     3     int listen_fd, real_fd;  
     4     struct sockaddr_in listen_addr, client_addr;  
     5     socklen_t len = sizeof(struct sockaddr_in);  
     6     listen_fd = socket(AF_INET, SOCK_STREAM, 0);  
     7     if(listen_fd == -1)  
     8     {  
     9         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 }  
    server.c
     1 int main(int argc, char** argv)  
     2 {  
     3     int send_sk;  
     4     struct sockaddr_in s_addr;  
     5     socklen_t len = sizeof(s_addr);  
     6     send_sk = socket(AF_INET, SOCK_STREAM, 0);  
     7     if(send_sk == -1)  
     8     {  
     9         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 = htons(SER_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 }  
    client.c

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

      

    长连接与短连接

      该部分整理自博文TCP长连接与短连接的区别

      当网络通信时采用TCP协议时,在真正的读写操作之前,客户端与服务端之间必须建立一个连接。当读写操作完成后,双方不再需要这个连接时可以释放这个连接。连接的建立需要三次握手,而释放则需要4次握手,所以说每个连接的建立和释放都需要消耗资源和时间。

    长连接与短连接概念

      长连接: 指在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接;一般需要自己做在线维持。
      短连接: 指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接。它的优点是管理起来比较简单、存在的连接都是有用的连接、不需要额外的控制手段。比如http的,只是连接、请求、关闭。过程时间较短,服务器若是一段时间内没有收到请求即可关闭连接。 

      其实长连接是相对于通常的短连接而说的,也就是长时间保持客户端与服务端的连接状态。

    长连接与短连接的操作过程

      通常的短连接操作步骤是: 连接→数据传输→关闭连接。

      而长连接通常就是: 连接→数据传输→保持连接(心跳)→数据传输→保持连接(心跳)→……→关闭连接。

      这就要求长连接在没有数据通信时,定时发送数据包(心跳),以维持连接状态,短连接在没有数据传输时直接关闭就行了。

    什么时候用长连接,短连接?

      长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,下次次处理时直接发送数据包就OK了,不用建立TCP连接。例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。

      相对长连接,短连接更适应于操作过程时间短和简单的情况。

    长连接程序实例

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <errno.h>
      4 #include <sys/socket.h>
      5 #include <resolv.h>
      6 #include <stdlib.h>
      7 #include <netinet/in.h>
      8 #include <arpa/inet.h>
      9 #include <arpa/inet.h>
     10 #include <unistd.h>
     11 #include <sys/time.h>
     12 #include <sys/types.h>
     13 #define MAXBUF 1024
     14 
     15 int main(int argc, char **argv)
     16 {
     17     int sockfd, len;
     18     struct sockaddr_in dest;
     19     char buffer[MAXBUF];
     20     char heartbeat[20] = "hello server";
     21     fd_set rfds;
     22     struct timeval tv;
     23     int retval, maxfd = -1;
     24 
     25     if (argc != 3)
     26     {
     27         printf("error! the right format should be : 
     28             
    		%s IP port
    	 eg:	%s127.0.0.1 80
    ",
     29             argv[0], argv[0]);
     30         exit(0);
     31     }
     32 
     33     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
     34     {
     35         perror("Socket");
     36         exit(errno);
     37     }
     38 
     39     bzero(&dest, sizeof(dest));
     40     dest.sin_family = AF_INET;
     41     dest.sin_port = htons(atoi(argv[2]));
     42     memset(&(dest.sin_zero), 0, 8);
     43     if (inet_aton(argv[1], (struct in_addr*)&dest.sin_addr.s_addr) == 0)
     44     {
     45         perror(argv[1]);
     46         exit(errno);
     47     }
     48 
     49     if (connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0)
     50     {
     51         perror("Connect");
     52         exit(errno);
     53     }
     54     printf("
    Ready to start chatting.
     Direct input messages and 
     enter to send messages to the server
    ");
     55 
     56     while (1)
     57     {
     58         FD_ZERO(&rfds);
     59         FD_SET(0, &rfds);
     60         maxfd = 0;
     61 
     62         FD_SET(sockfd, &rfds);
     63         if (sockfd > maxfd)
     64             maxfd = sockfd;
     65 
     66         tv.tv_sec = 2;
     67         tv.tv_usec = 0;
     68 
     69         retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
     70         if (retval == -1)
     71         {
     72             printf("Will exit and the select is error! %s", strerror(errno));
     73             break;
     74         }
     75         else if (retval == 0)
     76         {
     77             //printf("No message comes, no buttons, continue to wait ...
    ");
     78             len = send(sockfd, heartbeat, strlen(heartbeat), 0);
     79             if (len < 0)
     80             {
     81                 printf("Message '%s' failed to send ! The error code is %d, error message '%s'
    ",
     82                     heartbeat, errno, strerror(errno));
     83                 break;
     84             }
     85             else
     86             {
     87                 printf("News: %s 	 send, sent a total of %d bytes!
    ",
     88                     heartbeat, len);
     89             }
     90 
     91             continue;
     92         }
     93         else
     94         {
     95             if (FD_ISSET(sockfd, &rfds))
     96             {
     97                 bzero(buffer, MAXBUF + 1);
     98                 len = recv(sockfd, buffer, MAXBUF, 0);
     99                 if (len > 0)
    100                 {
    101                     printf("Successfully received the message: '%s',%d bytes of data
    ",
    102                         buffer, len);
    103                 }
    104                 else
    105                 {
    106                     if (len < 0)
    107                         printf("Failed to receive the message! The error code is %d, error message is '%s'
    ",
    108                             errno, strerror(errno));
    109                     else
    110                         printf("Chat to terminate!
    ");
    111                     break;
    112                 }
    113             }
    114 
    115 
    116             if (FD_ISSET(0, &rfds))
    117             {
    118                 bzero(buffer, MAXBUF + 1);
    119                 fgets(buffer, MAXBUF, stdin);
    120 
    121                 if (!strncasecmp(buffer, "quit", 4))
    122                 {
    123                     printf("Own request to terminate the chat!
    ");
    124                     break;
    125                 }
    126 
    127                 len = send(sockfd, buffer, strlen(buffer) - 1, 0);
    128                 if (len < 0)
    129                 {
    130                     printf("Message '%s' failed to send ! The error code is %d, error message '%s'
    ",
    131                         buffer, errno, strerror(errno));
    132                     break;
    133                 }
    134                 else
    135                 {
    136                     printf("News: %s 	 send, sent a total of %d bytes!
    ",
    137                         buffer, len);
    138                 }
    139             }
    140         }
    141     }
    142 
    143     close(sockfd);
    144     return 0;
    145 }
    View Code

     

  • 相关阅读:
    【02】AJAX XMLHttpRequest对象
    【01】什么是AJAX
    NPM是什么
    nodejs npm常用命令
    angular(转)
    s6 传输层
    s6-9 TCP 定时器
    s6-8 TCP 拥塞控制
    s6-7 TCP 传输策略
    s6-6 TCP 连接释放
  • 原文地址:https://www.cnblogs.com/xiehongfeng100/p/4768209.html
Copyright © 2020-2023  润新知