WebServer服务器二

以下均是自己的理解:利用进程池模拟了一个循环队列来处理不同的事件,其中每个进程都有它的子进程,父进程和子进程之间通过全双工通信进行信息的传递。当子进程的事件为读事件时,调用echo的process函数,使其向客户端发送信息

httpser.cc中创建进程池,echo中向客户端发送消息,processpool处理进程池中每个进程

server2.0:

httpser.cc

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<errno.h>
#include<fcntl.h>

#include"processpool.h"
#include"echo.h"


int main(int argc,char* argv[])
{
	printf("服务器已启动成功!------------\n");
	
	if(argc<3)
	{
		printf("Usage: %s ip_address portname\n",argv[0]);
		return 0;
	}
	
	const char* ip = argv[1];
	//atoi将字符串nptr转换为int
	int port = atoi(argv[2]);

	int listenfd = socket(AF_INET,SOCK_STREAM,0);
	assert(listenfd >= 1);

	struct sockaddr_in serv_addr;
	memset(&serv_addr,0,sizeof(serv_addr));

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(port);
	inet_pton(AF_INET,ip,&serv_addr.sin_addr);

	int ret = 0;
	ret = bind(listenfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
	assert(ret!=-1);

	ret = listen(listenfd,10);
	assert(ret!=-1);
	
	//创建进程池
	processpool<echo>* pool = processpool<echo>::create(listenfd,8);
	pool->run();

	close(listenfd);

	return 0;
}

processpool.h

#ifndef __PROCESSPOLL_H_
#define __PROCESSPOLL_H_

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include<netinet/in.h>
#include<fcntl.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/epoll.h>
#include<arpa/inet.h>

class process
{
public:
	int pid;    //进程的pid
	int pipe[2];    //父子通信通道  父pipe[0]  子pipe[1]  socketpair双向通信

	process():pid(-1),pipe{0,0}{}
};

template <typename T>
class processpool
{
private:
	static const int MAX_EVENTS_NUMBER = 5;
	static const int MAX_USER_PER_PROCESS = 10000;

	int idx;    //标记进程是父进程还是子进程,父进程为-1
	int listenfd;
	int epollfd;
	int max_processes_num;    //最大的进程数
	process* sub_processes;    //process类,记录进程池中每一个进程的属性
	static processpool<T>* instance;    //涉及内存泄露   由于是singleton模型

	processpool(int listenfd,int max_processes_num = 8);

	~processpool()
	{
		delete [] sub_processes;
	}

public:
	static processpool<T>* create(int listenfd,int _max_processes_num = 8)
	{
		
		if(instance == nullptr)
		{
			instance = new processpool<T>(listenfd,_max_processes_num);
			return instance;
		}
		return instance;
	}

	void run();
	void run_parent();
	void run_child();
	void setup_up_sig();
};

template<typename T>
processpool<T>* processpool<T>::instance = nullptr;

template<typename T>
processpool<T>::processpool(int listenfd,int _max_processes_num):
	idx(-1),listenfd(listenfd),epollfd(0),max_processes_num(_max_processes_num),sub_processes(nullptr)
{
	printf("processpool 构造函数-------\n");
	//记录子进程的数组
	sub_processes = new process[max_processes_num];

	for(int i=0;i<max_processes_num;i++)
	{
		//建立一对无名的、相互连接的套接字,可以用于全双工通信
		socketpair(AF_UNIX,SOCK_STREAM,0,sub_processes[i].pipe);
		//记录进程池中第i个进程的pid	,并创建该进程对应的子进程
		sub_processes[i].pid = fork();
		printf("[%d] has two value :[%d]、[%d]\n",sub_processes[i].pid,sub_processes[i].pipe[0],sub_processes[i].pipe[1]);
		//父进程  关闭子进程方的pipe,一端写,一端读
		if(sub_processes[i].pid>0)
		{
			close(sub_processes[i].pipe[1]);
			continue;
		}
		else
		{
			close(sub_processes[i].pipe[0]);
			idx = i;
			break;
		}
	}
}

//设置非阻塞模式
static int set_non_blocking(int fd)
{
	int old_state = fcntl(fd,F_GETFL);
	int new_state = old_state | O_NONBLOCK;
	fcntl(fd,F_SETFL,new_state);
	return old_state;
}

//将文件描述符加入监听树
static void addfd(int epollfd,int fd)
{
	struct epoll_event event;
	event.events = EPOLLIN | EPOLLET;
	event.data.fd = fd;
	epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
	set_non_blocking(fd);
}

//从监听树中移除描述符
static void removefd(int epollfd,int fd)
{
	epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,nullptr);
	close(fd);
}

