多进程并发编程----基于高级的预先创建进程池(accept不上锁)的模型
2016-04-21 16:45
549 查看
此篇博文先介绍最简单的服务器并发模型,此模型的大概框架如下:
其实也很简单,程序开始就预先创建N个进程,创建的每一个子进程都循环调用accept函数阻塞在子进程,直到接收到新的客户端连接或者说是描述符s可读事件发生。而父进程只负责创建N个进程并等待进程的结束处理。
下面通过例子给大家介绍使用方法:
服务器程序:
此模型面临一种“惊群”现象,也就是说连接到来之前所有子进程都处于睡眠状态,第一个链接到来会惊醒所有的子进程处于惊醒状态,但只有预先运行的子进程会处理这个连接,其他的子进程会检查内核维护的连接队列已经变成了0,那么这些子进程继续睡眠。
此模式最大的问题:
1 如何确定创建多少个进程,预先创建进程之后,无法再次修改进程数目
2 每个子进程都循环调用accept函数阻塞,浪费很多cpu和内存
针对上面提出的两个问题,有对应的解决方案(这两种方案都是实际工作中常用的方法,因为比较高级,处理起来比较复杂,所以放在后面的博文里面讲):
1 针对1问题,可以搭配动态创建进程的方法,实现动态和静态创建进程,提高灵活性
2 针对2问题,创建N个进程之前,父进程负责accept函数的调用,收到新的连接后按照一定规则给分发给子进程。而子进程只负责处理数据,不再调用accept函数。这样大大提高了cpu的效率!
main{ socket(); bind(); listen(); for(i=0;i<N;i++){ pid=fork(); if(pid==0)//子进程 do_child(); } waitpid(); close(fd); } do_child(){ while(1){ accept(); read(); write(); close(); } }
其实也很简单,程序开始就预先创建N个进程,创建的每一个子进程都循环调用accept函数阻塞在子进程,直到接收到新的客户端连接或者说是描述符s可读事件发生。而父进程只负责创建N个进程并等待进程的结束处理。
下面通过例子给大家介绍使用方法:
服务器程序:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <time.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <signal.h> #define BUFFLEN 1024 #define SERVER_PORT 8888 #define BACKLOG 5 #define PIDNUMB 5 static void handle_connect(int s_s,int pid) { int s_c; /*客户端套接字文件描述符*/ struct sockaddr_in from; /*客户端地址*/ int len = sizeof(from); printf("process %d is being to accept fd %d\n",pid,s_s); /*主处理过程*/ while(1) { /*接收客户端连接*/ s_c = accept(s_s, (struct sockaddr*)&from, &len); if(s_c > 0) printf("process %d is being to handle client %d\n",pid,s_c); time_t now; /*时间*/ char buff[BUFFLEN];/*收发数据缓冲区*/ int n = 0; memset(buff, 0, BUFFLEN);/*清零*/ n = recv(s_c, buff, BUFFLEN,0);/*接收发送方数据*/ if(n > 0 && !strncmp(buff, "TIME", 4))/*判断是否合法接收数据*/ { memset(buff, 0, BUFFLEN);/*清零*/ now = time(NULL);/*当前时间*/ sprintf(buff, "%24s\r\n",ctime(&now));/*将时间拷贝入缓冲区*/ send(s_c, buff, strlen(buff),0);/*发送数据*/ } /*关闭客户端*/ close(s_c); } } void sig_int(int num) { exit(1); } int main(int argc, char *argv[]) { int s_s; /*服务器套接字文件描述符*/ struct sockaddr_in local; /*本地地址*/ signal(SIGINT,sig_int); /*建立TCP套接字*/ s_s = socket(AF_INET, SOCK_STREAM, 0); /*初始化地址接哦股*/ memset(&local, 0, sizeof(local));/*清零*/ local.sin_family = AF_INET;/*AF_INET协议族*/ local.sin_addr.s_addr = htonl(INADDR_ANY);/*任意本地地址*/ local.sin_port = htons(SERVER_PORT);/*服务器端口*/ /*将套接字文件描述符绑定到本地地址和端口*/ int err = bind(s_s, (struct sockaddr*)&local, sizeof(local)); err = listen(s_s, BACKLOG);/*侦听*/ /*处理客户端连接*/ pid_t pid[PIDNUMB]; int i =0; for(i=0;i<PIDNUMB;i++) { pid[i] = fork(); if(pid[i] == 0)/*子进程*/ { printf("pid[%d]\n",getpid()); handle_connect(s_s,getpid()); } } while(1); close(s_s); return 0; }客户端程序:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #include <stdio.h> #define BUFFLEN 1024 #define SERVER_PORT 8888 int main(int argc, char *argv[]) { int s; /*服务器套接字文件描述符*/ struct sockaddr_in server; /*本地地址*/ char buff[BUFFLEN];/*收发数据缓冲区*/ int n = 0; /*接收字符串长度*/ /*建立TCP套接字*/ s = socket(AF_INET, SOCK_STREAM, 0); /*初始化地址接哦股*/ memset(&server, 0, sizeof(server));/*清零*/ server.sin_family = AF_INET;/*AF_INET协议族*/ server.sin_addr.s_addr = htonl(INADDR_ANY);/*任意本地地址*/ server.sin_port = htons(SERVER_PORT);/*服务器端口*/ /*连接服务器*/ int err = connect(s, (struct sockaddr*)&server,sizeof(server)); memset(buff, 0, BUFFLEN);/*清零*/ strcpy(buff, "TIME");/*拷贝发送字符串*/ /*发送数据*/ send(s, buff, strlen(buff), 0); memset(buff, 0, BUFFLEN);/*清零*/ /*接收数据*/ n = recv(s, buff, BUFFLEN, 0); /*打印消息*/ if(n >0){ printf("TIME:%s\n",buff); } close(s); return 0; }
此模型面临一种“惊群”现象,也就是说连接到来之前所有子进程都处于睡眠状态,第一个链接到来会惊醒所有的子进程处于惊醒状态,但只有预先运行的子进程会处理这个连接,其他的子进程会检查内核维护的连接队列已经变成了0,那么这些子进程继续睡眠。
此模式最大的问题:
1 如何确定创建多少个进程,预先创建进程之后,无法再次修改进程数目
2 每个子进程都循环调用accept函数阻塞,浪费很多cpu和内存
针对上面提出的两个问题,有对应的解决方案(这两种方案都是实际工作中常用的方法,因为比较高级,处理起来比较复杂,所以放在后面的博文里面讲):
1 针对1问题,可以搭配动态创建进程的方法,实现动态和静态创建进程,提高灵活性
2 针对2问题,创建N个进程之前,父进程负责accept函数的调用,收到新的连接后按照一定规则给分发给子进程。而子进程只负责处理数据,不再调用accept函数。这样大大提高了cpu的效率!
相关文章推荐
- 你真的会用 Java 中的三目运算符吗?
- 生产者/消费者问题的多种Java实现方式
- java 去除ArrayList中重复的元素
- C++无法打开文件 xx.lib
- java中的char和boolean
- java编译基础总结
- 如何使用java类来加载properties配置文件的属性信息
- C++:google style 强制类型转换
- Java Hex 16进制的 byte String 转换类
- java数组
- c++ 前置++与后置++的区别
- ASP.NET MVC之文件上传【一】(八)
- java中的函数重载和重新的区别
- 遗传算法的C++实现
- JAX-WS(JWS):Java WebService
- jrtplib—VS2010下RTP开源协议库JRTPLIB3.9.1编译
- vb17
- 《C# in Depth:深入理解C#》读书笔记 - 委托
- python os.listdir按文件存取时间顺序列出目录
- RabbitMQ学习之基于spring-rabbitmq的RPC远程调用