NO.1
一:什么是多进程模型
多进程模型是服务器在接收到大量高并发客户端访问时,通过创建多个子进程来与客户端进行通信。单进程阻塞在read()系统调用的时候,会导致服务器无法响应到其他的连接请求。这时可以通过fork()函数创建出多个子进程来处理业务,而主进程继续循环accept()其他客户连接,子进程实施具体的通信细节。
二:fork函数详解
NAME
fork - create a child process
#include <unistd.h>
fork() creates a new process by duplicating(复制) the calling process. The new process, referred to as the child, is an exact(准确的)
duplicate of the calling process, referred to as the parent, except for the following points:
......
RETURN VALUE
On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is
returned in the parent, no child process is created, and errno is set appropriately(适当的).
ERRORS
EAGAIN fork() cannot allocate sufficient(足够的) memory to copy the parent's page tables and allocate a task structure for the child.
EAGAIN It was not possible to create a new process because the caller's RLIMIT_NPROC(用户可拥有的最大进程数) resource limit was encountered(冲突).
To exceed this limit, the process must have either the CAP_SYS_ADMIN or the CAP_SYS_RESOURCE capability.
ENOMEM fork() failed to allocate the necessary kernel structures because memory is tight(紧的).
ENOSYS fork() is not supported on this platform(平台) (for example, hardware without a Memory-Management Unit).
所以由上可以总结出我们使用fork的“套路”:
pid_t pid; //pid_t是Process ID _Type的缩写,其实是宏定义下的unsigned int类型
pid = fork(); //调用fork函数
if(pid == -1) //如果返回值为-1,函数执行出错
ERR_EXIT("fork error\n");
else if(pid == 0){ //如果返回值为0,启动一个子进程
close(listenfd); //子进程负责通信细节,关闭监听套接字
do_service(connfd); //通信执行函数
exit(EXIT_SUCCESS); //do_service结束后,子进程已无用,在此处销毁
}
else //父进程
close(connfd); //父进程仍然循环accept
三:服务器代码示例
下面是一个简单的回射服务器代码,服务器将客户端发送的代码回给客户端。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#define ERR_EXIT(sz) \
do{ \
perror(sz); \
exit(EXIT_FAILURE); \
}while(0)
void do_service(int connfd)
{
char recvbuf[1024];
for(; ;){
memset(recvbuf, 0, sizeof(recvbuf));
ssize_t ret = read(connfd, recvbuf, sizeof(recvbuf));
if(ret == 0){
fprintf(stdout, "client close\n");
break;
}
fputs(recvbuf, stdout);
write(connfd, recvbuf, strlen(recvbuf));
memset(recvbuf, 0, sizeof(recvbuf));
}
}
int main(void)
{
struct sockaddr_in servaddr;
int listenfd;
if( (listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) //创建监听套接字,选择TCP协议
ERR_EXIT("sockfd err");
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET; //TCP协议族AF_INET或PF_INET,这两个宏实际上值是一样的
servaddr.sin_port = htons(8888); //端口8888
servaddr.sin_addr.s_addr = INADDR_ANY; //选择为本地任意地址
int on