博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【linux下c语言服务器开发系列1】多进程处理多客户端的连接
阅读量:6151 次
发布时间:2019-06-21

本文共 5406 字,大约阅读时间需要 18 分钟。

  hot3.png

    在上文中,提供了一个十分简单的回射服务器的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
=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连接服务器,发现服务器能为多个客户端服务啦。代码中存在的缺点如读写逻辑差、未进行子进程回收等内容,将在下一个版本中改掉。而且,多进程一般是用进程池,后面的版本也会实现。敬请期待。

转载于:https://my.oschina.net/shewa/blog/169653

你可能感兴趣的文章
《代码敲不队》第四次作业:项目需求调研与分析
查看>>
菜鸡互啄队—— 团队合作
查看>>
HttpWebRequest的GetResponse或GetRequestStream偶尔超时 + 总结各种超时死掉的可能和相应的解决办法...
查看>>
SparseArray
查看>>
第二章
查看>>
android背景选择器selector用法汇总
查看>>
[转]Paul Adams:为社交设计
查看>>
showdialog弹出窗口刷新问题
查看>>
java
查看>>
Vue.js连接后台数据jsp页面  ̄▽ ̄
查看>>
关于程序的单元测试
查看>>
mysql内存优化
查看>>
都市求生日记第一篇
查看>>
Java集合---HashMap源码剖析
查看>>
SQL优化技巧
查看>>
thead 固定,tbody 超出滚动(附带改变滚动条样式)
查看>>
Dijkstra算法
查看>>
css 动画 和 响应式布局和兼容性
查看>>
csrf 跨站请求伪造相关以及django的中间件
查看>>
MySQL数据类型--与MySQL零距离接触2-11MySQL自动编号
查看>>