IO复用之 epoll(一)

一、epoll简单实现 单线程实现多路链接与多路通信

/* 开始epoll流程 */
int epollfd;
struct epoll_event events[EPOLLEVENTS];// 并发100个事件
//创建一个描述符
epollfd = epoll_create(FDSIZE);
//添加监听描述符事件
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = sock_fd;
epoll_ctl(epollfd, EPOLL_CTL_ADD, sock_fd, &ev);

//获取已经准备好的描述符事件
ret = epoll_wait(epollfd, events, EPOLLEVENTS, -1);
int i;
int fd;
//进行选好遍历
for (i = 0; i < ret; i++)
{
    fd = events[i].data.fd;
    //根据描述符的类型和事件类型进行处理
    if ((fd == sock_fd) && (events[i].events & EPOLLIN))
    {
        handle_accpet(epollfd, sock_fd);
    }
    else if (events[i].events & EPOLLIN)
    {
        memset(recv_buf, 0, sizeof(recv_buf));
        do_read(epollfd, fd, recv_buf);
    }
}


二、 epoll封装:单线程实现多路链接与多路通信

epoll封装类:

IO复用之 epoll(一)_第1张图片

一、地址类CHostAddress:包含服务器socket地址信息

CHostAddress.h

#pragma once
#include 
#include 
#include 

//功能:完成地址相关信息的赋值(struct sockaddr_in s_addr;)


class CHostAddress {

public:
	CHostAddress(unsigned short port);
	~CHostAddress();
	unsigned short getport();
	struct sockaddr_in* getaddr_in();
	struct sockaddr* getaddr();
	int getlength();


private:
	unsigned short port;
	struct sockaddr_in s_addr;
	int length;

};

CHostAddress.cpp

#include "CHostAddress.h"


CHostAddress::CHostAddress(unsigned short port)
{
	this->port = port;
	this->s_addr.sin_family = AF_INET;
	this->s_addr.sin_addr.s_addr = INADDR_ANY;
	this->s_addr.sin_port = htons(10086);
	this->length = sizeof(this->s_addr);
}

CHostAddress::~CHostAddress()
{

}

unsigned short CHostAddress::getport()
{
	return this->port;
}

sockaddr_in* CHostAddress::getaddr_in()
{
	return &(this->s_addr);
}

sockaddr* CHostAddress::getaddr()
{
	return (struct sockaddr*)&(this->s_addr);
}

int CHostAddress::getlength()
{
	return this->length;
}

二、基于socket的通信基类CBaseSocket

CBaseSocket.h

#pragma once
#include 
#include 
#include 
#include
#include
using namespace std;


//功能:进行socket,返回fd,并提供继承的接口
class CBaseSocket
{
public:
	CBaseSocket(unsigned short port);
	virtual ~CBaseSocket();

	int getSocketfd();
	void Start();//可能会有TCP和UDP
	virtual void Stop() = 0;//纯虚函数
	virtual void Run() = 0;
protected:
	int socketfd;
};

 CBaseSocket.cpp

#include "CBaseSocket.h"

CBaseSocket::CBaseSocket(unsigned short port)
{
	this->socketfd = 0;
}

CBaseSocket::~CBaseSocket()
{
}

int CBaseSocket::getSocketfd()
{
	return this->socketfd;
}

void CBaseSocket::Start()
{
	//this->socketfd = socket(PF_INET, SOCK_STREAM,IPPROTO_TCP );//有点不一样
	this->socketfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	//错误的判断
	if (this->socketfd < 0) {
		perror("socket error:");
	}
	else {
		cout << "socket success" << endl;
		this->Run();
	}
}

三、 继承socket通信基类:TCP式通信

 CTcpServer.h

#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include "CBaseSocket.h"
#include "CHostAddress.h"
/*
父类::Start():socket
构造:socket、bind、listen
RUN:accpet相关

*/
#define LISTEN_MAX 10
class CTcpServer:
    public CBaseSocket
{
public:
    CTcpServer(unsigned short port);//子类的参数一
    ~CTcpServer();

    void addAcceptVector(int newfd);
    void subAcceptVector(int newfd);

    void Stop();
    void Run();


private:
    CHostAddress *Add;//地址
    vector v_accfd;

};

 CTcpServer.cpp

#include "CTcpServer.h"
#include
using namespace std;


CTcpServer::CTcpServer(unsigned short port):CBaseSocket(port)
{
	this->Add = new CHostAddress(port);
}


CTcpServer::~CTcpServer()
{
	delete this->Add;//子类的析构要执行,把父写成虚
}


void CTcpServer::addAcceptVector(int newfd)
{
	this->v_accfd.push_back(newfd);
	cout << "v_accfd len = " << this->v_accfd.size() << endl;
}

