并发服务器(TCP-fork)

为了提高服务器的并发处理能力,在这里又引入了并发服务器的模型。并发服务器解决了循环服务器的缺陷,即可以在同一时刻响应多个客户端的请求。其基本设计思想是在服务器端采用多任务机制(即多进程或多线程),分别为每一个客户端创建一个任务处理。也可以使用select函数实现并发服务器。
首先介绍使用父子进程实现并发服务器(多线程与之类似),具体设计细节为由子进程来处理客户端的消息,而父进程接收客户端的连接请求,但不处理具体消息。也就是说,服务器端父进程一旦接收到客户端的连接请求,便建立好连接并创建新的子进程。这意味着每个客户端在服务器端有一个专门的子进程为其服务。

根据流程图的展示的过程,其服务器端编程的示例代码如下所示。
  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/socket.h>
  4 #include <string.h>
  5 #include <netinet/in.h>
  6 #include <arpa/inet.h>
  7 #include <stdlib.h>
  8 #include <sys/wait.h>
  9 #include <unistd.h>
 10 #include <signal.h>
 11 
 12 #define N 128
 13 #define errlog(errmsg) do{perror(errmsg);\
 14                             printf("---%s---%s---%d---\n",\
 15                             __FILE__, __func__, __LINE__);\
 16                             return -1;\
 17                          }while(0)
 18 void handler(int sig){
 19     /*通过信号使父进程执行等待回收子进程的资源的操作*/
 20     wait(NULL);
 21 }
 22 int main(int argc, const char *argv[])
 23 {
 24     int sockfd, acceptfd;
 25 
 26     struct sockaddr_in serveraddr, clientaddr;
 27     socklen_t addrlen = sizeof(serveraddr);
 28     char buf[N] = "";
 29     pid_t pid;
 30                                                                                                                                                                                      
 31     bzero(&serveraddr, addrlen);
 32     bzero(&clientaddr, addrlen);
 33 
 34     /*提示程序需要命令行传参*/
 35     if(argc < 3){
36         fprintf(stderr, "Usage: %s ip port\n", argv[0]);
 37         return -1;
 38     }
 39 
 40     /*创建套接字*/
 41     if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
 42         errlog("socket error");
 43     }
 44 
 45     /*填充网络信息结构体
 46      *inet_addr:将点分十进制地址转换为网络字节序的整型数据
 47      *htons:将主机字节序转换为网络字节序
 48      *atoi:将数字型字符串转化为整型数据
 49      */                                                                                                                                                                              
 50     serveraddr.sin_family = AF_INET;
 51     serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
 52     serveraddr.sin_port = htons(atoi(argv[2]));
 53 
 54     /*将套接字与服务器网络信息结构体把绑定*/
 55     if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) < 0){
 56         errlog("bind error");
 57     }
 58 
 59     /*将套接字设置为被动监听模式*/
 60     if(listen(sockfd, 5) < 0){
 61         errlog("listen error");
 62     }
 63 
 64     /*注册信号*/
 65     signal(SIGUSR1, handler);
 66 
 67     while(1){
 68         /*阻塞等待客户端的连接请求*/
69         if((acceptfd = accept(sockfd,\
 70                     (struct sockaddr *)&clientaddr, &addrlen)) < 0){
 71             errlog("accept error");
 72         }
 73 
 74         printf("ip: %s, port: %d\n",\
 75                 inet_ntoa(clientaddr.sin_addr),\
 76                 ntohs(clientaddr.sin_port));
 77 
 78         pid = fork();
 79 
 80         if(pid < 0){                                                                                                                                                                 
 81             errlog("fork error");
 82         }
 83         else if(pid == 0){
 84             /*子进程专门负责处理客户端的消息,不负责客户端的连接*/
 85 
 86             /*释放资源,子进程不需要接收客户端的连接*/
 87             close(sockfd);
 88             while(1){
 89                 ssize_t bytes;
 90                 if((bytes = recv(acceptfd, buf, N, 0)) < 0){
 91                     errlog("recv error");
 92                 }
 93                 else if(bytes == 0){
 94                     errlog("no data\n");
 95                 }
 96                 else{
 97                     if(strncmp(buf, "quit", 4) == 0){
 98                         printf("client quit\n");
99                         sleep(1);
100                         break;
101                     }
102                     else{
103                         printf("client: %s\n", buf);
104                         strcat(buf, "-server");
105                                                                                                                                                                                      
106                         if(send(acceptfd, buf, N, 0) < 0){
107                             errlog("send error");
108                         }
109                     }
110                 }
111             }
112             kill(getppid(), SIGUSR1);
113             exit(0);
114         }
115         else{
116             /*
117              *父进程的执行代码段释放资源,不需要收发信息
118              *关闭收发信息的文件描述符
119              */
120             close(acceptfd);
121         }
122     }
123     return 0;
124 }
客户端的代码如下所示。
  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/socket.h>
  4 #include <string.h>
  5 #include <netinet/in.h>
  6 #include <arpa/inet.h>
  7 
  8 #define N 128
  9 #define errlog(errmsg) do{perror(errmsg);\
 10                           printf("---%s---%s---%d---\n",\
 11                                   __FILE__, __func__, __LINE__);\
 12                           return -1;\
 13                          }while(0)                                                                                                                                                   
 14 int main(int argc, const char *argv[])
 15 {
 16     int sockfd;
 17     struct sockaddr_in serveraddr;
 18     socklen_t addrlen = sizeof(serveraddr);
 19     char buf[N] = "";
 20 
 21     /*提示程序需要命令行传参*/
 22     if(argc < 3){
 23         fprintf(stderr, "Usage: %s ip port\n", argv[0]);
 24         return -1;
 25     }
 26 
 27     /*创建套接字*/
 28     if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
 29         errlog("socket error");
 30     }
 31 
 32     /*填充网络信息结构体
 33      *inet_addr:将点分十进制地址转换为网络字节序的整型数据
 34      *htons:将主机字节序转换为网络字节序
 35      *atoi:将数字型字符串转化为整型数据
36      */
 37     serveraddr.sin_family = AF_INET;
 38     serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
 39     serveraddr.sin_port = htons(atoi(argv[2]));
 40 
 41 #if 0
 42     系统可以随机为客户端指定IP地址和端口号,客户端也可以自己指定
 43     struct sockaddr_in clientaddr;
 44     clientaddr.sin_family = AF_INET;
 45     clientaddr.sin_addr.s_addr = inet_addr(argv[3]);
 46     clientaddr.sin_port = htons(atoi(argv[4]));
 47 
 48     if(bind(sockfd, (struct sockaddr *)&clientaddr, addrlen) < 0){
 49         errlog("bind error");
 50     }
 51 #endif
 52 
 53     /*发送客户端连接请求*/
 54     if(connect(sockfd, (struct sockaddr *)&serveraddr, addrlen) < 0){
 55         errlog("connect error");
 56     }
 57 
 58     while(1){
 59         fgets(buf, N, stdin);
 60         buf[strlen(buf) - 1] = '\0';
 61 
 62         if(send(sockfd, buf, N, 0) < 0){
 63             errlog("send error");
 64         }
 65         else{
 66             if(strncmp(buf,"quit", 4) == 0){
 67                 break;
 68             }
69 
 70             if(recv(sockfd, buf, N, 0) < 0){
 71                 errlog("recv error");
 72             }
 73 
 74             printf("server: %s\n", buf);
 75         }
 76     }
 77 
 78     return 0;
 79 }
先运行服务器,等待客户端的连接。再运行客户端,不同于循环服务器,此时可同时运行多个客户端发起连接。服务器将不断通过创建子进程完成与客户端的对话。
服务器运行结果如下所示,同时接收两次客户端的连接请求。
linux@Master:~/1000phone/net/tcp_concurrent$ ./server 10.0.36.199 7777
ip: 10.0.36.199, port: 53385
ip: 10.0.36.199, port: 53386
client: hello
client: world
client quit
client quit
客户端其一的运行结果如下所示。发送数据,并接收服务器端发送修改之后的数据。
linux@Master:~/1000phone/net/tcp_concurrent$ ./client 10.0.36.199 7777
hello
server: hello-server
quit
另一个客户端的运行结果如下所示。发送数据,并接收服务器端发送修改之后的数据。
linux@Master:~/1000phone/net/tcp_concurrent$ ./client 10.0.36.199 7777
world
server: world-server
quit

select实现且看下周分享

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值