一般情况下,服务器的长连接和短连接不是服务器说了算,而是客户端说了算。因为服务器是给别人提供业务的,一旦连接建立起来之后,服务器端不会主动把连接给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\n", 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\n peerport = %d\n", 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 \n");
 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协议流协议,服务器读到\0时就知道客户端已经断开连接了。 什么时候客户端会发送'\0'呢? 就是当客户端关闭套接字时,TCP IP协议栈会发送一个FIN,这时候服务器端如果继续read的话,就会读到一个'\0' 。当客户端按下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\n", 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\n peerport = %d\n", 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 \n");
 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\n", 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\n");
62                 break;
63             }
64             
65             if(ret < 0)
66             {
67                 printf("read error\n");
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 }

执行结果如下:

 

posted on 2018-07-29 19:24  周伯通789  阅读(460)  评论(0编辑  收藏  举报