//进程池run函数,如果是父进程执行run_parent(),否则执行run_child()
template<typename T>
void processpool<T>::run()
{
	if(idx == -1)
	{
		printf("run_parent()\n");
		run_parent();
	}
	else
	{
		printf("run_child()\n");
		run_child();
	}

}

//设置监听树
template<typename T>
void processpool<T>::setup_up_sig()
{
	printf("设置红黑树根节点\n");
	//设置epoll监听树
	epollfd = epoll_create(5);
	assert(epollfd!= -1);
}


template<typename T>
void processpool<T>::run_parent()
{
	epoll_event events[MAX_EVENTS_NUMBER];
	setup_up_sig();
	//将lfd加入监听树
	addfd(epollfd,listenfd);

	int pre_idx = 0;
	int has_new_cli = 1;
	int number = 0;
	while(1)
	{
		//满足的监听事件个数,events是传出参数
		number = epoll_wait(epollfd,events,MAX_EVENTS_NUMBER,-1);

		for(int i=0;i<number;i++)
		{
			int cfd = events[i].data.fd;
			if(cfd == listenfd)
			{
				//pre_idx表示前一个进程的索引,为了实现循环
				int pos = pre_idx;
				do{
					pos = (pos+1)%max_processes_num;
				}while(sub_processes[pos].pid == -1);  //切换到下一个进程时结束
				pre_idx = pos;  //记录进程索引
				//从父进程端的套接字发送信息
				send(sub_processes[pos].pipe[0],(void*)&has_new_cli,sizeof(has_new_cli),0);
				printf("parent processes has sent msg to %d child\n",pos);
			}
		}
	}
//	close(pipe[0]);
}

template<typename T>
void processpool<T>::run_child()
{
	epoll_event events[MAX_EVENTS_NUMBER];
	//设置监听树
	setup_up_sig();
	int pipefd = sub_processes[idx].pipe[1];
	addfd(epollfd,pipefd);
	T* users = new T[MAX_USER_PER_PROCESS];

	int number = 0;
	while(1)
	{
		//监听满足条件的事件个数
		number = epoll_wait(epollfd,events,MAX_EVENTS_NUMBER,-1);
		for(int i=0;i<number;i++)
		{

			int cfd = events[i].data.fd;
			//如果当前描述符为子进程读端的描述符并且事件为读事件时,表示从父进程收到了新客户端建立的消息
			if(cfd == pipefd && (events[i].events &EPOLLIN))
			{
				struct sockaddr_in cli_addr;
				socklen_t cli_addr_len = sizeof(cli_addr);
				//阻塞等待
				int connfd = accept(listenfd,(struct sockaddr*)&cli_addr,&cli_addr_len);
				//将新接收到的文件描述符加入监听树
				addfd(epollfd,connfd);
				//echo的connfd初始化
				users[connfd].init(epollfd,connfd,cli_addr);
				printf("child %d is addfding \n",idx);
				continue;
			}
			//如果当前描述符不是子进程的两端套接字,则执行echo的process,
			else if(events[i].events & EPOLLIN)
			{
				printf("child %d has recv msg\n",idx);
				users[cfd].process();
			}
		}
	}
	delete [] users;
	users = nullptr;
	
	close(epollfd);
	close(pipefd);
}
#endif

echo.h

#ifndef __ECHO_H_
#define __ECHO_H_

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<errno.h>
#include<fcntl.h>

#include "processpool.h"

class echo
{
private:
	static const int BUFFER_SIZE = 1024;
	static int epollfd;
	int cfd;
	sockaddr_in client_addr;
	char buf[1024] = {0};

public:
	echo(){}

	~echo(){}

	void init(int _epollfd,int _cfd,const sockaddr_in& address)
	{
		epollfd = _epollfd;			
		cfd = _cfd;
		client_addr = address;
	}

	void process()
	{
		while(1)
		{
			memset(buf,0,sizeof(buf));
			int ret = recv(cfd,buf,sizeof(buf),0);
			if(ret<0)
			{
				if(errno == EAGAIN||errno == EWOULDBLOCK)
				{
					break;
				}
			}
			else if(ret == 0)
			{
				removefd(epollfd,cfd);
				break;
			}
			else
			{
				printf("echo process send msg\n");
				//从cfd文件描述符往出发送东西,即向客户端发送:
				send(cfd,buf,sizeof(buf),0);
			}
		}
		return;
	}
};

int echo::epollfd = -1;
#endif

参考文章:https://love6.blog.csdn.net/article/details/123509817

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值