C/S程序设计范式
在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程序设计范式的更多相关文章
- Linux客户/服务器程序设计范式1——并发服务器(多进程)
引言 本文会写一个并发服务器(concurrent server)程序,它为每个客户请求fork出一个子进程. 注意 1. 信号处理问题 对于相同信号,按信号的先后顺序依次处理.可能会产生的问题是,正 ...
- Linux C++服务器程序设计范式
<Unix网络编程>30章详细介绍了几种服务器设计范式.总结了其中的几种,记录一下: 多进程的做法: 1.每次创建一个新的请求,fork一个子进程,处理该连接的数据传输. 2.预先派生一定 ...
- UNP学习笔记(第三十章 客户/服务器程序设计范式)
TCP测试用客户程序 #include "unp.h" #define MAXN 16384 /* max # bytes to request from server */ in ...
- Linux客户/服务器程序设计范式2——并发服务器(进程池)
引言 让服务器在启动阶段调用fork创建一个子进程池,通过子进程来处理客户端请求.子进程与父进程之间使用socketpair进行通信(为了方便使用sendmsg与recvmsg,如果使用匿名管道,则无 ...
- UNIX网络编程卷1 server程序设计范式7 预先创建线程,以相互排斥锁上锁方式保护accept
本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.预先创建一个线程池.并让每一个线程各自调用 accept 2.用相互排斥锁代替让每一个线 ...
- UNIX网络编程卷1 server程序设计范式1 并发server,为每一个客户请求fork一个进程
本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.传统并发server调用 fork 派生一个子进程来处理每一个客户 2.传统并发serv ...
- UNIX网络编程卷1 server程序设计范式8 预先创建线程,由主线程调用accept
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.程序启动阶段创建一个线程池之后仅仅让主线程调用 accept 并把客户连接传递给池中某个 ...
- 编程范式(Programming Paradigm)-[ 程序员的编程世界观 ]
编程范式(Programming Paradigm)是某种编程语言典型的编程风格或者说是编程方式.随着编程方法学和软件工程研究的深入,特别是OO思想的普及,范式(Paradigm)以及编程范式等术语渐 ...
- UNP服务器设计范式总结
一:客户端 本章总结的服务器程序设计范式,使用同一个客户端程序进行测试.个连接.使用的命令如下: client 206.62.226.36 8888 5 500 4000 二:结论 上图测量 ...
随机推荐
- typename 关键字
1.class关键字的同义词 template <typename T> const T& max(const T& x, const T& y) { return ...
- 【python实现卷积神经网络】Dropout层实现
代码来源:https://github.com/eriklindernoren/ML-From-Scratch 卷积神经网络中卷积层Conv2D(带stride.padding)的具体实现:https ...
- 【Tool】IDEA配置Maven依赖管理
IDEA配置Maven 打开IDEA,在项目界面打开[File] — [Settings] 找到构建工具,下面第一个就是Maven 主选项更换我们自己的主目录和设置目录与本地仓库 勾选[打印异常捕获信 ...
- Progress笔记
1. iconv -f gbk -t unicode test.tmp > test.csv 如果出现文件数据补全,需要确认在这之前,output是否已经close,如果output to指定了 ...
- python_ck01(虚拟环境管理)
拖拖拉拉的毛病还是依旧如初... 断断续续坚持三天总算把虚拟环境管理部分的内容给看完了. 对三天的知识点进行梳理,方便以后回顾. ①虚拟环境安装 用pip install + 包名的方式安装,涉及到的 ...
- [转+自]disable_functions之巧用LD_PRELOAD突破
写在前面: 通过知乎的一篇艰难的渗透提权,引发了一些对于disable_funcionts绕过的思考,虽然在暑假日记中记载了四种绕过disable_functions,比如com组件,pcntl_ex ...
- APT32入侵我国,试图窃取COVID-19相关情报
新闻一篇: 一直以来,APT32都以东南亚为攻击目标,并且是近几年来针对中国大陆进行攻击活动最活跃的APT攻击组织,没有之一.此次再将目标对准中国,与新冠疫情离不开关系. 4月22日,Fireye发布 ...
- TensorFlow keras 迁移学习
数据的读取 import tensorflow as tf from tensorflow.python import keras from tensorflow.python.keras.prepr ...
- 模拟电磁曲射炮_H题 方案分析【2019年电赛】【刘新宇qq522414928】
请查看我的有道云笔记: 文档:电磁曲射炮分析.note链接:http://note.youdao.com/noteshare?id=26f6b6febc04a8983d5efce925e92e21
- 0day堆(1)堆的管理策略
基本概念 堆块:堆区内存的基本单位 包括两个部分:块首,块身 块首:标识这个堆块自身的信息:如大小,是否被占用等 块身:分配给用户使用的数据区 堆表:一般位于堆区的起始位置,用于索引堆区所有堆块的信息 ...