Linux网络编程客户服务器设计范式

Posted Dale工作学习笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 }

 

以上是关于Linux网络编程客户服务器设计范式的主要内容,如果未能解决你的问题,请参考以下文章

论如何设计一款端对端加密通讯软件

UNIX网络编程卷1 server程序设计范式8 预先创建线程,由主线程调用accept

Linux(程序设计):41---TCP网络编程

Scala的面向对象与函数编程

通过UNIX域套接字传递文件描述符

设计数据库的三范式