zoukankan      html  css  js  c++  java
  • 6.1 socket 长连接、短连接

      一般情况下,服务器的长连接和短连接不是服务器说了算,而是客户端说了算。因为服务器是给别人提供业务的,一旦连接建立起来之后,服务器端不会主动把连接给close掉。

      客户端发送一笔业务,没有关闭连接,然后又发送一笔业务,还是没有关闭连接,这个连接叫长连接,就是说客户端和服务器端建立完业务以后,就不断开连接了。建立连接需要很长时间,优化服务器一般就是优化连接,

      客户端每做一次通信就连接一下服务器,也就是每做一次通信就建立一个连接,然后断掉。这叫短连接。

    短连接的示例程序如下:

     1 #include <sys/types.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <stdio.h>
     5 #include <string.h>
     6 #include <errno.h>
     7 #include <arpa/inet.h>
     8 #include <sys/socket.h>
     9 #include <netinet/in.h>
    10 #include <sys/socket.h>
    11 #include <netinet/ip.h> /* superset of previous */
    12 
    13 int main()
    14 {
    15     int i = 0;
    16     for(i = 0; i < 10; i++)
    17     {
    18         int sockfd = 0;
    19         sockfd = socket(AF_INET, SOCK_STREAM, 0);
    20     
    21         struct sockaddr_in addr;
    22         addr.sin_family = AF_INET;
    23         addr.sin_port = htons(8001);
    24         inet_aton("192.168.31.128", &addr.sin_addr);
    25         //addr.sin_addr.s_addr = inet_addr("192.168.31.128");
    26     
    27         if( connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 )
    28         {
    29             perror("connect error");
    30             exit(0);
    31         }
    32     
    33         char recvbuf[1024] = {0};
    34         char sendbuf[1024] = {0};
    35     
    36         sprintf(sendbuf, "i : %d
    ", i);
    37         
    38         write(sockfd, sendbuf, strlen(sendbuf));
    39     
    40         read(sockfd, recvbuf, sizeof(recvbuf));
    41     
    42         fputs(recvbuf, stdout);
    43         memset(recvbuf, 0, sizeof(recvbuf));
    44         memset(sendbuf, 0, sizeof(sendbuf));
    45     
    46         close(sockfd);
    47     }
    48     
    49     return 0;
    50 }

    每发一次报文就连接一次,一共进行了10次连接,执行结果如下:

    如果需要和服务器频繁的交互,长连接比较好。如果长时间不发一次报文,则短连接好。

    客户端和服务器建立连接后,假如20分钟不动,特别是在公网上,则这个连接有可能被TCP IP协议重置,再发报文就是错误码。20分钟不动就重置连接这种操作可以在服务器提前设置。

    p2p聊天程序,模型如下:

    客户端的父进程从键盘接收数据,然后发送给服务器,服务器父进程接收数据并打印。 服务器子进程从键盘接收数据,然后发送给客户端,客户端的子进程接收数据并打印。

    服务器端程序如下:

      1 #include <sys/types.h>
      2 #include <unistd.h>
      3 #include <stdlib.h>
      4 #include <stdio.h>
      5 #include <string.h>
      6 #include <errno.h>
      7 #include <arpa/inet.h>
      8 #include <sys/socket.h>
      9 #include <netinet/in.h>
     10 #include <sys/socket.h>
     11 #include <netinet/ip.h> /* superset of previous */
     12 
     13 
     14 int main()
     15 {
     16     int sockfd = 0;
     17     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     18     
     19     if(sockfd == -1)
     20     {
     21         perror("socket error");
     22         exit(0);
     23     }
     24     
     25     struct sockaddr_in addr;
     26     addr.sin_family = AF_INET;
     27     addr.sin_port = htons(8001);
     28     inet_aton("192.168.31.128", &addr.sin_addr);
     29     //addr.sin_addr.s_addr = inet_addr("192.168.6.249");
     30     //addr.sin_addr.s_addr = INADDR_ANY;
     31     
     32     int optval = 1;
     33     if( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0)
     34     {
     35         perror("setsockopt error");
     36         exit(0);    
     37     }
     38     
     39     if( bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
     40     {
     41         perror("bind error");
     42         exit(0);
     43     }
     44     
     45     if(listen(sockfd, SOMAXCONN) < 0)
     46     {
     47         perror("listen error");
     48         exit(0);
     49     }
     50     
     51     struct sockaddr_in peeraddr;
     52     socklen_t peerlen;
     53     
     54     int conn = 0;
     55     
     56     char *p = NULL;
     57     int peerport = 0;
     58     p = inet_ntoa(peeraddr.sin_addr);
     59     peerport = ntohs(peeraddr.sin_port);
     60     
     61     char recvbuf[1024] = {0};
     62     int ret = 0;
     63 
     64     conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
     65     if(conn == -1)
     66     {
     67         perror("accept error");
     68         exit(0);
     69     }
     70     
     71     pid_t pid = 0;
     72     pid = fork();
     73     
     74     if(pid > 0)
     75     {
     76         printf("peeraddr = %s
     peerport = %d
    ", p, peerport);
     77         char recvbuf[1024] = {0};
     78         
     79         while(1)
     80         {
     81             ret = read(conn, recvbuf, sizeof(recvbuf));
     82     
     83             if(ret == 0)
     84             {
     85                 printf("peer closed 
    ");
     86                 exit(0);
     87             }
     88             else if(ret < 0)
     89             {
     90                 perror("read error");
     91                 exit(0);
     92             }
     93             
     94             printf("recvive from client : %s", recvbuf);
     95             //fputs(recvbuf, stdout);
     96     
     97         }
     98     }
     99     else if(pid == 0)
    100     {
    101         close(sockfd);
    102         char sendbuf[1024] = {0};
    103         
    104         while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
    105         {
    106             write(conn, sendbuf, strlen(sendbuf));
    107             memset(sendbuf, 0, sizeof(sendbuf));
    108         }
    109         close(conn);
    110     }
    111     else
    112     {
    113         perror("fork error");
    114         close(conn);
    115         close(sockfd);
    116         exit(0);
    117     }
    118     
    119     
    120     return 0;
    121 }

    客户端程序如下:

     1 #include <sys/types.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <stdio.h>
     5 #include <string.h>
     6 #include <errno.h>
     7 #include <arpa/inet.h>
     8 #include <sys/socket.h>
     9 #include <netinet/in.h>
    10 #include <sys/socket.h>
    11 #include <netinet/ip.h> /* superset of previous */
    12 
    13 int main()
    14 {
    15     int sockfd = 0;
    16     sockfd = socket(AF_INET, SOCK_STREAM, 0);
    17     
    18     struct sockaddr_in addr;
    19     addr.sin_family = AF_INET;
    20     addr.sin_port = htons(8001);
    21     inet_aton("192.168.31.128", &addr.sin_addr);
    22     //addr.sin_addr.s_addr = inet_addr("192.168.31.128");
    23     
    24     if( connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 )
    25     {
    26         perror("connect error");
    27         exit(0);
    28     }
    29     
    30     int pid = fork();
    31     
    32     if(pid > 0)
    33     {
    34         char sendbuf[1024] = {0};
    35         while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
    36         {
    37             write(sockfd, sendbuf, strlen(sendbuf));
    38             memset(sendbuf, 0, sizeof(sendbuf));
    39         }
    40         close(sockfd);
    41     }
    42     else if(pid == 0)
    43     {
    44         char recvbuf[1024] = {0};
    45         while(1)
    46         {
    47             read(sockfd, recvbuf, sizeof(recvbuf));
    48             printf("recvive from server : %s", recvbuf);
    49             //fputs(recvbuf, stdout);
    50             memset(recvbuf, 0, sizeof(recvbuf));
    51         }
    52     }
    53     else
    54     {
    55         perror("fork error");
    56         exit(0);
    57     }
    58     
    59     return 0;
    60 }

    执行结果如下:

    TCP IP协议流协议,服务器读到时就知道客户端已经断开连接了。 什么时候客户端会发送''呢? 就是当客户端关闭套接字时,TCP IP协议栈会发送一个FIN,这时候服务器端如果继续read的话,就会读到一个'' 。当客户端按下ctrl+c时,TCP IP协议栈就会发出FIN了,这时服务器端就能检测到客户端关闭了,如下所示:

     服务器端的read读到0之后,父进程就退出了,迅速查看进程状态,发现服务器端子进程没有死掉,如下:

    在父进程退出时,我们需要通知子进程也要退出,防止出现孤儿进程。

    修改服务器端程序如下:

      1 #include <sys/types.h>
      2 #include <unistd.h>
      3 #include <stdlib.h>
      4 #include <stdio.h>
      5 #include <string.h>
      6 #include <errno.h>
      7 #include <signal.h>
      8 #include <arpa/inet.h>
      9 #include <sys/socket.h>
     10 #include <netinet/in.h>
     11 #include <sys/socket.h>
     12 #include <netinet/ip.h> /* superset of previous */
     13 
     14 void handler(int num)
     15 {
     16     printf("receive num : %d
    ", num);
     17     exit(0);
     18 }
     19 
     20 int main()
     21 {
     22     int sockfd = 0;
     23     
     24     signal(SIGUSR1, handler);
     25     
     26     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     27     
     28     if(sockfd == -1)
     29     {
     30         perror("socket error");
     31         exit(0);
     32     }
     33     
     34     struct sockaddr_in addr;
     35     addr.sin_family = AF_INET;
     36     addr.sin_port = htons(8001);
     37     inet_aton("192.168.31.128", &addr.sin_addr);
     38     //addr.sin_addr.s_addr = inet_addr("192.168.6.249");
     39     //addr.sin_addr.s_addr = INADDR_ANY;
     40     
     41     int optval = 1;
     42     if( setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0)
     43     {
     44         perror("setsockopt error");
     45         exit(0);    
     46     }
     47     
     48     if( bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
     49     {
     50         perror("bind error");
     51         exit(0);
     52     }
     53     
     54     if(listen(sockfd, SOMAXCONN) < 0)
     55     {
     56         perror("listen error");
     57         exit(0);
     58     }
     59     
     60     struct sockaddr_in peeraddr;
     61     socklen_t peerlen;
     62     
     63     int conn = 0;
     64     
     65     char *p = NULL;
     66     int peerport = 0;
     67     p = inet_ntoa(peeraddr.sin_addr);
     68     peerport = ntohs(peeraddr.sin_port);
     69     
     70     char recvbuf[1024] = {0};
     71     int ret = 0;
     72 
     73     conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
     74     if(conn == -1)
     75     {
     76         perror("accept error");
     77         exit(0);
     78     }
     79     
     80     pid_t pid = 0;
     81     pid = fork();
     82     
     83     if(pid > 0)
     84     {
     85         printf("peeraddr = %s
     peerport = %d
    ", p, peerport);
     86         char recvbuf[1024] = {0};
     87         
     88         while(1)
     89         {
     90             ret = read(conn, recvbuf, sizeof(recvbuf));
     91     
     92             if(ret == 0)
     93             {
     94                 printf("client closed 
    ");
     95                 break;
     96                 //exit(0);
     97             }
     98             else if(ret < 0)
     99             {
    100                 perror("read error");
    101                 break;
    102                 //exit(0);
    103             }
    104             
    105             printf("recvive from client : %s", recvbuf);
    106             //fputs(recvbuf, stdout);
    107         }
    108         
    109         kill(pid, SIGUSR1);
    110         wait(NULL);
    111         
    112     }
    113     else if(pid == 0)
    114     {
    115         close(sockfd);
    116         char sendbuf[1024] = {0};
    117         
    118         while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
    119         {
    120             write(conn, sendbuf, strlen(sendbuf));
    121             memset(sendbuf, 0, sizeof(sendbuf));
    122         }
    123     }
    124     else
    125     {
    126         perror("fork error");
    127         close(conn);
    128         close(sockfd);
    129         exit(0);
    130     }
    131     
    132     close(conn);
    133     close(sockfd);
    134     return 0;
    135 }

    当服务端read接收到0之后,跳出循环,发送信号,并等待子进程死亡。子进程在信号处理函数中执行exit退出。

    执行结果如下:

    客户端程序中,子进程没有对read函数进行处理,所以,当服务器端执行主动关闭时,客户端父进程和子进程都不会退出,下面我们加入read函数的返回值处理。

    程序如下:

     1 #include <sys/types.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <stdio.h>
     5 #include <string.h>
     6 #include <errno.h>
     7 #include <signal.h>
     8 #include <arpa/inet.h>
     9 #include <sys/socket.h>
    10 #include <netinet/in.h>
    11 #include <sys/socket.h>
    12 #include <netinet/ip.h> /* superset of previous */
    13 
    14 void handler(int num)
    15 {
    16     printf("receive num : %d
    ", num);
    17     wait(NULL);
    18     exit(0);
    19 }
    20 
    21 int main()
    22 {
    23     int sockfd = 0;
    24     signal(SIGUSR1, handler);
    25     
    26     sockfd = socket(AF_INET, SOCK_STREAM, 0);
    27     
    28     struct sockaddr_in addr;
    29     addr.sin_family = AF_INET;
    30     addr.sin_port = htons(8001);
    31     inet_aton("192.168.31.128", &addr.sin_addr);
    32     //addr.sin_addr.s_addr = inet_addr("192.168.31.128");
    33     
    34     if( connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 )
    35     {
    36         perror("connect error");
    37         exit(0);
    38     }
    39     
    40     int pid = fork();
    41     
    42     if(pid > 0)
    43     {
    44         char sendbuf[1024] = {0};
    45         while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
    46         {
    47             write(sockfd, sendbuf, strlen(sendbuf));
    48             memset(sendbuf, 0, sizeof(sendbuf));
    49         }
    50         close(sockfd);
    51     }
    52     else if(pid == 0)
    53     {
    54         char recvbuf[1024] = {0};
    55         int ret = 0;
    56         while(1)
    57         {
    58             ret = read(sockfd, recvbuf, sizeof(recvbuf));
    59             if(ret == 0)
    60             {
    61                 printf("server closed
    ");
    62                 break;
    63             }
    64             
    65             if(ret < 0)
    66             {
    67                 printf("read error
    ");
    68                 break;
    69             }
    70             
    71             printf("recvive from server : %s", recvbuf);
    72             //fputs(recvbuf, stdout);
    73             memset(recvbuf, 0, sizeof(recvbuf));
    74         }
    75         
    76         close(sockfd);
    77         kill(getppid(), SIGUSR1);
    78         exit(0);
    79 
    80     }
    81     else
    82     {
    83         perror("fork error");
    84         exit(0);
    85     }
    86     
    87     return 0;
    88 }

    执行结果如下:

  • 相关阅读:
    Html代码查看器
    Http请求
    HTTP协议
    AsyncTask2
    AsyncTask
    幽灵线程解决方案
    handler消息机制入门
    多叉树的树形背包常见建模方法
    Codeforces Round #263
    怎样在win7下装ubuntu(硬盘版安装)
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9386651.html
Copyright © 2011-2022 走看看