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 二:结论 上图测量 ... 
随机推荐
- 在SpringBoot中使用SpringSecurity
			@ 目录 提出一个需求 解决方案: 使用SpringSecurity进行解决 SpringSecurity和SpringBoot结合 1. 首先在pom.xml中引入依赖: 2. 配置用户角色和接口的 ... 
- .NET Core 3 WPF MVVM框架 Prism系列之导航系统
			本文将介绍如何在.NET Core3环境下使用MVVM框架Prism基于区域Region的导航系统 在讲解Prism导航系统之前,我们先来看看一个例子,我在之前的demo项目创建一个登录界面: 我们看 ... 
- Win10 cmd的ssh命令连接linux虚拟机
			其实就是一个小发现了~ 闲的没事的时候在cmd里面敲了ssh命令,居然提示是一个命令,貌似以前是没有这功能的.然后就打开虚拟机试试能不能远程连接.没想到还成功了~ 有了这功能就省得安装专门的远程连接工 ... 
- CVE-2019-0193 远程命令执行-漏洞复现
			0x01 漏洞简介 Apache Solr 是一个开源的搜索服务器.Solr 使用 Java 语言开发,主要基于 HTTP 和 Apache Lucene 实现.此次漏洞出现在Apache Solr的 ... 
- Android 修改应用程序字体
			在网上搜索了相关资料,研究了两种算是比较快速的改变程序字体的方法,好,先来介绍着两种方法. 首先第一种方法是重写控件(以Textview为例): 1.Android在写程序的时候谷歌早已将所有字体都默 ... 
- JUC强大的辅助类讲解--->>>CountDownLatchDemo (减少计数)
			原理: CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞.其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞), ... 
- 【Jenkins】参数化引用
			我们在Jenkins里设置了参数如下 1. Jenkins中引用 shell引用 $env windows bat引用 %env% 在git等源码管理时,调用参数的格式${env} 2. jmete ... 
- vue2.x学习笔记(五)
			接着前面的内容:https://www.cnblogs.com/yanggb/p/12571062.html. 计算属性 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的.如果在模板中放入太 ... 
- 磁盘性能测试工具之fio
			fio是测试磁盘性能的一个非常好的工具,用来对硬件进行压力测试和验证. 注意事项 CentOS 6.5等较老版本的操作系统用fdisk创建分区时,默认为非4KB对齐选择初始磁柱编号,对性能有较大的影响 ... 
- react: typescript system params method optimize
			import * as _ from "lodash"; import paramCache from "../common/param-cache" impo ... 
