1. 程式人生 > >Linux網路程式設計客戶\伺服器設計正規化

Linux網路程式設計客戶\伺服器設計正規化

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\n", 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>\n"); 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\n", 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\n", reply); 70 } 71 close(fd); 72 } else { 73 break; 74 } 75 } 76 printf("child %d done\n", 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.\n");
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\n", 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\n", 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\n",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.\n", count);
            close(listenfd);
            char buffer[MAXLINE];
            int nbytes = read(connfd, buffer, MAXLINE);
            printf("read from client is:%s\n", 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.\n", 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\n", 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\n", 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\n", 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\n", strerror(errno));
70     }
71     return 0;
72 }