IO复用之 epoll(一)

本文介绍了如何使用epoll在单线程环境下实现多路复用,通过创建epoll实例、添加监听和已连接套接字、等待事件以及处理事件,实现了高效地处理多个连接请求。同时,文章展示了CHostAddress和CBaseSocket等辅助类的封装,以及CTcpServer和CEpollServer的实现,展示了TCP服务器的基础操作和epoll的整合应用。
摘要由CSDN通过智能技术生成

一、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封装类:

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

CHostAddress.h

#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

//功能:完成地址相关信息的赋值(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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include<stdio.h>
#include<iostream>
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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <vector>
#include <algorithm>
#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<int> v_accfd;

};

 CTcpServer.cpp

#include "CTcpServer.h"
#include<iostream>
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 服务器启动……(" <<this->socketfd <<")" << endl;
}

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

 CEpollServer.h

#pragma once
#include <sys/epoll.h>
#include <string.h>
#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<iostream>
using namespace std;
#include "CEpollServer.h"

int main() {

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以回答关于C语言实现epoll IO复用的问题。以下是一个基本的epoll IO复用示例代码: ``` #include <sys/epoll.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #define MAX_EVENTS 10 #define PORT 8080 int main () { int server_fd, new_socket, epoll_fd, n, i; struct sockaddr_in address; struct epoll_event event, events[MAX_EVENTS]; // Create a socket if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("Failed to create socket"); exit(EXIT_FAILURE); } // Set socket options if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { perror("Failed to set socket options"); exit(EXIT_FAILURE); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // Bind the socket to a port if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("Failed to bind socket"); exit(EXIT_FAILURE); } // Listen on the socket if (listen(server_fd, 3) < 0) { perror("Failed to listen on socket"); exit(EXIT_FAILURE); } // Create epoll instance if ((epoll_fd = epoll_create1(0)) < 0) { perror("Failed to create epoll instance"); exit(EXIT_FAILURE); } // Add server socket to epoll decriptor event.events = EPOLLIN; event.data.fd = server_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) < 0) { perror("Failed to add server socket to epoll"); exit(EXIT_FAILURE); } while (1) { n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); for (i = 0; i < n; i++) { if (events[i].data.fd == server_fd) { // Server socket event if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { perror("Failed to accept incoming connection"); exit(EXIT_FAILURE); } // Add new socket to epoll descriptor event.events = EPOLLIN; event.data.fd = new_socket; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_socket, &event) < 0) { perror("Failed to add new socket to epoll"); exit(EXIT_FAILURE); } } else { // Client socket event // Handle read events on the client sockets and process the // incoming data. Also handle disconnect events. } } } return 0; } ``` 希望能对您有所帮助!算了,这是一个关于蒟蒻程序员的笑话:为什么程序员总喜欢喝奶茶?因为他们喜欢借用框架!哈哈!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值