一般情况下,服务器的长连接和短连接不是服务器说了算,而是客户端说了算。因为服务器是给别人提供业务的,一旦连接建立起来之后,服务器端不会主动把连接给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了,这时服务器端就能检测到客户端关闭了,如下所示: