引言

本文会写一个并发服务器(concurrent server)程序,它为每个客户请求fork出一个子进程。

注意

1. 信号处理问题

对于相同信号,按信号的先后顺序依次处理。可能会产生的问题是,正在处理sig1信号时,又来了2个或更多的sig1信号,此sig1时只会在处理完原来的sig1信号后,再处理1个sig1信号。因此对于相同信号,会产生信号掉包的问题。 一个儿子退了之后,程序在处理handler(),如果此时又退了两个儿子,那么必然有一个儿子的资源回收不到,称为僵尸进程。

对于不同信号,优先处理后者,处理完后者在回头处理上一个。例如正在处理sig1时,来了sig2,则会先处理sig2,等处理完sig2后,回过头继续处理sig1。

2. 解决方案

在注册的信号处理函数中,使用循环,并在循环中用waitpid来回收子进程资源。只要进入信号处理函数,那么该信号处理函数就可以把所有fork出的儿子的资源都回收掉。注意:waitpid要设置成非阻塞模式,不然当进入循环后,如果没有子进程退出时,会阻塞在信号处理函数中。

3. wait与waitpid

wait一定是阻塞模式。因此在信号处理函数中,用while1{wait(NULL)}有问题,如果进入信号处理函数后,只要有儿子不退,就会一直阻塞在这里。

waitpid可以是阻塞模式,也可以是非阻塞模式。

4. select与accept

两者在收到信号时,均会返回-1。

对于系统默认不处理的信号,程序收不到,两者当然也不会返回-1。换句话说,SIGCHLD是系统默认不处理的信号,如果不对其注册信号处理函数,当子进程退出时,select与accept也是不会返回-1的。

代码

server.c

 7 #include "my_socket.h"
 8 #include <sys/wait.h>
 9 #include <signal.h>
10 #include <errno.h>
11 #define MY_IP "192.168.1.100"
12 #define MY_PORT 8888
13 #define SIZE 192
14 #define MSG_SIZE (SIZE - 4)
15 extern int errno ;
16 typedef struct tag_mag
17 {
18     int  msg_len;//记录msg_buf的真实大小
19     char msg_buf[MSG_SIZE];//msg_buf所占空间为188byte
20 }MSG, *pMSG;
21
22 void my_handle(int num)
23 {
24     /*waitpid参数:
25      * -1表示回收每一个儿子
26      * NULL表示不关心子进程的exit的返回值
27      * WNOHANG:wait no hang 非阻塞模式
28      *waitpid返回值:
29      * -1表示没有创建任何儿子
30      *  0表示没有儿子退出
31      *大于0表示有儿子退出
32      * */
33     while(waitpid(-1, NULL, WNOHANG ) > 0) ;
34 }
35
36 int main(int argc, char* argv[])
37 {
38     int fd_listen , fd_client ;
39     signal(SIGCHLD, my_handle);
40     my_socket(&fd_listen, MY_TCP, MY_IP ,MY_PORT);
41     my_listen(fd_listen, 10);
42
43     while( fd_client = accept(fd_listen, NULL, NULL))
44     {
45         /* 只要不是程序默认忽略的信号,accept都能收到,并返回-1 */
46         if(fd_client == -1)
47         {
48             if(errno == EINTR)
49             {
50                 continue ;
51             }else
52             {
53                 break ; //break退出后,父亲就退了。下来儿子会由init接管。
54             }
55         }else
56         {
57             if(fork() == 0) //fork儿子用于与客户端通信
58             {
59                 MSG recv_msg ;
60                 int recvn;
61                     while(1 )
62                     {
63                         memset(&recv_msg, 0, sizeof(MSG));
64                         /*在my_socket.c中,my_recv接收的长度 与 my_send 发送的长度必须是精确值
65                          * my_recv中填的长度小于等于实际要收的,是可以的,大于的话就永远退不出循环了*/
66                         my_recv(&recvn, fd_client, &recv_msg, 4);
67                         if(recvn == 0) //当对面客户端退出(关闭socket),系统调用recv的返回值为0
68                         {
69                             break ;
70                         }else
71                         {
72                             my_recv(NULL,fd_client, &recv_msg.msg_buf, recv_msg.msg_len);
73                             my_send(NULL, fd_client, &recv_msg, 4 + recv_msg.msg_len);
74
75                         }
76                     }
77                 close(fd_client);
78                 exit(0);
79             }
80             close(fd_client);
81         }
82     }
83     return 0 ;
84 }

client.c

 1 #include "my_socket.h"
 2 #define MY_IP "192.168.1.100"
 3 #define MY_PORT 6666
 4 #define SER_IP "192.168.1.100"
 5 #define SER_PORT 8888
 6 #define SIZE 192
 7 #define MSG_SIZE (SIZE - 4)
 8 typedef struct tag_mag
 9 {
10     int msg_len ;
11     char msg_buf[MSG_SIZE];//188
12 }MSG, *pMSG;
13 int main(int argc, char* argv[])
14 {
15     int sfd ;
16     my_socket(&sfd, MY_TCP, MY_IP, MY_PORT);
17     my_connect(sfd, SER_IP, SER_PORT);
18     MSG my_msg ;
19     while(memset(&my_msg, 0, sizeof(MSG)), fgets(my_msg.msg_buf, MSG_SIZE, stdin)!= NULL)
20     {
21         my_msg.msg_len = strlen(my_msg.msg_buf);
22         my_send(NULL, sfd, &my_msg, 4 + my_msg.msg_len );
23         memset(&my_msg, 0, sizeof(MSG));
24         my_recv(NULL, sfd, &my_msg, 4);
25         my_recv(NULL, sfd, &my_msg.msg_buf, my_msg.msg_len);
26         printf("recv from server : %s \n", my_msg.msg_buf);
27
28     }
29     close(sfd);
30
31 }

