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 }
时间: 2024-08-04 22:21:48

Linux网络编程客户\服务器设计范式的相关文章

Linux 网络编程——并发服务器的三种实现模型

服务器设计技术有很多,按使用的协议来分有 TCP 服务器和 UDP 服务器,按处理方式来分有循环服务器和并发服务器. 循环服务器与并发服务器模型 在网络程序里面,一般来说都是许多客户对应一个服务器(多对一),为了处理客户的请求,对服务端的程序就提出了特殊的要求. 目前最常用的服务器模型有: ·循环服务器:服务器在同一时刻只能响应一个客户端的请求 ·并发服务器:服务器在同一时刻可以响应多个客户端的请求 UDP 循环服务器的实现方法 UDP 循环服务器每次从套接字上读取一个客户端的请求 -> 处理

UNIX网络编程——客户/服务器心搏函数 (转)

下面是关于回送客户和服务器程序开发一些简单的心搏函数.这些函数可以发现对端主机或到对端的通信路径的过早失效.         在给出这些函数之前我们必须提出一些警告.首先,有人会想到使用TCP的保持存活特性(SO_KEEPALIVE套接字选项)来提供这种功能,然而TCP得在连接已经闲置2小时之后才发送一个保持存活探测段.意识到这一点以后,他们的下一个问题是如何把保持存活参数改为一个小得多的值(往往是在秒钟的量级),以便更快的检测到失效.尽管缩短TCP的保持存活定时器参数在许多系统上确实可行,但是

linux网络编程echo服务器

echo_server #include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <sys/socket.h>#include <www.qixoo.qixoo.com/sys/types.h>#include <signal.h>#include <memory.h>#include <errno.h>#include <netin

Linux网络编程入门 (转载)

http://www.cnblogs.com/RascallySnake/archive/2012/01/04/2312564.html (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端        在网络程序中,如果一个程序主动和外面的程序通信,那么我们把这个程序称为客户端程序. 比如我们使用ftp程序从另外一        个地方获取文件

Linux网络编程入门

(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍 客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端 在网络程序中,如果一个程序主动和外面的程序通信,那么我们把这个程序称为客户端程序. 比如我们使用ftp程序从另外一 个地方获取文件的时候,是我们的ftp程序主动同外面进行通信(获取文件), 所以这个地方我们的ftp程序就是客户端程序. 服务端 和客户端相对应的程序即为服务端程序.被动的等待外面的程序来和自己通

[转] - Linux网络编程 -- 网络知识介绍

(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端        在网络程序中,如果一个程序主动和外面的程序通信,那么我们把这个程序称为客户端程序. 比如我们使用ftp程序从另外一        个地方获取文件的时候,是我们的ftp程序主动同外面进行通信(获取文件), 所以这个地方我们的ftp程序就是客户端程序. 服务端        和客户端相

Linux客户/服务器程序设计范式1&mdash;&mdash;并发服务器(进程)

引言 本文会写一个并发服务器(concurrent server)程序,它为每个客户请求fork出一个子进程. 注意 1. 信号处理问题 对于相同信号,按信号的先后顺序依次处理.可能会产生的问题是,正在处理sig1信号时,又来了2个或更多的sig1信号,此sig1时只会在处理完原来的sig1信号后,再处理1个sig1信号.因此对于相同信号,会产生信号掉包的问题. 一个儿子退了之后,程序在处理handler(),如果此时又退了两个儿子,那么必然有一个儿子的资源回收不到,称为僵尸进程. 对于不同信号

Linux网络编程:客户端/服务器的简单实现

一. Socket的基本知识 1. socket功能 Socket层次 Socket实质上提供了进程通信的端点,进程通信之前,双方必须首先各自创建一个端点,否则是没有办法建立联系并相互通信的. 每一个Socket都一个半相关描述: {协议, 本地地址, 本地端口} 完整的Socket的描述: {协议, 本地地址, 本地端口, 远程地址, 远程端口} 2. Socket工作流程 面向连接(TCP)的Socket工作流程 UDP的socket工作流程 l 服务器端 首先,服务器应用程序用系统调用so

《Linux高性能服务器编程》学习总结(五)——Linux网络编程基础API

第五章      Linux网络编程基础API 对于网络编程,首先要了解的就是字节序的问题,字节序分为主机字节序和网络字节序,主机字节序又称小端字节序,是低字节存放在地地址,而网络字节序又称大端字节序,是低字节放在高地址.当数据在不同的机器上传播时,就需要统一字节顺序以保证不出现错误.在发送数据前,先将需要转变的数据转成网络字节序再发送,接收时先转成主机字节序再处理,要特别注意的是,即使是本机的两个进程通信,也要考虑字节序的问题,比如JAVA的虚拟机就使用大端字节序.使用如下代码可以查看本机的字