1、前言
网络编程分为客户端和服务端,服务器通常分为迭代服务器和并发服务器。并发服务器可以根据多进程或多线程进行细分,给每个连接创建一个独立的进程或线程,或者预先分配好多个进程或线程等待连接的请求。今天探讨三种设计范式
(1)迭代服务器
(2)并发服务器,为每个客户请求创建一个进程或线程
(3)预先分配子进程或线程,每个子进程或线程调用accept
3、测试用例:
客户端代码:
1 #include <sys/wait.h> 2 #include <string.h> 3 #include <errno.h> 4 #include <netdb.h> 5 #include <stdlib.h> 6 7 #define IP "127.0.0.1" 8 #define PORT 8888 9 #define WORKER 4 10 #define MAXIN 4096 11 #define MAXLINE 4096 12 13 int tcp_connect(const char *host, const char *port) 14 { 15 if (host == NULL || port == NULL) { 16 return -1; 17 } 18 int sockfd, n; 19 struct addrinfo hints, *res, *ressave; 20 bzero(&hints, sizeof(struct addrinfo)); 21 hints.ai_family = AF_UNSPEC; 22 hints.ai_socktype = SOCK_STREAM; 23 if ((n = getaddrinfo(host, port, &hints, &res)) != 0) { 24 printf("tcp_connect error for %s,%s: %s ", host, port, strerror(errno)); 25 return -1; 26 } 27 ressave = res; 28 do { 29 sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 30 if (sockfd < 0) { 31 continue; 32 } 33 if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0) { 34 break; 35 } 36 close(sockfd); 37 } while( (res = res->ai_next) != NULL); 38 if (res == NULL) { 39 printf("tcp_connect error for %s,%s: %s", host, port, strerror(errno)); 40 return -1; 41 } 42 freeaddrinfo(ressave); 43 return sockfd; 44 } 45 46 int main(int argc, char **argv) 47 { 48 if (argc != 6) { 49 printf("usage: client <hostname or IPaddr> <port> <#children> <#loops/child> <#bytes/request> "); 50 return -1; 51 } 52 53 int i, j, fd, nchildlen, nloops, nbytes; 54 pid_t pid; 55 ssize_t n; 56 char request[MAXLINE], reply[MAXIN]; 57 nchildlen = atoi(argv[3]); 58 nloops = atoi(argv[4]); 59 nbytes = atoi(argv[5]); 60 snprintf(request, sizeof(request), "%d ", nbytes); 61 for (i = 0; i < nchildlen; i++) { 62 if ((pid = fork()) == 0) { 63 for (j = 0; j < nloops; j++) { 64 fd = tcp_connect(argv[1], argv[2]); 65 if (fd > 0) { 66 write(fd, request, strlen(request)); 67 68 if ((n = read(fd, reply, nbytes)) != nbytes) { 69 printf("read from server is:%s ", reply); 70 } 71 close(fd); 72 } else { 73 break; 74 } 75 } 76 printf("child %d done ", i); 77 exit(0); 78 } 79 } 80 /*waits all child process*/ 81 while (wait(NULL) > 0) 82 ; 83 if (errno != ECHILD) { 84 fprintf(stderr, "wait error"); 85 return -1; 86 } 87 return 0; 88 }
迭代服务器代码如下:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <netinet/in.h> 6 #include <arpa/inet.h> 7 #include <assert.h> 8 #include <string.h> 9 #include <errno.h> 10 11 #define IP "127.0.0.1" 12 #define PORT 8888 13 #define MAXLINE 4096 14 15 int main() 16 { 17 int listenfd, connfd; 18 struct sockaddr_in address, client_addr; 19 socklen_t client_addrlen = sizeof(client_addr); 20 bzero(&address, sizeof(address)); 21 address.sin_family = AF_INET; 22 inet_pton( AF_INET, IP, &address.sin_addr); 23 address.sin_port = htons(PORT); 24 listenfd = socket(PF_INET, SOCK_STREAM, 0); 25 assert(listenfd >= 0); 26 int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address)); 27 assert(ret != -1); 28 ret = listen(listenfd, 5); 29 assert(ret != -1); 30 31 char buffer[MAXLINE]; 32 while (1) { 33 printf("begin to accept. "); 34 int connfd = accept( listenfd, ( struct sockaddr* )&client_addr, &client_addrlen ); 35 if (connfd != -1) { 36 printf("accept a connection success.ip :%s, port :%d ", inet_ntoa(client_addr.sin_addr), client_addr.sin_port); 37 } else { 38 printf("accept a connection failed,error:%s", strerror(errno)); 39 } 40 41 int nbytes = read(connfd, buffer, MAXLINE); 42 printf("read from client is:%s ", buffer); 43 write(connfd, buffer, nbytes); 44 45 close(connfd); 46 } 47 return 0; 48 }
并发服务器,为每个客户请求创建一个进程测试代码如下:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <assert.h> #include <sys/wait.h> #include <string.h> #include <errno.h> #include <stdlib.h> #define IP "127.0.0.1" #define PORT 8888 #define MAXLINE 4096 int main() { int count = 0; struct sockaddr_in address, client_addr; socklen_t client_addrlen = sizeof( client_addr ); bzero(&address, sizeof(address)); address.sin_family = AF_INET; inet_pton( AF_INET, IP, &address.sin_addr); address.sin_port = htons(PORT); int listenfd,connfd; listenfd = socket(PF_INET, SOCK_STREAM, 0); assert(listenfd >= 0); int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address)); assert(ret != -1); ret = listen(listenfd, 5); assert(ret != -1); while(1) { connfd = accept( listenfd, ( struct sockaddr* )&client_addr, &client_addrlen ); if (connfd == -1) { printf("accept a connection failed,error:%s", strerror(errno)); break; } printf("accept a connection success.ip: %s,prot: %d ",inet_ntoa(client_addr.sin_addr),client_addr.sin_port); pid_t pid = fork(); count = count + 1; /*child process */ if (pid == 0) { printf("Create process %d handle a new connetcion. ", count); close(listenfd); char buffer[MAXLINE]; int nbytes = read(connfd, buffer, MAXLINE); printf("read from client is:%s ", buffer); write(connfd, buffer, nbytes); exit(0); } if (pid < 0) { printf("fork error"); } close(connfd); } return 0; }
预先分配子进程,每个子进程调用accept测试代码如下:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <netinet/in.h> 6 #include <arpa/inet.h> 7 #include <assert.h> 8 #include <sys/wait.h> 9 #include <string.h> 10 #include <errno.h> 11 #include <stdlib.h> 12 13 #define IP "127.0.0.1" 14 #define PORT 8888 15 #define WORKER 4 16 #define MAXLINE 4096 17 18 int worker(int listenfd, int i) 19 { 20 while (1) { 21 printf("I am worker %d, begin to accept connection. ", i); 22 struct sockaddr_in client_addr; 23 socklen_t client_addrlen = sizeof( client_addr ); 24 int connfd = accept( listenfd, ( struct sockaddr* )&client_addr, &client_addrlen ); 25 if (connfd != -1) { 26 printf("worker %d accept a connection success. ip:%s, prot:%d ", i, inet_ntoa(client_addr.sin_addr), client_addr.sin_port); 27 } else { 28 printf("worker %d accept a connection failed,error:%s", i, strerror(errno)); 29 } 30 char buffer[MAXLINE]; 31 int nbytes = read(connfd, buffer, MAXLINE); 32 printf("read from client is:%s ", buffer); 33 write(connfd, buffer, nbytes); 34 close(connfd); 35 } 36 return 0; 37 } 38 39 int main() 40 { 41 int i = 0; 42 struct sockaddr_in address; 43 bzero(&address, sizeof(address)); 44 address.sin_family = AF_INET; 45 inet_pton( AF_INET, IP, &address.sin_addr); 46 address.sin_port = htons(PORT); 47 int listenfd = socket(PF_INET, SOCK_STREAM, 0); 48 assert(listenfd >= 0); 49 int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address)); 50 assert(ret != -1); 51 ret = listen(listenfd, 5); 52 assert(ret != -1); 53 54 for (i = 0; i < WORKER; i++) { 55 printf("Create worker %d ", i+1); 56 pid_t pid = fork(); 57 /*child process */ 58 if (pid == 0) { 59 worker(listenfd, i); 60 } 61 if (pid < 0) { 62 printf("fork error"); 63 } 64 } 65 /*wait child process*/ 66 while (wait(NULL) != 0) 67 ; 68 if (errno == ECHILD) { 69 fprintf(stderr, "wait error:%s ", strerror(errno)); 70 } 71 return 0; 72 }