注意:本代码由于client.c中绑定了端口,因此只能连一个客户端,读者可以自己从命令行中输入端口号或者让系统自行分配。

本范式的缺陷在于,服务端在每次收到一个客户端连接请求后,才会fork儿子进行处理,fork有时间开销。更好的方法是,服务端提前fork好儿子,即进程池。

转载于:https://www.cnblogs.com/DLzhang/p/4020914.html

Linux客户/服务器程序设计范式——阿帕奇服务器(多进程)相关推荐

  1. 《jQuery与JavaScript入门经典》——第 1 章 动态Web编程简介 1.1理解Web服务器浏览器范式...

    本节书摘来自异步社区<jQuery与JavaScript入门经典>一书中的第1章,第1.1节,作者:[美]Brad Dayley著,更多章节内容可以访问云栖社区"异步社区&quo ...

  2. Linux客户机访问FTP服务器常见的问题及解决办法

    在Linux系统中,搭建一个服务,就是修改其配置文件,一般情况下,配置文件的修改出现问题的概率不大,多半是一些其他因素导致客户机与服务器之间访问出现问题,下面就针对客户机访问FTP服务器常出现的一些问 ...

  3. 27.Linux网络编程 掌握三次握手建立连接过程掌握四次握手关闭连接的过程掌握滑动窗口的概念掌握错误处理函数封装实现多进程并发服务器实现多线程并发服务器

    基本概念叫协议 什么叫协议? 协议是一个大家共同遵守的一个规则, 那么在这个网络通信当中,其实就是双方通信和解释数据的一个规则,这个概念 你也不用记,你只要心里明白就可以了, 分层模型, 物数网传会表 ...

  4. UNIX网络编程——客户/服务器程序设计示范(一)

    下面给出的是客户程序用于测试我们的服务器程序的各个变体. #include "unp.h"#define MAXN 16384 /* max # bytes to request ...

  5. Linux下两种TCP网络服务器实现方式:循环服务并发服务

    转载声明:this article is writen by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com) 以上博客已失效,所以 ...

  6. 基于Linux平台的TCP通信并发服务器---在线英语词典项目

    文章目录 前言 一.什么是并发服务器 二.服务器的实现 三.客户端的实现 四.代码测试结果 五.代码测试注意 总结 前言 本文是我在IO进线程.网络编程学习阶段的练习项目.项目基于linux平台,利用 ...

  7. linux线程池实现多线程并发,基于Linux的多线程池并发Web服务器设计-电子设计工程.PDF...

    基于Linux的多线程池并发Web服务器设计-电子设计工程.PDF 第 卷 第 期 电子设计工程 年 月 基于 的多线程池并发 服务器设计 陈 涛 任海兰 武汉邮电科学研究院 湖北 武汉 摘要 时至今 ...

  8. 在Linux下轻松玩转Samba服务器

    一.samba概念和功能 Samba是一个能让Linux系统应用Microsoft网络通讯协议的软件,而SMB是Server Message Block的缩写,即为服务器消息块 ,SMB主要是作为Mi ...

  9. Linux下Tomcat与Apache Web服务器的整合

    原文:http://os.51cto.com/art/200709/57327.htm ◆1.引言 基于Web技术的Internet/Intranet近年来已经得到了广泛的应用,Intranet是以T ...

  10. linux—用nc命令监控检测服务器端口

    前端用apache htttpd进行发布(80端口),通过双机负载均衡转发到后端的两个tomcat进行处理(8081和8082端口), 现在需要随时监控这三个端口的情况,一旦down掉需要能够立即告警 ...

最新文章

  1. python、numpy,keras,tensorflow等函数用法积累(持续更新)
  2. python __builtins__ credits类 (15)
  3. 【示例】solr添加文档/删除文档/简单查询/多条件查询
  4. 查找linux所有目录中包含字符,Linux查找目录下包含有某字符串的全部文件
  5. C语言项目:图形马赛克处理技术
  6. AJAX入门——工作原理
  7. 多个客户同时连接ServerSocket的简单实现
  8. Java 基础 10 个简单测试
  9. RNA甲基化修饰种类
  10. java编写蠕虫病毒_教大家编写蠕虫病毒
  11. 机器人bl虐心_【原创】爱你、无悔(双赛,BL,微虐,含H)
  12. Linux编译移植Qt5的环境_OMAPL138平台
  13. 数字电路低电平有效逻辑和符号的理解
  14. windows 实时自动同步两个文件夹
  15. 程序员福音 免费在线制作证件照
  16. 算法:最长公共子序列(输出所有最长公共子序列)
  17. 闰年的判断(python)
  18. java网络编程实现一个聊天程序
  19. 老子说:知其雄,守其雌
  20. Bearer Token 了解

热门文章

  1. matlab用prewitt算子,canny算子边缘检测_prewitt算子_matlab怎么中值滤波
  2. Verilog HDL 出租车计费器实现
  3. Sqlmap命令大全
  4. day01(计算机基本知识+JAVA基础知识+环境变量的配置+标识符命名规则+注释的分类)
  5. xx排排网数据加密(js逆向)
  6. 当代移动通信发展四个阶段
  7. 单片机的多路温度采集系统
  8. [ 隧道技术 ] cpolar 工具详解之将内网端口映射到公网
  9. 全球及中国CT机产业营销渠道现状与投资机遇研究报告2022版
  10. 时域,频域与傅立叶变换 - 慕水 - CSDNBlog