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,会产生"惊群"问题:

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

还有一点需要补充的是:

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

最新文章

  1. Hilbert-Huang Transform(希尔伯特-黄变换)
  2. Javascript:一个屌丝的逆袭
  3. Random随机类(11选5彩票)BigInteger大数据类(华为面试题1000的阶乘)
  4. 关于使用FusionCharts生成图表时出现invalid xml data错误提示的解决方法
  5. Topcoder SRM570 900 CurvyonRails
  6. C# 对象深度拷贝
  7. [Android自定义控件] Android自定义控件
  8. NSRange、NSPoint(CGPoint)、NSSize(CGSize)、NSRect(CGRect)
  9. Poj 3239 Solution to the n Queens Puzzle
  10. 表设计与SQL优化
  11. input 输入框获得/失去焦点时隐藏/显示文字(jquery版)
  12. WinForm - 两个窗体之间的方法调用
  13. [USACO08JAN]手机网络Cell Phone Network
  14. JavaScript实现省市联动
  15. 201521123004 《Java程序设计》第3周学习总结
  16. 【C#点滴记录】ASP.NET 使用C# 导出Word 和Excel
  17. 升级CentOS5.6_X64 python2.4.3到2.7
  18. Gradle 1.12用户指南翻译——第四十一章. 项目报告插件
  19. sqlserver2008中删除了windows用户,导致无法登陆的解决方案
  20. Qt学习3---子窗口与父窗口

热门文章

  1. Python语言-selenium webdriver操作记录汇总
  2. JSON Extractor(JSON提取器)
  3. 如何在Ubuntu 18.04上安装Nginx
  4. [一道蓝鲸安全打卡Web分析] 文件上传引发的二次注入
  5. python 基础篇 自定义函数
  6. 2019-2020-1 20199328《Linux内核原理与分析》第九周作业
  7. Django Channel实时推送与聊天
  8. IDEA 之 ERROR:无法在web.xml或使用此应用程序部署的jar文件中解析绝对uri:[http://java.sun.com/jsp/jstl/core]
  9. XEP-0199 XMPP Ping
  10. 微软宣布一批新获得Microsoft Teams认证的会议硬件