Reactor模式
- 单 Reactor 单线程,前台接待员和服务员是同一个人,全程为顾客服务
- 单 Reactor 多线程,1 个前台接待员,多个服务员,接待员只负责接待
- 主从 Reactor 多线程,多个前台接待员,多个服务员
特点
- 响应快,不必为单个同步事件所阻塞
- 避免了多线程 或 进程的切换开销
主要使用单 Reactor 单线程,相当于请求到来时,判断请求是各种事件,然后将请求和事件和回调方法结合存放到红黑树当中,当时间就绪的时候回调对应事件的处理方法
epoll ET服务器(Reactor模式)
设计思路
Epoller.hpp
对 epoll 的三个系统调用函数进行一定的封装
#pragma once
#include <iostream>
#include <cerrno>
#include <cstdlib>
#include <unistd.h>
#include <sys/epoll.h>
class Epoller
{
public:
static const int gsize = 128;
public:
static int CreateEpoller()
{
int epfd = epoll_create(gsize);// 创建对应size的epfd
if (epfd < 0)
{
cout<<"epoll_create :" <<errno<< ':'<< strerror(errno)<<endl;
exit(3);
}
return epfd;
}
static bool AddEvent(int epfd, int sock, uint32_t event)
{
struct epoll_event ev;
ev.events = event;
ev.data.fd = sock;
int n = epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev);// 给对应的socket添加到epoll中
return n == 0;
}
static bool ModEvent(int epfd, int sock, uint32_t event)
{
struct epoll_event ev;
ev.events = event;
ev.data.fd = sock;
int n = epoll_ctl(epfd, EPOLL_CTL_MOD, sock, &ev);// 修改已有scoket的event
return n == 0;
}
static bool DelEvent(int epfd, int sock)
{
int n = epoll_ctl(epfd, EPOLL_CTL_DEL, sock, nullptr);// 删除指定socket
return n == 0;
}
static int LoopOnce(int epfd, struct epoll_event revs[], int num)
{
// 单次wait的调用,从数组里面取回就绪的文件描述符
int n = epoll_wait(epfd, revs, num, -1);
if(n == -1)
{
cout<<"epoll_wait : %d : %s" <<errno<< ':'<< strerror(errno)<<endl;
}
return n;
}
};
Sock.hpp
有关套接字初始化,绑定,监听,接收
#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <cerrno>
#include <cassert>
class Sock
{
public:
static int SocketInit()
{
int listenSock = socket(PF_INET, SOCK_STREAM, 0);
if (listenSock < 0)
{
exit(1);
}
int opt = 1;
setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
return listenSock;
}
static void Bind(int socket, uint16_t port)
{
struct sockaddr_in local; // 用户栈
memset(&local, 0, sizeof local);
local.sin_family = PF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY;
// 2.2 本地socket信息,写入sock_对应的内核区域
if (bind(socket, (const struct sockaddr *)&local, sizeof local) < 0)
{
exit(2);
}
}
static void Listen(int socket,int gbacklog)
{
if (listen(socket, gbacklog) < 0)
{
exit(3);
}
}
static int Accept(int socket, std::string *clientip, uint16_t *clientport)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int serviceSock = accept(socket, (struct sockaddr *)&peer, &len);
if (serviceSock < 0)
{
// 获取链接失败
return -1;
}
if(clientport) *clientport = ntohs(peer.sin_port);
if(clientip) *clientip = inet_ntoa(peer.sin_addr);
return serviceSock;
}
};
Protocol.hpp
有关序列化反序列化协议
#pragma once
#include <iostream>
#include <vector>
#include <cstring>
#include <string>
#include <cstdio>
#define SEP 'X'
#define SEP_LEN sizeof(SEP)
#define CRLF "\r\n"
#define CRLF_LEN strlen(CRLF)
#define SPACE " "
#define SPACE_LEN strlen(SPACE)
// 分离独立报文
void PackageSplit(std::string &inbuffer, std::vector&