linux c 多进程,【linux下c语言服务器开发系列1】多进程处理多客户端的连接
在上文中,提供了一个十分简单的回射服务器的demo,但是,这个demo的作用十分有限,只能接收一个客户端的连接,然后和其建立联系,进行通讯。等这个客户端断开连接后,才能处理另外的连接。功能太弱了。因为我们之前学过多进程程序设计,所以,这里提供一个回射服务器的多进程版本,使之能够同时处理多个客户端的连接。
多进程主要是靠fork函数来完成的,他是一个特殊函数,一次调用,两次返回。如果返回大于0,表示是父进程,同时返回值表示了他的子进程的pid。返回值如果等于0,表示他是一个子进程。子进程是父进程的一个拷贝,保留了父进程的所有东西。但是,我们往往需要让子进程执行别的函数。这个函数,你可以在程序里写,然后在子进程调用;也可以是已经存在的文件,你可以用exec族的函数,对子进程的内容进行复写。这部分具体怎么处理,完全看你的业务需求。在本例中,就是对已经接受的连接,进行读写操作。
在返回的父进程中,往往需要继续处理程序的主框架的内容。而且,需要对子进程的资源进行回收,即等待子进程执行完成,用wait和waitpid函数回收子进程,避免其成为僵尸进程而占用资源。wait可以返回子进程的退出状态,waipid可以非阻塞的处理。是wait的加强版。
下面是这个版本的回射服务器的demo代码,贴一下。注意,因为代码没有重构,看起来比较乱。尤其是读写函数,目前还处于一个零散的状态,没有抽出来。此外,没有进行wait,否则容易卡住,这个接下来会提供解决的方法。
#include"head.h"
#define MAX_USER 1024
#define BUF_SIZE 1024
typedef struct user //yon {
int conn; //表示客户与服务器的连接
sockaddr_in client_addr; //用户的地址
char bufread[BUF_SIZE]; // 用户的读缓存
char bufwrite[BUF_SIZE]; //用户的写缓存
} user;
int main(int argc, char **argv)
{
if(argc<3)
{
printf("usage: %s ip port\n",basename(argv[0]));
exit(0);
}
printf("start echoback server...\n");
char * ip=(char * ) argv[1];
int port =atoi(argv[2]);
/*构造服务器端的地址,主要是填充sockaddr_in 结构体*/
struct sockaddr_in server_address;
bzero(&server_address,sizeof(server_address));
server_address.sin_family=AF_INET;
inet_pton(AF_INET,ip,&server_address.sin_addr);
server_address.sin_port=htons(port);
int listenfd=socket(AF_INET,SOCK_STREAM,0);
assert(listenfd>=0);
int reuse=1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));
setnonblock(listenfd);
int ret=bind(listenfd,(struct sockaddr *) &server_address, sizeof(server_address));
assert(ret==0);
ret=listen(listenfd,5);
assert(ret==0);
/*服务器服务启动,等待客户端的链接的到来*/
int run_flag=1;
char buf[1024];
int conn=-1;
user users[MAX_USER];
int stop[MAX_USER];
for(int i=0;i
stop[i]=0;
int user_number=0;
while(run_flag)
{
int acfd=accept(listenfd,NULL,NULL);
if(acfd>=0)
{
printf("acfd>0,hhahahaha");
user_number++;
/*users[user_number] 表示一个conn,唯一标识这个用户链接*/
users[user_number].conn=acfd;
pid_t pid=fork();
if(pid<0)
{
printf("fork error\n");
exit(0);
}
else if (pid==0) /*这里进入子进程*/
{
close(listenfd); //关闭无用的监听套接字,由父进程继续监听
setnonblock(users[user_number].conn);
//while(!stop[ users[user_number].conn] )
while(1)
{
int readn=read(users[user_number].conn,users[user_number].bufread,BUF_SIZE);
// printf("readn=%d\n",readn);
//printf("errno=%d\n",errno);
if( (readn<0) && (errno!=EAGAIN))
{
printf("a\n");
printf("read fail, client %d\n",users[user_number].conn);
close(users[user_number].conn);
//stop[ users[user_number].conn ]=1;
//break;
exit(0);
}
else if (readn==0)
{
printf("b\n");
close(users[user_number].conn);
//stop[ users[user_number].conn ]=1;
//break;
exit(0);
} else if (readn>0)
{
printf("pid= %d\n",getpid());
users[user_number].bufread[readn]='\0';
memcpy(users[user_number].bufwrite,users[user_number].bufread,sizeof(users[user_number].bufread));
int writen=write(users[user_number].conn,users[user_number].bufwrite,sizeof(users[user_number].bufwrite));
memset(users[user_number].bufread,0,sizeof(users[user_number].bufread));
printf("write %d bytes to %d\n",writen,users[user_number].conn);
}
}
}
/*parent process*/
else
{
close(users[user_number].conn);
int stat_loc;
/*如果想要多个进程共同服务的话,就不能在这里阻塞的等待,可以通过sigchld信号进行捕获*/
// wait((int *)&stat_loc);
printf("parent, finished wait\n");
}
}
}
}
这段代码,运行起来后,可以用多个telnet连接服务器,发现服务器能为多个客户端服务啦。代码中存在的缺点如读写逻辑差、未进行子进程回收等内容,将在下一个版本中改掉。而且,多进程一般是用进程池,后面的版本也会实现。敬请期待。
linux c 多进程,【linux下c语言服务器开发系列1】多进程处理多客户端的连接相关推荐
- [ 搭建Redis本地服务器实践系列三 ] :图解Redis客户端工具连接Redis服务器
原文:[ 搭建Redis本地服务器实践系列三 ] :图解Redis客户端工具连接Redis服务器 上一章 [ 搭建Redis本地服务器实践系列二 ] :图解CentOS7配置Redis 介绍了Red ...
- 服务器开发系列(五)——服务器运维
系列文章目录 服务器开发系列(一)--计算机硬件 服务器开发系列(二)--Jetson Xavier NX 服务器开发系列(三)--Linux与Windows操作系统基础功能对比 服务器开发系列(四) ...
- 服务器开发系列(三)——Linux与Windows操作系统基础功能对比
系列文章目录 服务器开发系列(一)--计算机硬件 服务器开发系列(二)--Jetson Xavier NX 文章目录 系列文章目录 前言 一.操作系统概述 二.Linux和Windows的应用场景 三 ...
- 【Linux服务器开发系列】手写一个用户态网络协议栈,瞬间提升你网络功底丨netmap/dpdk的实现
手写一个用户态网络协议栈,瞬间提升你网络功底 1. 网卡基础架构 2. netmap/dpdk的实现 3. 网络协议栈实战 [Linux服务器开发系列]手写一个用户态网络协议栈,瞬间提升你网络功底丨n ...
- 【Linux服务器开发系列】一场redis线上事故引发的思考丨redis持久化 rdb和aof丨redis主从复制
一场redis线上事故引发的思考 1. 事故背景介绍 2. redis持久化 rdb和aof 3. redis主从复制 4. 解决方案详解 [Linux服务器开发系列]一场redis线上事故引发的思考 ...
- 【Linux服务器开发系列】不同阶段的工程师,眼中的即时通讯,竟然如此差异
不同阶段的程序员,眼中的即时通讯,竟然如此差异 1. 接入层方案( reactor,协程,iocp) 2. 逻辑层方案(xmpp,mqtt,protobuf) 3. 业务划分与实现 [Linux服务器 ...
- 【Linux服务器开发系列】详解多线程网络编程丨百分百干货分享丨学到就是赚到
90分钟搞懂多线程网络编程模型 1. 网络编程关注的问题 2. 网络编程的几种模型reactor,one loop per thread及其变种 3. skynet,redis,nginx,memca ...
- 【Linux服务器开发系列】手写用户态协议栈,udpipeth数据包的封装,零拷贝的实现,柔性数组
视频教你手写网络协议栈,保证大家能学会,耐心看 1. 用户态协议栈 2. udp/ip/eth数据包的封装 3. 零拷贝的实现 4. 零长数组(柔性数组) [Linux服务器开发系列]手写用户态协议栈 ...
- go语言web开发系列之五:gin用zap+file-rotatelogs实现日志记录及按日期切分日志
一,安装需要用到的库: 1,安装zap日志库: liuhongdi@ku:/data/liuhongdi/zaplog$ go get -u go.uber.org/zap 2,安装go-file-r ...
最新文章
- apache日志分析
- 3306端口是什么协议_防黑必备技能之端口篇
- 局域网上传文件到服务器很慢,win10局域网内传文件很慢怎么办_win10局域网内文件传输很慢如何处理-win7之家...
- phpcmsv9修改表单直接在列表中显示字段方法
- android网络切换socket,Android版的websocket切换网络无法重连
- 松下年净利润预计降20% 或启动新一轮裁员
- JavaTPoint Java 中文教程【翻译完成】
- mybatis 二级缓存使用注意
- java代码程序流程思想_控制执行流程——java编程思想第4章
- 基于 Electron 做视频会议的两种实现方式
- 跨平台日志清理工具 Log-Cutter v2.0.1 RC-1 发布
- 角谱 matlab,关于角谱法实现数字全息 - 程序语言 - MATLAB/Mathematica - 小木虫论坛-学术科研互动平台...
- 手机APP测试需要注意的问题
- 用excel制作,出入库信息管理表,动态库存表
- 尼尔森十大交互设计原则
- 电脑硬件常见故障维修技巧
- mysql 单表 子查询_04 数据库入门学习-单表查询、多表查询、子查询(示例代码)...
- tyvj 火焰巨魔的惆怅
- win10 悬浮日历_Win10日历隐藏功能:可直接打印空白月历
- python eval函数的神奇魔法
热门文章
- php中的require(),PHP中include()与require()的区别说明
- C语言程序设计精要,C语言程序设计精要.doc
- mysql进程异常_关于MySQL-Proxy子进程异常退出BUG修复
- 机器视觉基本设计因素有哪几点?
- 赋能时空云计算,阿里云数据库时空引擎Ganos上线
- 高质量 Android 开发框架 LoonAndroid 详解
- flask+uwsgi 在调试过程中让python文件的更改自动重启uwsgi
- Maven Ant 中截取字符串
- Rational Rose :从用例图开始
- Eratosthenes筛法求1-100之间的素数