在上文中,提供了一个十分简单的回射服务器的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】多进程处理多客户端的连接相关推荐

  1. [ 搭建Redis本地服务器实践系列三 ] :图解Redis客户端工具连接Redis服务器

    原文:[ 搭建Redis本地服务器实践系列三 ] :图解Redis客户端工具连接Redis服务器 上一章 [ 搭建Redis本地服务器实践系列二 ] :图解CentOS7配置Redis  介绍了Red ...

  2. 服务器开发系列(五)——服务器运维

    系列文章目录 服务器开发系列(一)--计算机硬件 服务器开发系列(二)--Jetson Xavier NX 服务器开发系列(三)--Linux与Windows操作系统基础功能对比 服务器开发系列(四) ...

  3. 服务器开发系列(三)——Linux与Windows操作系统基础功能对比

    系列文章目录 服务器开发系列(一)--计算机硬件 服务器开发系列(二)--Jetson Xavier NX 文章目录 系列文章目录 前言 一.操作系统概述 二.Linux和Windows的应用场景 三 ...

  4. 【Linux服务器开发系列】手写一个用户态网络协议栈,瞬间提升你网络功底丨netmap/dpdk的实现

    手写一个用户态网络协议栈,瞬间提升你网络功底 1. 网卡基础架构 2. netmap/dpdk的实现 3. 网络协议栈实战 [Linux服务器开发系列]手写一个用户态网络协议栈,瞬间提升你网络功底丨n ...

  5. 【Linux服务器开发系列】一场redis线上事故引发的思考丨redis持久化 rdb和aof丨redis主从复制

    一场redis线上事故引发的思考 1. 事故背景介绍 2. redis持久化 rdb和aof 3. redis主从复制 4. 解决方案详解 [Linux服务器开发系列]一场redis线上事故引发的思考 ...

  6. 【Linux服务器开发系列】不同阶段的工程师,眼中的即时通讯,竟然如此差异

    不同阶段的程序员,眼中的即时通讯,竟然如此差异 1. 接入层方案( reactor,协程,iocp) 2. 逻辑层方案(xmpp,mqtt,protobuf) 3. 业务划分与实现 [Linux服务器 ...

  7. 【Linux服务器开发系列】详解多线程网络编程丨百分百干货分享丨学到就是赚到

    90分钟搞懂多线程网络编程模型 1. 网络编程关注的问题 2. 网络编程的几种模型reactor,one loop per thread及其变种 3. skynet,redis,nginx,memca ...

  8. 【Linux服务器开发系列】手写用户态协议栈,udpipeth数据包的封装,零拷贝的实现,柔性数组

    视频教你手写网络协议栈,保证大家能学会,耐心看 1. 用户态协议栈 2. udp/ip/eth数据包的封装 3. 零拷贝的实现 4. 零长数组(柔性数组) [Linux服务器开发系列]手写用户态协议栈 ...

  9. go语言web开发系列之五:gin用zap+file-rotatelogs实现日志记录及按日期切分日志

    一,安装需要用到的库: 1,安装zap日志库: liuhongdi@ku:/data/liuhongdi/zaplog$ go get -u go.uber.org/zap 2,安装go-file-r ...

最新文章

  1. apache日志分析
  2. 3306端口是什么协议_防黑必备技能之端口篇
  3. 局域网上传文件到服务器很慢,win10局域网内传文件很慢怎么办_win10局域网内文件传输很慢如何处理-win7之家...
  4. phpcmsv9修改表单直接在列表中显示字段方法
  5. android网络切换socket,Android版的websocket切换网络无法重连
  6. 松下年净利润预计降20% 或启动新一轮裁员
  7. JavaTPoint Java 中文教程【翻译完成】
  8. mybatis 二级缓存使用注意
  9. java代码程序流程思想_控制执行流程——java编程思想第4章
  10. 基于 Electron 做视频会议的两种实现方式
  11. 跨平台日志清理工具 Log-Cutter v2.0.1 RC-1 发布
  12. 角谱 matlab,关于角谱法实现数字全息 - 程序语言 - MATLAB/Mathematica - 小木虫论坛-学术科研互动平台...
  13. 手机APP测试需要注意的问题
  14. 用excel制作,出入库信息管理表,动态库存表
  15. 尼尔森十大交互设计原则
  16. 电脑硬件常见故障维修技巧
  17. mysql 单表 子查询_04 数据库入门学习-单表查询、多表查询、子查询(示例代码)...
  18. tyvj 火焰巨魔的惆怅
  19. win10 悬浮日历_Win10日历隐藏功能:可直接打印空白月历
  20. python eval函数的神奇魔法

热门文章

  1. php中的require(),PHP中include()与require()的区别说明
  2. C语言程序设计精要,C语言程序设计精要.doc
  3. mysql进程异常_关于MySQL-Proxy子进程异常退出BUG修复
  4. 机器视觉基本设计因素有哪几点?
  5. 赋能时空云计算,阿里云数据库时空引擎Ganos上线
  6. 高质量 Android 开发框架 LoonAndroid 详解
  7. flask+uwsgi 在调试过程中让python文件的更改自动重启uwsgi
  8. Maven Ant 中截取字符串
  9. Rational Rose :从用例图开始
  10. Eratosthenes筛法求1-100之间的素数