void CTcpServer::subAcceptVector(int newfd)
{
	//vt.erase( remove( vt.begin() , vt.end() , k ) , vt.end() )
	this->v_accfd.erase(std::remove(v_accfd.begin(), v_accfd.end(), newfd), v_accfd.end());
	cout << "v_accfd len = " << this->v_accfd.size() << endl;
}

void CTcpServer::Stop()
{
	if (this->socketfd != 0) {
		close(this->socketfd);
		this->socketfd = 0;
	}
}

void CTcpServer::Run()
{
	int opt_val = 1;
	if (setsockopt(this->socketfd, SOL_SOCKET, SO_REUSEADDR, (const void*)&opt_val, sizeof(opt_val)) == -1) {
		perror("setsockopt error");
	}
	if (bind(this->socketfd, this->Add->getaddr(),this->Add->getlength()) == -1) {
		perror("bind error");
	}
	if (listen(this->socketfd, 5) == -1) {
		perror("listen error");
	}
	cout << "setsockopt、bind、listen Success 服务器启动……(" <socketfd <<")" << endl;
}

四、 epoll封装:利用成员实现socket前几个步骤,依靠epoll实现单线程

 CEpollServer.h

#pragma once
#include 
#include 
#include "CTcpServer.h"

/*包含TCPserver
* 
* 第一步:epoll_create(1)
* 第二步:ADD
* 第三步epoll_wait
* 
* 
* 
* 
*/

class CEpollServer
{
public:
	CEpollServer(unsigned short port);//对象初始化
	~CEpollServer();
	void Start();
	void Run();
	void Stop();

private:
	int epollfd;
	struct epoll_event epollevent;
	struct epoll_event epolleventArray[5];
	CTcpServer *Tcp;//完成到listen的类对象
};

 CEpollServer.cpp

#include "CEpollServer.h"

CEpollServer::CEpollServer(unsigned short port)
{
	this->Tcp = new CTcpServer(port);
	this->epollfd = 0;
	bzero(&epollevent, sizeof(epollevent));
}

CEpollServer::~CEpollServer()
{
}

void CEpollServer::Start()
{
	this->Tcp->Start();

	this->epollevent.data.fd = Tcp->getSocketfd();
	this->epollevent.events = EPOLLIN;	//有事件进入
	this->epollfd = epoll_create(5);
	if (this->epollfd == -1) {
		perror("epoll_create error");
	}
	else {
		cout << "epoll_create success" << endl;
	}
	int res = epoll_ctl(this->epollfd, EPOLL_CTL_ADD, Tcp->getSocketfd(), &(this->epollevent));
	if (res == -1) {
		perror("epoll_ctl error");
	}
	else {
		cout << "epoll_ctl success" << endl;
	}
}


//wait利用epoll accept新的客户端、接收消息
void CEpollServer::Run()
{
	char buf[300] = {0};
	int nReady = epoll_wait(this->epollfd, this->epolleventArray, 5, -1);//-1:永远等待
	cout << "就绪队列取出" << nReady  << "组事件" << endl;
	for (int i = 0; i < nReady; i++) {
		if (this->epolleventArray[i].data.fd == Tcp->getSocketfd()) {
			int acceptfd = accept(Tcp->getSocketfd(), NULL, NULL);
			cout << "新用户连接 acceptfd = " << acceptfd << endl;
			this->epollevent.data.fd = acceptfd;
			this->epollevent.events = EPOLLIN;
			epoll_ctl(this->epollfd, EPOLL_CTL_ADD, acceptfd, &(this->epollevent));
			Tcp->addAcceptVector(acceptfd);

		}
		else if (this->epolleventArray[i].events & EPOLLIN) {
			int res = read(this->epolleventArray[i].data.fd, buf, sizeof(buf));
			if (res > 0) {
				cout << "收到了来自fd:" << this->epolleventArray[i].data.fd << "的信息:" << buf << endl;
				bzero(buf,sizeof(buf));
			}
			else {
				//从epoll中删除
				Tcp->subAcceptVector(epolleventArray[i].data.fd);
				this->epollevent.data.fd = this->epolleventArray[i].data.fd;
				this->epollevent.events = EPOLLIN;
				epoll_ctl(this->epollfd, EPOLL_CTL_DEL, this->epolleventArray[i].data.fd, &(this->epollevent));
				//关闭这个fd对应的网络通道
				close(this->epolleventArray[i].data.fd);
			}
		}
	}
}

void CEpollServer::Stop()
{
	close(this->epollfd);
}

五、 项目入口

main.cpp 

#include
using namespace std;
#include "CEpollServer.h"

int main() {

	CEpollServer* ep = new CEpollServer(10086);
	ep->Start();
	while (1) {
		cout << "!" << endl;
		ep->Run();
	}
	return 0;
}

你可能感兴趣的:(linux,服务器,运维)