目录
[水平触发Level Triggered 工作模式](#水平触发Level Triggered 工作模式)
[边缘触发Edge Triggered工作模式](#边缘触发Edge Triggered工作模式)
epoll工作方式
你妈喊你吃饭的例子
你正在吃鸡, 眼看进入了决赛圈, 你妈饭做好了, 喊你吃饭的时候有两种方式: 1. 如果你妈喊你一次, 你没动, 那么你妈会继续喊你第二次, 第三次...(亲妈, 水平触发) 2. 如果你妈喊你一次, 你没动, 你妈就不管你了(后妈, 边缘触发)
epoll有2种工作方式-
水平触发(LT)
和
边缘触发(ET)
假如有这样一个例子:
- 我们已经把一个tcp socket添加到epoll描述符
- 这个时候socket的另一端被写入了2KB的数据
- 调用epoll_wait,并且它会返回. 说明它已经准备好读取操作
- 然后调用read, 只读取了1KB的数据
- 继续调用epoll_wait......
水平触发Level Triggered 工作模式
epoll默认状态下就是LT工作模式
- 当epoll检测到socket上事件就绪的时候, 可以不立刻进行处理. 或者只处理一部分.
- 如上面的例子, 由于只读了1K数据, 缓冲区中还剩1K数据, 在第二次调用 epoll_wait 时, epoll_wait 仍然会立刻返回并通知socket读事件就绪.
- 直到缓冲区上所有的数据都被处理完, epoll_wait 才不会立刻返回.
- 支持阻塞读写和非阻塞读写
边缘触发Edge Triggered工作模式
如果我们在第1步将socket添加到epoll描述符的时候使用了EPOLLET标志, epoll进入ET工作模式.
- 当epoll检测到socket上事件就绪时, 必须立刻处理.
- 如上面的例子, 虽然只读了1K的数据, 缓冲区还剩1K的数据, 在第二次调用 epoll_wait 的时候, epoll_wait 不会再返回了.
- 也就是说, ET模式下, 文件描述符上的事件就绪后, 只有一次处理机会.
- ET的性能比LT性能更高( epoll_wait 返回的次数少了很多). Nginx默认采用ET模式使用epoll.
- 只支持非阻塞的读写
select和poll其实也是工作在LT模式下. epoll既可以支持LT, 也可以支持ET.
对比LT和ET
LT是 epoll 的默认行为. 使用 ET 能够减少 epoll 触发的次数. 但是代价就是强逼着程序猿一次响应就绪过程中就把 所有的数据都处理完.
相当于一个文件描述符就绪之后, 不会反复被提示就绪, 看起来就比 LT 更高效一些. 但是在 LT 情况下如果也能做到 每次就绪的文件描述符都立刻处理, 不让这个就绪被重复提示的话, 其实性能也是一样的.
另一方面, ET 的代码复杂程度更高了.
理解ET模式和非阻塞文件描述符
使用 ET 模式的 epoll, 需要将文件描述设置为非阻塞. 这个不是接口上的要求, 而是 "工程实践" 上的要求.
假设这样的场景: 服务器接受到一个10k的请求, 会向客户端返回一个应答数据. 如果客户端收不到应答, 不会发送第 二个10k请求.
如果服务端写的代码是阻塞式的read, 并且一次只 read 1k 数据的话(read不能保证一次就把所有的数据都读出来, 参考 man 手册的说明, 可能被信号打断), 剩下的9k数据就会待在缓冲区中.
此时由于 epoll 是ET模式, 并不会认为文件描述符读就绪. epoll_wait 就不会再次返回. 剩下的 9k 数据会一直在缓 冲区中. 直到下一次客户端再给服务器写数据. epoll_wait 才能返回
但是问题来了.
- 服务器只读到1k个数据, 要10k读完才会给客户端返回响应数据.
- 客户端要读到服务器的响应
- 客户端发送了下一个请求, epoll_wait 才会返回, 才能去读缓冲区中剩余的数据.
所以, 为了解决上述问题(阻塞read不一定能一下把完整的请求读完), 于是就可以使用非阻塞轮训的方式来读缓冲区, 保证一定能把完整的请求都读出来.
而如果是LT没这个问题. 只要缓冲区中的数据没读完, 就能够让 epoll_wait 返回文件描述符读就绪.
epoll的使用场景
epoll的高性能, 是有一定的特定场景的. 如果场景选择的不适宜, epoll的性能可能适得其反.
- 对于多连接, 且多连接中只有一部分连接比较活跃时, 比较适合使用epoll.
例如, 典型的一个需要处理上万个客户端的服务器, 例如各种互联网APP的入口服务器, 这样的服务器就很适合epoll.
如果只是系统内部, 服务器和服务器之间进行通信, 只有少数的几个连接, 这种情况下用epoll就并不合适. 具体要根 据需求和场景特点来决定使用哪种IO模型.
代码实现
cpp
#include<iostream>
#include<string>
using namespace std;
class Connection
{
private:
int _sock;
//输入输出缓冲区
string _inbuffer; // string 二进制流, vector
string _outbuffer;
};
cpp
#pragma once
#include<iostream>
#include<memory>
#include<functional>
#include<string>
#include "Epoller.hpp"
#include "Socket.hpp"
#include "logs/ljwlog.h"
#include "nocopy.hpp"
using namespace std;
class Connection;
//设计回调
using fun_t = function<void(shared_ptr<Connection>)>;
class Connection
{
private:
int _sock;
//输入输出缓冲区
string _inbuffer; // string 二进制流, vector
string _outbuffer;
//读回调
fun_t _recv_cb;
//发送回调
fun_t _send_cb;
fun_t _except_cb;
//添加一个回指指针
shared_ptr<TcpServer> _tcp_server_ptr;
};
class TcpServer
{
public:
TcpServer()
{}
~TcpServer()
{}
private:
shared_ptr<Epoller> _epoller_ptr;
shared_ptr<Sock> _listensock_ptr;
unordered_map<int, shared_ptr<Connection>> _connections;
};
设计接口
只读方式和只写方式
设置ET模式
非阻塞设置
- 1.给sock也建立一个connection对象,将listensock添加到Connection中,同时,listensock和Connection放进_connections
- 2.添加到unordered_map
- 3.我们添加对应的事件,除了要加到内核中,fd,event
两个错误信号
EWOULDBLOCK
和 EINTR
的比较
错误类型 | EWOULDBLOCK |
EINTR |
---|---|---|
错误码值 | 通常为 11 (与 EAGAIN 等价) |
通常为 4 |
描述 | 操作不能立即完成,但未来可能成功,通常是非阻塞 I/O 的一种正常情况。 | 系统调用被信号中断,通常表示程序在等待某些事件时被外部信号中断。 |
常见原因 | 非阻塞 I/O 操作没有立即成功,通常会返回 EWOULDBLOCK 。 |
阻塞的系统调用(如 read() 或 wait() )被信号中断。 |
处理方式 | 在非阻塞 I/O 模式下,通常需要重试操作或使用多路复用(如 select() )。 |
程序通常需要检查返回的 EINTR 错误,并根据需要重试操作。 |
打印客户端信息
事件管理器
一次性读取字符串数据
IO把数据都读到了上层
测试:
TcpServer.hpp
cpp
#pragma once
#include <iostream>
#include <memory>
#include <functional>
#include <string>
#include "Epoller.hpp"
#include "Socket.hpp"
#include "logs/ljwlog.h"
#include "nocopy.hpp"
#include "Comm.hpp"
using namespace std;
class Connection;
class TcpServer;
uint32_t EVENT_IN = (EPOLLIN | EPOLLET);
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET);
// 设计回调
using func_t = function<void(shared_ptr<Connection>)>;
class Connection
{
public:
Connection(int sock, shared_ptr<TcpServer> tcp_server_ptr) : _sock(sock), _tcp_server_ptr(tcp_server_ptr)
{
}
void SetHandler(func_t recv_cb, func_t send_cb, func_t except_cb)
{
_recv_cb = recv_cb;
_send_cb = send_cb;
_except_cb = except_cb;
}
int SockFd()
{
return _sock;
}
void Append(const string &info)
{
_inbuffer += info;
}
const string& Inbuffer()
{
return _inbuffer;
}
~Connection()
{
}
private:
int _sock;
// 输入输出缓冲区
string _inbuffer; // string 二进制流, vector
string _outbuffer;
public:
// 读回调
func_t _recv_cb;
// 发送回调
func_t _send_cb;
func_t _except_cb;
// 添加一个回指指针
shared_ptr<TcpServer> _tcp_server_ptr;
string _ip;
uint16_t _port;
};
class TcpServer : public nocopy
{
public:
static const int num = 64;
public:
TcpServer(uint16_t port)
: _port(port),
_epoller_ptr(new Epoller()),
_listensock_ptr(new Sock)
{
}
void Init()
{
_listensock_ptr->Socket();
// 将文件描述符设置为非阻塞
SetNonBlockOrDie(_listensock_ptr->Fd());
_listensock_ptr->Bind(_port);
_listensock_ptr->Listen();
INFO("create listen socket success :%d\n", _listensock_ptr->Fd());
AddConnection(_listensock_ptr->Fd(), EVENT_IN,
bind(&TcpServer::Accepter, this, placeholders::_1), nullptr, nullptr);
}
void AddConnection(int sock, uint32_t event, func_t recv_cb, func_t send_cb, func_t except_cb,
const string &ip = "0.0.0.0", uint16_t port = 0)
{
// 1.给sock也建立一个connection对象,将listensock添加到Connection中,同时,listensock和Connection放进_connections
shared_ptr<Connection> new_connection = make_shared<Connection>(sock, shared_ptr<TcpServer>(this));
new_connection->SetHandler(recv_cb, send_cb, except_cb);
new_connection->_ip = ip;
new_connection->_port = port;
// 2.添加到unordered_map
_connections.insert(make_pair(sock, new_connection));
// 3.我们添加对应的事件,除了要加到内核中,fd,event
_epoller_ptr->EpllerUpdate(EPOLL_CTL_ADD, sock, event);
DEBUG("add a new connection success, sockfd is : %d ", sock);
}
// 获取新连接
void Accepter(shared_ptr<Connection> connection)
{
while (true)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
// 获取新连接
int sock = accept(_listensock_ptr->Fd(), (struct sockaddr *)(&peer), &len);
if (sock > 0)
{
uint16_t peerport = ntohs(peer.sin_port);
char inbuf[1024];
inet_ntop(AF_INET, &(peer.sin_addr), inbuf, sizeof(inbuf));
DEBUG("get a new client, get info->[ %s : %d], sockfd :%d ", inbuf, peerport, sock);
// 设置非阻塞
SetNonBlockOrDie(sock);
// 添加新的文件描述符进来
AddConnection(sock, EVENT_IN,
bind(&TcpServer::Recver, this, placeholders::_1),
bind(&TcpServer::Sender, this, placeholders::_1),
bind(&TcpServer::Excepter, this, placeholders::_1),
inbuf, peerport);
}
else
{
if (errno == EWOULDBLOCK)
break;
else if (errno == EINTR)
continue;
else
break;
}
}
}
// 事件管理器
// 应不应该关心数据的格式???不应该!!服务器只要I0数据就可以,有没有读完,报文的格式细节,你不用管。
void Recver(shared_ptr<Connection> connection)
{
const int g_buffer_size = 128;
int sock = connection->SockFd();
// ET模式要把数据都拿出来
while (true)
{
char buffer[g_buffer_size];
// 重置一下
memset(buffer, 0, sizeof(buffer));
ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0); // 非阻塞读取,sock已经设置为非阻塞
if (n > 0)
{
// 读取成功了,就向我们当前套接字对应的connection所指向的Append新增
connection->Append(buffer);
}
// 把读写异常都放进一个一个异常里
else if (n == 0)
{
// 退出了
INFO("sockfd:%d, client info %s:%d quit...", connection->_ip.c_str(), connection->_port);
connection->_except_cb(connection);
}
else
{
if (errno == EWOULDBLOCK)
{
// 一直读一直读,读出错了,说明读完了,然后break
break;
}
else if (errno == EINTR)
{
continue;
}
else
{
WARN("sockfd :%d ,client info %s : %d recv error...", sock, connection->_ip.c_str(), connection->_port);
connection->_except_cb(connection);
break;
}
// 读出错
}
}
}
void Sender(shared_ptr<Connection> connection)
{
}
void Excepter(shared_ptr<Connection> connection)
{
WARN("Excepter hander sockfd :%d, client info %s : %d excepter handler ", connection->_ip.c_str(), connection->_port);
}
bool IsConnectionSafe(int fd)
{
auto iter = _connections.find(fd);
if (iter == _connections.end())
{
return false;
}
else
return true;
}
// 调度
void Dispatcher(int timeout)
{
// n个就绪事件
int n = _epoller_ptr->EpollerWait(revs, num, timeout);
for (int i = 0; i < n; i++)
{
// 哪个事件就绪了,哪一个文件描述符就绪了
uint32_t events = revs[i].events;
int sock = revs[i].data.fd;
// 统一把事件异常转换成为读写问题
if (events & EPOLLERR)
{
events |= (EPOLLIN | EPOLLOUT);
}
if (events & EPOLLHUP)
{
events |= (EPOLLIN | EPOLLOUT);
}
// 只需处理EPOLLIN EPOLLOUT
if ((events & EPOLLIN) && IsConnectionSafe(sock))
{
if (_connections[sock]->_recv_cb)
{
_connections[sock]->_recv_cb(_connections[sock]);
}
}
if ((events & EPOLLOUT) && IsConnectionSafe(sock))
{
if (_connections[sock]->_send_cb)
{
_connections[sock]->_send_cb(_connections[sock]);
}
}
}
}
void Loop()
{
quit = false;
// AddConnection();
//_epoller_ptr->EpllerUpdate(EPOLL_CTL_ADD, _listensock_ptr->Fd(), EVENT_IN);
while (!quit)
{
// Dispatcher(3000);
Dispatcher(-1);
PrintConnection();
}
quit = true;
}
void PrintConnection()
{
cout << "_connections fd list: ";
for (auto &connection : _connections)
{
//把自己的文件描述符和输入缓冲区里的内容打印出来
cout << connection.second->SockFd() << ", ";
cout << "inbuffer: " << connection.second->Inbuffer();
}
cout << endl;
}
~TcpServer()
{
}
private:
bool quit;
uint16_t _port;
struct epoll_event revs[num];
shared_ptr<Epoller> _epoller_ptr;
shared_ptr<Sock> _listensock_ptr;
unordered_map<int, shared_ptr<Connection>> _connections;
};
上层处理
进行应用层处理
关心写事件epoll
cpp
void Sender(shared_ptr<Connection> connection)
{
auto &outbuffer = connection->Outbuffer();
while (true)
{
ssize_t n = send(_listensock_ptr->Fd(), outbuffer.c_str(), outbuffer.size(), 0);
if (n > 0)
{
// 发送成功就把数据从缓冲区中移除
outbuffer.erase(0, n);
}
else if (n == 0)
{
// 没有数据没发
return;
}
else
{
// 发送失败
if (errno == EWOULDBLOCK)
break; // 底层满了
else if (errno == EINTR)
continue;
else
{
WARN("sockfd :%d ,client info %s : %d send error...", connection->SockFd(), connection->_ip.c_str(), connection->_port);
connection->_except_cb(connection);
return;
}
}
}
总代码展示
TcpServer.hpp
cpp
#pragma once
#include <iostream>
#include <memory>
#include <functional>
#include <string>
#include "Epoller.hpp"
#include "Socket.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"
#include "logs/ljwlog.h"
using namespace std;
class Connection;
class TcpServer;
uint32_t EVENT_IN = (EPOLLIN | EPOLLET);
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET);
// 设计回调
using func_t = function<void(shared_ptr<Connection>)>;
class Connection
{
public:
Connection(int sock, shared_ptr<TcpServer> tcp_server_ptr) : _sock(sock), _tcp_server_ptr(tcp_server_ptr)
{
}
void SetHandler(func_t recv_cb, func_t send_cb, func_t except_cb)
{
_recv_cb = recv_cb;
_send_cb = send_cb;
_except_cb = except_cb;
}
int SockFd()
{
return _sock;
}
void AppendInBuffer(const string &info)
{
_inbuffer += info;
}
void AppendOutBuffer(const string &info)
{
_outbuffer += info;
}
string &Inbuffer()
{
return _inbuffer;
}
string &Outbuffer()
{
return _outbuffer;
}
~Connection()
{
}
private:
int _sock;
// 输入输出缓冲区
string _inbuffer; // string 二进制流, vector
string _outbuffer;
public:
// 读回调
func_t _recv_cb;
// 发送回调
func_t _send_cb;
func_t _except_cb;
// 添加一个回指指针
shared_ptr<TcpServer> _tcp_server_ptr;
string _ip;
uint16_t _port;
};
class TcpServer : public nocopy
{
public:
static const int num = 64;
public:
TcpServer(uint16_t port, func_t OnMessage)
: _port(port),
_epoller_ptr(new Epoller()),
_listensock_ptr(new Sock),
_OnMessage(OnMessage)
{
}
void Init()
{
_listensock_ptr->Socket();
// 将文件描述符设置为非阻塞
SetNonBlockOrDie(_listensock_ptr->Fd());
_listensock_ptr->Bind(_port);
_listensock_ptr->Listen();
INFO("create listen socket success :%d\n", _listensock_ptr->Fd());
AddConnection(_listensock_ptr->Fd(), EVENT_IN,
bind(&TcpServer::Accepter, this, placeholders::_1), nullptr, nullptr);
}
void AddConnection(int sock, uint32_t event, func_t recv_cb, func_t send_cb, func_t except_cb,
const string &ip = "0.0.0.0", uint16_t port = 0)
{
// 1.给sock也建立一个connection对象,将listensock添加到Connection中,同时,listensock和Connection放进_connections
shared_ptr<Connection> new_connection = make_shared<Connection>(sock, shared_ptr<TcpServer>(this));
new_connection->SetHandler(recv_cb, send_cb, except_cb);
new_connection->_ip = ip;
new_connection->_port = port;
// 2.添加到unordered_map
_connections.insert(make_pair(sock, new_connection));
// 3.我们添加对应的事件,除了要加到内核中,fd,event
_epoller_ptr->EpllerUpdate(EPOLL_CTL_ADD, sock, event);
DEBUG("add a new connection success, sockfd is : %d ", sock);
}
// 获取新连接
void Accepter(shared_ptr<Connection> connection)
{
while (true)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
// 获取新连接
int sock = accept(_listensock_ptr->Fd(), (struct sockaddr *)(&peer), &len);
if (sock > 0)
{
uint16_t peerport = ntohs(peer.sin_port);
char inbuf[1024];
inet_ntop(AF_INET, &(peer.sin_addr), inbuf, sizeof(inbuf));
DEBUG("get a new client, get info->[ %s : %d], sockfd :%d ", inbuf, peerport, sock);
// 设置非阻塞
SetNonBlockOrDie(sock);
// 添加新的文件描述符进来
AddConnection(sock, EVENT_IN,
bind(&TcpServer::Recver, this, placeholders::_1),
bind(&TcpServer::Sender, this, placeholders::_1),
bind(&TcpServer::Excepter, this, placeholders::_1),
inbuf, peerport);
}
else
{
if (errno == EWOULDBLOCK)
break;
else if (errno == EINTR)
continue;
else
break;
}
}
}
// 事件管理器
// 应不应该关心数据的格式???不应该!!服务器只要I0数据就可以,有没有读完,报文的格式细节,你不用管。
void Recver(shared_ptr<Connection> connection)
{
const int g_buffer_size = 128;
int sock = connection->SockFd();
// ET模式要把数据都拿出来
while (true)
{
char buffer[g_buffer_size];
// 重置一下
memset(buffer, 0, sizeof(buffer));
ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0); // 非阻塞读取,sock已经设置为非阻塞
if (n > 0)
{
// 读取成功了,就向我们当前套接字对应的connection所指向的Append新增
connection->AppendInBuffer(buffer);
}
// 把读写异常都放进一个一个异常里
else if (n == 0)
{
// 退出了
INFO("sockfd:%d, client info %s:%d quit...", connection->_ip.c_str(), connection->_port);
connection->_except_cb(connection);
}
else
{
if (errno == EWOULDBLOCK)
{
// 一直读一直读,读出错了,说明读完了,然后break
break;
}
else if (errno == EINTR)
{
continue;
}
else
{
WARN("sockfd :%d ,client info %s : %d recv error...", sock, connection->_ip.c_str(), connection->_port);
connection->_except_cb(connection);
break;
}
// 读出错
}
}
// 读到的当Sck所有的数据在connection结构体内部
// 数据有了,但是不一定全,1.检测 2.如果有完整的报文,就处理
_OnMessage(connection);
}
void Sender(shared_ptr<Connection> connection)
{
auto &outbuffer = connection->Outbuffer();
while (true)
{
ssize_t n = send(_listensock_ptr->Fd(), outbuffer.c_str(), outbuffer.size(), 0);
if (n > 0)
{
// 发送成功就把数据从缓冲区中移除
outbuffer.erase(0, n);
}
else if (n == 0)
{
// 没有数据没发
return;
}
else
{
// 发送失败
if (errno == EWOULDBLOCK)
break; // 底层满了
else if (errno == EINTR)
continue;
else
{
WARN("sockfd :%d ,client info %s : %d send error...", connection->SockFd(), connection->_ip.c_str(), connection->_port);
connection->_except_cb(connection);
return;
}
}
}
if (!outbuffer.empty())
{
//没有发完
//开启对写事件关心
EnableEvent(connection->SockFd(), true, true);
}
}
void Excepter(shared_ptr<Connection> connection)
{
WARN("Excepter hander sockfd :%d, client info %s : %d excepter handler ", connection->_ip.c_str(), connection->_port);
}
void EnableEvent(int sock, bool readable, bool writeable)
{
uint32_t events = 0;
//关心事件
events |= ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
_epoller_ptr->EpllerUpdate(EPOLL_CTL_ADD, sock, events);
}
bool IsConnectionSafe(int fd)
{
auto iter = _connections.find(fd);
if (iter == _connections.end())
{
return false;
}
else
return true;
}
// 调度
void Dispatcher(int timeout)
{
// n个就绪事件
int n = _epoller_ptr->EpollerWait(revs, num, timeout);
for (int i = 0; i < n; i++)
{
// 哪个事件就绪了,哪一个文件描述符就绪了
uint32_t events = revs[i].events;
int sock = revs[i].data.fd;
// 统一把事件异常转换成为读写问题
if (events & EPOLLERR)
{
events |= (EPOLLIN | EPOLLOUT);
}
if (events & EPOLLHUP)
{
events |= (EPOLLIN | EPOLLOUT);
}
// 只需处理EPOLLIN EPOLLOUT
if ((events & EPOLLIN) && IsConnectionSafe(sock))
{
if (_connections[sock]->_recv_cb)
{
_connections[sock]->_recv_cb(_connections[sock]);
}
}
if ((events & EPOLLOUT) && IsConnectionSafe(sock))
{
if (_connections[sock]->_send_cb)
{
_connections[sock]->_send_cb(_connections[sock]);
}
}
}
}
void Loop()
{
quit = false;
// AddConnection();
//_epoller_ptr->EpllerUpdate(EPOLL_CTL_ADD, _listensock_ptr->Fd(), EVENT_IN);
while (!quit)
{
// Dispatcher(3000);
Dispatcher(-1);
PrintConnection();
}
quit = true;
}
void PrintConnection()
{
cout << "_connections fd list: ";
for (auto &connection : _connections)
{
// 把自己的文件描述符和输入缓冲区里的内容打印出来
cout << connection.second->SockFd() << ", ";
cout << "inbuffer: " << connection.second->Inbuffer();
}
cout << endl;
}
~TcpServer()
{
}
private:
bool quit;
uint16_t _port;
struct epoll_event revs[num];
shared_ptr<Epoller> _epoller_ptr;
shared_ptr<Sock> _listensock_ptr;
unordered_map<int, shared_ptr<Connection>> _connections;
// 上层处理
func_t _OnMessage;
};
cpp
#include <iostream>
#include <memory>
#include "TcpServer.hpp" //处理IO的
#include "Calculator.hpp" //处理业务的
using namespace std;
Calculator calculator;
void DefaultOnMessage(shared_ptr<Connection> connection_ptr)
{
// 对报文进行处理,有bug
cout << "上层得到了数据: " << connection_ptr->Inbuffer() << endl;
//Handler这会自动移除缓冲区的数据
string response_str = calculator.Handler(connection_ptr->Inbuffer());
//如果是空的就不处理,说明没有完整的报文
if(response_str.empty()) return;
DEBUG(" %s ", response_str.c_str());
// response_str发送出去
connection_ptr->AppendOutBuffer(response_str);
//正确理解发送?
connection_ptr->_tcp_server_ptr->Sender(connection_ptr);
}
int main()
{
unique_ptr<TcpServer> epoll_server(new TcpServer(8080, DefaultOnMessage));
epoll_server->Init();
epoll_server->Loop();
return 0;
}
Epoller.hpp
cpp
#pragma once
#include "nocopy.hpp"
#include "logs/ljwlog.h"
#include<cstring>
#include <sys/epoll.h>
using namespace std;
class Epoller : public nocopy //防拷贝
{
static const int size = 128;
public:
Epoller()
{
_epfd = epoll_create(size);
if(_epfd == -1)
{
ERROR("epoll_create error :%s ", strerror(errno));
}
else
{
INFO("epoll_create sucess: %d ", _epfd);
}
}
int EpollerWait(struct epoll_event events[], int num, int timeout)//num是已经就绪fd个数
{
int n = epoll_wait(_epfd, events, num, timeout);//-1 阻塞等待
return n;//n是已经就绪fd文件描述符的个数
}
int EpllerUpdate(int oper, int sock, uint32_t event)
{
int n = 0;
if(oper == EPOLL_CTL_DEL)
{
n = epoll_ctl(_epfd, oper, sock, nullptr);
if(n != 0)
{
ERROR("epoll_ctl delete error!!!!!");
}
}
else
{
struct epoll_event ev;
ev.events = event;
ev.data.fd = sock;
n = epoll_ctl(_epfd, oper, sock, &ev);
if(n != 0)
{
ERROR("epoll_ctl error!!!");
}
}
return n;
}
~Epoller()
{
if(_epfd >= 0)
{
close(_epfd);
}
}
private:
int _epfd;
int _timeout{3000};
};