socket编程之并发回射服务器3篇文章中,提到了3种设计范式:

多进程

父进程阻塞于accept调用,然后为每个连接创建一个子进程。

多线程

主线程阻塞于accept调用,然后为每个连接创建一个子线程。

I/O复用

主进程阻塞于select调用,select负责监听listenfd和connfd。

本文描述第4种设计范式:prefork

prefork是指父进程预先派生若干个子进程,然后每个子进程阻塞于accept调用。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/errno.h> #define MAXLINE 4096
#define LISTENQ 10
#define PORT 8888 int tcp_listen(const char *port);
pid_t child_make(int i, int listenfd);
void child_main(int i, int listenfd);
void doEcho(int sockfd);
void sig_int(int signo); static int nchildren;
static pid_t *pids; int main(int argc, char **argv) { if (argc != ) {
printf("Usage: a.out nchildren\n");
exit();
}
int listenfd = tcp_listen("");
nchildren = atoi(argv[]);
pids = (pid_t*)calloc(nchildren, sizeof(pid_t));
for (int i = ; i < nchildren; i++) {
pids[i] = child_make(i, listenfd);
}
signal(SIGINT, sig_int);
for (; ; ) {
pause();
}
} int tcp_listen(const char *port) {
int listenfd;
struct sockaddr_in servaddr; if ( (listenfd = socket(AF_INET, SOCK_STREAM, )) < ) {
perror("socket error");
return -;
} bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT); if ( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < ) {
perror("bind error");
return -;
} if ( listen(listenfd, LISTENQ) < ) {
perror("listen error");
return -;
}
return listenfd;
} pid_t child_make(int i, int listenfd) {
pid_t pid;
if ( (pid = fork()) > ) {
return pid;
}
child_main(i, listenfd);
} void child_main(int i, int listenfd) {
int connfd;
struct sockaddr cliaddr;
socklen_t clilen; printf("child %ld starting\n", (long)getpid()); for (; ; ) {
clilen = sizeof(cliaddr);
if ( (connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen)) < ) {
if (errno == EINTR) {
continue;
} else {
perror("accept error");
exit();
}
}
doEcho(connfd);
close(connfd);
}
} void doEcho(int sockfd) {
char buff[MAXLINE];
while (true) {
memset(buff, , sizeof(buff));
int n = read(sockfd, buff, MAXLINE);
if (n < ) {
perror("read error");
exit();
} else if (n == ) {
printf("client closed\n");
break;
}
fputs(buff, stdout);
write(sockfd, buff, n);
}
} void sig_int(int signo) {
for (int i = ; i < nchildren; i++) {
kill(pids[i], SIGTERM);
}
while (wait(NULL) > )
;
if (errno != ECHILD) {
perror("wait error");
exit();
}
exit();
}

多个进程在同一个listenfd上调用accept,会产生"惊群"问题:

一个连接到来,所有进程都被唤醒,但只有一个进程能够成功获得连接。

还有一点需要补充的是:

父进程应该监视闲置子进程个数,随着所服务客户数的变化动态增减子进程个数。

C/S程序设计范式的更多相关文章

  1. Linux客户/服务器程序设计范式1——并发服务器(多进程)

    引言 本文会写一个并发服务器(concurrent server)程序,它为每个客户请求fork出一个子进程. 注意 1. 信号处理问题 对于相同信号,按信号的先后顺序依次处理.可能会产生的问题是,正 ...

  2. Linux C++服务器程序设计范式

    <Unix网络编程>30章详细介绍了几种服务器设计范式.总结了其中的几种,记录一下: 多进程的做法: 1.每次创建一个新的请求,fork一个子进程,处理该连接的数据传输. 2.预先派生一定 ...

  3. UNP学习笔记(第三十章 客户/服务器程序设计范式)

    TCP测试用客户程序 #include "unp.h" #define MAXN 16384 /* max # bytes to request from server */ in ...

  4. Linux客户/服务器程序设计范式2——并发服务器(进程池)

    引言 让服务器在启动阶段调用fork创建一个子进程池,通过子进程来处理客户端请求.子进程与父进程之间使用socketpair进行通信(为了方便使用sendmsg与recvmsg,如果使用匿名管道,则无 ...

  5. UNIX网络编程卷1 server程序设计范式7 预先创建线程,以相互排斥锁上锁方式保护accept

    本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.预先创建一个线程池.并让每一个线程各自调用 accept 2.用相互排斥锁代替让每一个线 ...

  6. UNIX网络编程卷1 server程序设计范式1 并发server,为每一个客户请求fork一个进程

    本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.传统并发server调用 fork 派生一个子进程来处理每一个客户 2.传统并发serv ...

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

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.程序启动阶段创建一个线程池之后仅仅让主线程调用 accept 并把客户连接传递给池中某个 ...

  8. 编程范式(Programming Paradigm)-[ 程序员的编程世界观 ]

    编程范式(Programming Paradigm)是某种编程语言典型的编程风格或者说是编程方式.随着编程方法学和软件工程研究的深入,特别是OO思想的普及,范式(Paradigm)以及编程范式等术语渐 ...

  9. UNP服务器设计范式总结

    一:客户端 本章总结的服务器程序设计范式,使用同一个客户端程序进行测试.个连接.使用的命令如下: client  206.62.226.36  8888  5  500  4000 二:结论 上图测量 ...

随机推荐

  1. String 对象-->charCodeAt() 方法

    1.定义和用法 获取指定下标的字符的ASCII码(Unicode) 返回值:0~65535之间的整数 语法: string.charCodeAt(index) 参数: index:指定字符的下标 举例 ...

  2. "二号标题"组件:<h2> —— 快应用组件库H-UI

     <import name="h2" src="../Common/ui/h-ui/text/c_h2"></import> < ...

  3. "一号标题"组件:<h1> —— 快应用组件库H-UI

     <import name="h1" src="../Common/ui/h-ui/text/c_h1"></import> < ...

  4. MySQL中group_concat函数 --- 很有用的一个用来查询出所有group by 分组后所有 同组内的 内容

    本文通过实例介绍了MySQL中的group_concat函数的使用方法,比如select group_concat(name) . MySQL中group_concat函数 完整的语法如下: grou ...

  5. Java团队课程设计——基于学院的搜索引擎

    团队名称.团队成员介绍.任务分配,团队成员课程设计博客链接 姓名 成员介绍 任务分配 课程设计博客地址 谢晓淞(组长) 团队输出主力 爬虫功能实现,Web前端设计及其后端衔接 爬虫:https://w ...

  6. stand up meeting 12/29/2015

    part 组员                今日工作              工作耗时/h 明日计划 工作耗时/h    UI 冯晓云 重写popup UI添加笔记功能     6 mergeUI ...

  7. Video tagging systems based on DNNs

    Need: With the ever-growth large-scale video in the mobile phone, so what will everyone get from the ...

  8. B - How many integers can you find 杭电1976

     Now you get a number N, and a M-integers set, you should find out how many integers which are small ...

  9. redis的安装(ubuntu版本)

    1.使用apt-get命令进行安装 安装gcc依赖 root@yatces-virtual-machine:~# apt-get update root@yatces-virtual-machine: ...

  10. python3爬虫 爬取动漫视频

    起因 因为本人家里有时候网速不行,所以看动漫的时候播放器总是一卡一卡的,看的太难受了.闲暇无聊又F12看看.但是动漫网站却无法打开控制台.这就勾起了我的兴趣.正好反正无事,去寻找下视频源. 但是这里事 ...