day14-完善Connection类
在之前的编写中,服务器只能完成单一任务,今天我们将改写服务器为一个能够由用户自定义业务的通用网络库。
今天的任务主要在Connection中完成,并且今天我们要将项目整体展示出来(之前将业务工程化后,对于读者项目整体变得不清晰了)。
1、错误检测机制
//声明
void ErrorIf(bool condition, const char *msg);
//实现
void ErrorIf(bool condition, const char *errmsg) {
if (condition) {
perror(errmsg);
exit(EXIT_FAILURE);
}
}
2、Macros
定义了一些宏,用以控制类是否可移动复制等操作。
ASSERT(expr, message):如果expr的值为false,则打印信息message。
UNREACHABLE(message):表示某个程序路径是不可达的,即程序不应该执行到这个位置,如果达到了则抛出错误并打印信息message。
#define DISALLOW_COPY(cname) \
cname(const cname &) = delete; /* NOLINT */ \
cname &operator=(const cname &) = delete; /* NOLINT */
#define DISALLOW_MOVE(cname) \
cname(cname &&) = delete; /* NOLINT */ \
cname &operator=(cname &&) = delete; /* NOLINT */
#define DISALLOW_COPY_AND_MOVE(cname) \
DISALLOW_COPY(cname); \
DISALLOW_MOVE(cname);
#define ASSERT(expr, message) assert((expr) && (message))
#define UNREACHABLE(message) throw std::logic_error(message)
3、Socket
在之前的工作中,我们将地址创建同Socket创建放到了一个文件中,通过声明查看发现本次多了一个Connect的重写以及IsNonBlocking方法。注意,这两个类都是禁止复制移动的,这样可以提高调用效率。
class InetAddress {
public:
InetAddress();
InetAddress(const char *ip, uint16_t port);
~InetAddress() = default;
DISALLOW_COPY_AND_MOVE(InetAddress);
void SetAddr(sockaddr_in addr);
sockaddr_in GetAddr();
const char *GetIp();
uint16_t GetPort();
private:
struct sockaddr_in addr_ {};
};
class Socket {
private:
int fd_{-1};
public:
Socket();
explicit Socket(int fd);
~Socket();
DISALLOW_COPY_AND_MOVE(Socket);
void Bind(InetAddress *addr);
void Listen();
int Accept(InetAddress *addr);
void Connect(InetAddress *addr);
void Connect(const char *ip, uint16_t port);
void SetNonBlocking();
bool IsNonBlocking();
int GetFd();
};
之后我们通过实现的代码来理解发生了哪些变化以及多了那些能力。首先在实现中看到的是IsNonBlocking方法,这个方法是用来检查Socket是否是非阻塞模式,是的话返回true;接下来是void Connect(const char *ip, uint16_t port)方法,从实现来看该方法将创建地址同创建Socket做了结合。
Socket::Socket() {
fd_ = socket(AF_INET, SOCK_STREAM, 0);
ErrorIf(fd_ == -1, "socket create error");
}
Socket::Socket(int fd) : fd_(fd) { ErrorIf(fd_ == -1, "socket create error"); }
Socket::~Socket() {
if (fd_ != -1) {
close(fd_);
fd_ = -1;
}
}
void Socket::Bind(InetAddress *addr) {
struct sockaddr_in tmp_addr = addr->GetAddr();
ErrorIf(bind(fd_, (sockaddr *)&tmp_addr, sizeof(tmp_addr)) == -1, "socket bind error");
}
void Socket::Listen() { ErrorIf(::listen(fd_, SOMAXCONN) == -1, "socket listen error"); }
void Socket::SetNonBlocking() { fcntl(fd_, F_SETFL, fcntl(fd_, F_GETFL) | O_NONBLOCK); }
bool Socket::IsNonBlocking() { return (fcntl(fd_, F_GETFL) & O_NONBLOCK) != 0; }
int Socket::Accept(InetAddress *addr) {
// for server socket
int clnt_sockfd = -1;
struct sockaddr_in tmp_addr {};
socklen_t addr_len = sizeof(tmp_addr);
if (IsNonBlocking()) {
while (true) {
clnt_sockfd = accept(fd_, (sockaddr *)&tmp_addr, &addr_len);
if (clnt_sockfd == -1 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {
// printf("no connection yet\n");
continue;
}
if (clnt_sockfd == -1) {
ErrorIf(true, "socket accept error");
} else {
break;
}
}
} else {
clnt_sockfd = accept(fd_, (sockaddr *)&tmp_addr, &addr_len);
ErrorIf(clnt_sockfd == -1, "socket accept error");
}
addr->SetAddr(tmp_addr);
return clnt_sockfd;
}
void Socket::Connect(InetAddress *addr) {
// for client socket
struct sockaddr_in tmp_addr = addr->GetAddr();
if (fcntl(fd_, F_GETFL) & O_NONBLOCK) {
while (true) {
int ret = connect(fd_, (sockaddr *)&tmp_addr, sizeof(tmp_addr));
if (ret == 0) {
break;
}
if (ret == -1 && (errno == EINPROGRESS)) {
continue;
}
if (ret == -1) {
ErrorIf(true, "socket connect error");
}
}
} else {
ErrorIf(connect(fd_, (sockaddr *)&tmp_addr, sizeof(tmp_addr)) == -1, "socket connect error");
}
}
void Socket::Connect(const char *ip, uint16_t port) {
InetAddress *addr = new InetAddress(ip, port);
Connect(addr);
delete addr;
}
int Socket::GetFd() { return fd_; }
InetAddress::InetAddress() = default;
InetAddress::InetAddress(const char *ip, uint16_t port) {
memset(&addr_, 0, sizeof(addr_));
addr_.sin_family = AF_INET;
addr_.sin_addr.s_addr = inet_addr(ip);
addr_.sin_port = htons(port);
}
void InetAddress::SetAddr(sockaddr_in addr) { addr_ = addr; }
sockaddr_in InetAddress::GetAddr() { return addr_; }
const char *InetAddress::GetIp() { return inet_ntoa(addr_.sin_addr); }
uint16_t InetAddress::GetPort() { return ntohs(addr_.sin_port); }
4、Epoll
声明中未发现改变。
class Channel;
class Epoll {
public:
Epoll();
~Epoll();
DISALLOW_COPY_AND_MOVE(Epoll);
void UpdateChannel(Channel *ch);
void DeleteChannel(Channel *ch);
std::vector<Channel *> Poll(int timeout = -1);
private:
int epfd_{1};
struct epoll_event *events_{nullptr};
};
实现同样未发生变化。
Epoll::Epoll() {
epfd_ = epoll_create1(0);
ErrorIf(epfd_ == -1, "epoll create error");
events_ = new epoll_event[MAX_EVENTS];
memset(events_, 0, sizeof(*events_) * MAX_EVENTS);
}
Epoll::~Epoll() {
if (epfd_ != -1) {
close(epfd_);
epfd_ = -1;
}
delete[] events_;
}
std::vector<Channel *> Epoll::Poll(int timeout) {
std::vector<Channel *> active_channels;
int nfds = epoll_wait(epfd_, events_, MAX_EVENTS, timeout);
ErrorIf(nfds == -1, "epoll wait error");
for (int i = 0; i < nfds; ++i) {
Channel *ch = (Channel *)events_[i].data.ptr;
ch->SetReadyEvents(events_[i].events);
active_channels.push_back(ch);
}
return active_channels;
}
void Epoll::UpdateChannel(Channel *ch) {
int fd = ch->GetFd();
struct epoll_event ev {};
ev.data.ptr = ch;
ev.events = ch->GetListenEvents();
if (!ch->GetInEpoll()) {
ErrorIf(epoll_ctl(epfd_, EPOLL_CTL_ADD, fd, &ev) == -1, "epoll add error");
ch->SetInEpoll();
} else {
ErrorIf(epoll_ctl(epfd_, EPOLL_CTL_MOD, fd, &ev) == -1, "epoll modify error");
}
}
void Epoll::DeleteChannel(Channel *ch) {
int fd = ch->GetFd();
ErrorIf(epoll_ctl(epfd_, EPOLL_CTL_DEL, fd, nullptr) == -1, "epoll delete error");
ch->SetInEpoll(false);
}
5、Channel
声明未发生变化。
class Socket;
class EventLoop;
class Channel {
public:
Channel(EventLoop *loop, int fd);
~Channel();
DISALLOW_COPY_AND_MOVE(Channel);
void HandleEvent();
void EnableRead();
int GetFd();
uint32_t GetListenEvents();
uint32_t GetReadyEvents();
bool GetInEpoll();
void SetInEpoll(bool in = true);
void UseET();
void SetReadyEvents(uint32_t ev);
void SetReadCallback(std::function<void()> const &callback);
private:
EventLoop *loop_;
int fd_;
uint32_t listen_events_;
uint32_t ready_events_;
bool in_epoll_;
std::function<void()> read_callback_;
std::function<void()> write_callback_;
};
实现未发生变化
Channel::Channel(EventLoop *loop, int fd)
: loop_(loop), fd_(fd), listen_events_(0), ready_events_(0), in_epoll_(false) {}
Channel::~Channel() {
if (fd_ != -1) {
close(fd_);
fd_ = -1;
}
}
void Channel::HandleEvent() {
if (ready_events_ & (EPOLLIN | EPOLLPRI)) {
read_callback_();
}
if (ready_events_ & (EPOLLOUT)) {
write_callback_();
}
}
void Channel::EnableRead() {
listen_events_ |= EPOLLIN | EPOLLPRI;
loop_->UpdateChannel(this);
}
void Channel::UseET() {
listen_events_ |= EPOLLET;
loop_->UpdateChannel(this);
}
int Channel::GetFd() { return fd_; }
uint32_t Channel::GetListenEvents() { return listen_events_; }
uint32_t Channel::GetReadyEvents() { return ready_events_; }
bool Channel::GetInEpoll() { return in_epoll_; }
void Channel::SetInEpoll(bool in) { in_epoll_ = in; }
void Channel::SetReadyEvents(uint32_t ev) { ready_events_ = ev; }
void Channel::SetReadCallback(std::function<void()> const &callback) { read_callback_ = callback; }
6、EventLoop
声明未发生变化
class Epoll;
class Channel;
class EventLoop {
public:
EventLoop();
~EventLoop();
DISALLOW_COPY_AND_MOVE(EventLoop);
void Loop();
void UpdateChannel(Channel *ch);
private:
Epoll *epoll_{nullptr};
bool quit_{false};
};
实现未发生变化。
class Epoll;
class Channel;
class EventLoop {
public:
EventLoop();
~EventLoop();
DISALLOW_COPY_AND_MOVE(EventLoop);
void Loop();
void UpdateChannel(Channel *ch);
private:
Epoll *epoll_{nullptr};
bool quit_{false};
};
7、Acceptor
声明未发生改变
class EventLoop;
class Socket;
class Channel;
class Acceptor {
public:
explicit Acceptor(EventLoop *loop);
~Acceptor();
DISALLOW_COPY_AND_MOVE(Acceptor);
void AcceptConnection();
void SetNewConnectionCallback(std::function<void(Socket *)> const &callback);
private:
EventLoop *loop_;
Socket *sock_;
Channel *channel_;
std::function<void(Socket *)> new_connection_callback_;
};
实现未发生变化
Acceptor::Acceptor(EventLoop *loop) : loop_(loop), sock_(nullptr), channel_(nullptr) {
sock_ = new Socket();
InetAddress *addr = new InetAddress("127.0.0.1", 1234);
sock_->Bind(addr);
// sock->setnonblocking(); acceptor使用阻塞式IO比较好
sock_->Listen();
channel_ = new Channel(loop_, sock_->GetFd());
std::function<void()> cb = std::bind(&Acceptor::AcceptConnection, this);
channel_->SetReadCallback(cb);
channel_->EnableRead();
delete addr;
}
Acceptor::~Acceptor() {
delete channel_;
delete sock_;
}
void Acceptor::AcceptConnection() {
InetAddress *clnt_addr = new InetAddress();
Socket *clnt_sock = new Socket(sock_->Accept(clnt_addr));
printf("new client fd %d! IP: %s Port: %d\n", clnt_sock->GetFd(), clnt_addr->GetIp(), clnt_addr->GetPort());
clnt_sock->SetNonBlocking();
new_connection_callback_(clnt_sock);
delete clnt_addr;
}
void Acceptor::SetNewConnectionCallback(std::function<void(Socket *)> const &callback) {
new_connection_callback_ = callback;
}
8、Connection
由于需要处理不同的事件,而之前Connection的作用是处理事件并作出空间回收这个行为的,而今天的目的是能够使服务器根据事件类型做出相应的行为,那么这里一定是我们修改的重点。先来看声明,明显看出来相对于之前仅有读事件的声明复杂很多。
首先多了一个枚举类型State,首先推测其是一些标志位根据标志位做出不同的行为;原本处理事件的方法Echo也被分离为Read和Write;之后设置两个回调函数SetDeleteConnectionCallback、SetOnConnectCallback,推测一个是删除连接的回调函数另一个是处理事件的回调函数;GetState用以获取通道状态;GetReadBuffer、GetSendBuffer、GetlineSendBuffer用以获取不同的缓冲区;OnConnect推测是用来处理事件的;ReadNonBlocking、WriteNonBlocking、ReadBlocking、WriteBlocking用以设置不同的读取方式(阻塞/非阻塞)。
class EventLoop;
class Socket;
class Channel;
class Buffer;
class Connection {
public:
enum State {
Invalid = 1,
Handshaking,
Connected,
Closed,
Failed,
};
Connection(EventLoop *loop, Socket *sock);
~Connection();
DISALLOW_COPY_AND_MOVE(Connection);
void Read();
void Write();
void SetDeleteConnectionCallback(std::function<void(Socket *)> const &callback);
void SetOnConnectCallback(std::function<void(Connection *)> const &callback);
State GetState();
void Close();
void SetSendBuffer(const char *str);
Buffer *GetReadBuffer();
const char *ReadBuffer();
Buffer *GetSendBuffer();
const char *SendBuffer();
void GetlineSendBuffer();
Socket *GetSocket();
void OnConnect(std::function<void()> fn);
private:
EventLoop *loop_;
Socket *sock_;
Channel *channel_{nullptr};
State state_{State::Invalid};
Buffer *read_buffer_{nullptr};
Buffer *send_buffer_{nullptr};
std::function<void(Socket *)> delete_connectioin_callback_;
std::function<void(Connection *)> on_connect_callback_;
void ReadNonBlocking();
void WriteNonBlocking();
void ReadBlocking();
void WriteBlocking();
};
首先来看构造和析构函数的实现,构造函数中将状态设置为连接状态。
Connection::Connection(EventLoop *loop, Socket *sock) : loop_(loop), sock_(sock) {
if (loop_ != nullptr) {
channel_ = new Channel(loop_, sock->GetFd());
channel_->EnableRead();
channel_->UseET();
}
read_buffer_ = new Buffer();
send_buffer_ = new Buffer();
state_ = State::Connected;
}
Connection::~Connection() {
if (loop_ != nullptr) {
delete channel_;
}
delete sock_;
delete read_buffer_;
delete send_buffer_;
}
之后是Read和Write,首先判断通道是否连接,如果不处于连接状态则返回错误,之后清空各自缓存,按照设置的IO方式进行数据读取或写入,注意缓冲区清空的时机,读取前要清空读缓冲,写入后将缓冲清空,防止之前数据影响。
void Connection::Read() {
ASSERT(state_ == State::Connected, "connection state is disconnected!");
read_buffer_->Clear();
if (sock_->IsNonBlocking()) {
ReadNonBlocking();
} else {
ReadBlocking();
}
}
void Connection::Write() {
ASSERT(state_ == State::Connected, "connection state is disconnected!");
if (sock_->IsNonBlocking()) {
WriteNonBlocking();
} else {
WriteBlocking();
}
send_buffer_->Clear();
}
之后是对阻塞和非阻塞两种读取方式进行设计,这里能够更加明确的理解两者之间的区别。
在阻塞读取的过程中首先需要获得需要处理数据的长度,并设置对应长度的临时缓冲区,将数据读取到临时缓冲区,在这个过程中不能进行其他任务,之后将缓冲区中的数据添加到读缓冲区中。
在非阻塞读取的过程中则无所谓临时缓冲区的大小,数据只要准备好了就可以读入。
void Connection::ReadBlocking() {
int sockfd = sock_->GetFd();
unsigned int rcv_size = 0;
socklen_t len = sizeof(rcv_size);
getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcv_size, &len);
char buf[rcv_size];
ssize_t bytes_read = read(sockfd, buf, sizeof(buf));
if (bytes_read > 0) {
read_buffer_->Append(buf, bytes_read);
} else if (bytes_read == 0) {
printf("read EOF, blocking client fd %d disconnected\n", sockfd);
state_ = State::Closed;
} else if (bytes_read == -1) {
printf("Other error on blocking client fd %d\n", sockfd);
state_ = State::Closed;
}
}
void Connection::ReadNonBlocking() {
int sockfd = sock_->GetFd();
char buf[1024]; // 这个buf大小无所谓
while (true) { // 使用非阻塞IO,读取客户端buffer,一次读取buf大小数据,直到全部读取完毕
memset(buf, 0, sizeof(buf));
ssize_t bytes_read = read(sockfd, buf, sizeof(buf));
if (bytes_read > 0) {
read_buffer_->Append(buf, bytes_read);
} else if (bytes_read == -1 && errno == EINTR) { // 程序正常中断、继续读取
printf("continue reading\n");
continue;
} else if (bytes_read == -1 &&
((errno == EAGAIN) || (errno == EWOULDBLOCK))) { // 非阻塞IO,这个条件表示数据全部读取完毕
break;
} else if (bytes_read == 0) { // EOF,客户端断开连接
printf("read EOF, client fd %d disconnected\n", sockfd);
state_ = State::Closed;
break;
} else {
printf("Other error on client fd %d\n", sockfd);
state_ = State::Closed;
break;
}
}
}
在写入缓冲区的过程中如果使用阻塞方式在send_buffer_数据大于TCP写缓冲区的情况中有可能会出现问题,使用无阻塞的情况需要计算剩余字符数量并一步一步将数据写入到缓冲区。
void Connection::WriteBlocking() {
// 没有处理send_buffer_数据大于TCP写缓冲区的情况,可能会有bug
int sockfd = sock_->GetFd();
ssize_t bytes_write = write(sockfd, send_buffer_->ToStr(), send_buffer_->Size());
if (bytes_write == -1) {
printf("Other error on blocking client fd %d\n", sockfd);
state_ = State::Closed;
}
}
void Connection::WriteNonBlocking() {
int sockfd = sock_->GetFd();
char buf[send_buffer_->Size()];
memcpy(buf, send_buffer_->ToStr(), send_buffer_->Size());
int data_size = send_buffer_->Size();
int data_left = data_size;
while (data_left > 0) {
ssize_t bytes_write = write(sockfd, buf + data_size - data_left, data_left);
if (bytes_write == -1 && errno == EINTR) {
printf("continue writing\n");
continue;
}
if (bytes_write == -1 && errno == EAGAIN) {
break;
}
if (bytes_write == -1) {
printf("Other error on client fd %d\n", sockfd);
state_ = State::Closed;
break;
}
data_left -= bytes_write;
}
}
之后就是一些设置/获取属性的方法。
void Connection::Close() { delete_connectioin_callback_(sock_); }
Connection::State Connection::GetState() { return state_; }
void Connection::SetSendBuffer(const char *str) { send_buffer_->SetBuf(str); }
Buffer *Connection::GetReadBuffer() { return read_buffer_; }
const char *Connection::ReadBuffer() { return read_buffer_->ToStr(); }
Buffer *Connection::GetSendBuffer() { return send_buffer_; }
const char *Connection::SendBuffer() { return send_buffer_->ToStr(); }
void Connection::SetDeleteConnectionCallback(std::function<void(Socket *)> const &callback) {
delete_connectioin_callback_ = callback;
}
void Connection::SetOnConnectCallback(std::function<void(Connection *)> const &callback) {
on_connect_callback_ = callback;
channel_->SetReadCallback([this]() { on_connect_callback_(this); });
}
void Connection::GetlineSendBuffer() { send_buffer_->Getline(); }
Socket *Connection::GetSocket() { return sock_; }
但是目前仍有一些疑问未解除,on_connect_callback_、delete_connectioin_callback_ 分别是什么作用?接下来我们带着这些问题继续向下读。
9、Buffer
声明没什么变化
class Buffer {
public:
Buffer() = default;
~Buffer() = default;
DISALLOW_COPY_AND_MOVE(Buffer);
void Append(const char *_str, int _size);
ssize_t Size();
const char *ToStr();
void Clear();
void Getline();
void SetBuf(const char *buf);
private:
std::string buf_;
};
实现也没什么变化
void Buffer::Append(const char *str, int size) {
for (int i = 0; i < size; ++i) {
if (str[i] == '\0') {
break;
}
buf_.push_back(str[i]);
}
}
ssize_t Buffer::Size() { return buf_.size(); }
const char *Buffer::ToStr() { return buf_.c_str(); }
void Buffer::Clear() { buf_.clear(); }
void Buffer::Getline() {
buf_.clear();
std::getline(std::cin, buf_);
}
void Buffer::SetBuf(const char *buf) {
buf_.clear();
buf_.append(buf);
}
10、ThreadPool
声明没发生变化
class ThreadPool {
public:
explicit ThreadPool(unsigned int size = std::thread::hardware_concurrency());
~ThreadPool();
DISALLOW_COPY_AND_MOVE(ThreadPool);
template <class F, class... Args>
auto Add(F &&f, Args &&... args) -> std::future<typename std::result_of<F(Args...)>::type>;
private:
std::vector<std::thread> workers_;
std::queue<std::function<void()>> tasks_;
std::mutex queue_mutex_;
std::condition_variable condition_variable_;
bool stop_{false};
};
// 不能放在cpp文件,C++编译器不支持模版的分离编译
template <class F, class... Args>
auto ThreadPool::Add(F &&f, Args &&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
using return_type = typename std::result_of<F(Args...)>::type;
auto task =
std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
std::future<return_type> res = task->get_future();
{
std::unique_lock<std::mutex> lock(queue_mutex_);
if (stop_) {
throw std::runtime_error("enqueue on stopped ThreadPool");
}
tasks_.emplace_back([task]() { (*task)(); });
}
condition_variable_.notify_one();
return res;
}
实现也没变化,注意这里如果池中没有任务,则将卡在死循环中,一旦有任务添加进来就执行。
ThreadPool::ThreadPool(unsigned int size) {
for (unsigned int i = 0; i < size; ++i) {
workers_.emplace_back(std::thread([this]() {
while (true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(queue_mutex_);
condition_variable_.wait(lock, [this]() { return stop_ || !tasks_.empty(); });
if (stop_ && tasks_.empty()) {
return;
}
task = tasks_.front();
tasks_.pop();
}
task();
}
}));
}
}
ThreadPool::~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queue_mutex_);
stop_ = true;
}
condition_variable_.notify_all();
for (std::thread &th : workers_) {
if (th.joinable()) {
th.join();
}
}
}
11、服务器类
服务器类中多了on_connect_callback_属性和OnConnect根据之前推测这个是处理事件的部分。
class EventLoop;
class Socket;
class Acceptor;
class Connection;
class ThreadPool;
class Server {
private:
EventLoop *main_reactor_;
Acceptor *acceptor_;
std::map<int, Connection *> connections_;
std::vector<EventLoop *> sub_reactors_;
ThreadPool *thread_pool_;
std::function<void(Connection *)> on_connect_callback_;
public:
explicit Server(EventLoop *loop);
~Server();
DISALLOW_COPY_AND_MOVE(Server);
void NewConnection(Socket *sock);
void DeleteConnection(Socket *sock);
void OnConnect(std::function<void(Connection *)> fn);
};
在实现中,差别不大,对on_connect_callback_进行了应用,将其放在Channel进行处理;这里应用了std::move,这是因为这个变量在赋值后不再需要,指向的资源可以被转移所有权,因此用std::move转换为右值。
Server::Server(EventLoop *loop) : main_reactor_(loop), acceptor_(nullptr), thread_pool_(nullptr) {
acceptor_ = new Acceptor(main_reactor_);
std::function<void(Socket *)> cb = std::bind(&Server::NewConnection, this, std::placeholders::_1);
acceptor_->SetNewConnectionCallback(cb);
int size = static_cast<int>(std::thread::hardware_concurrency());
thread_pool_ = new ThreadPool(size);
for (int i = 0; i < size; ++i) {
sub_reactors_.push_back(new EventLoop());
}
for (int i = 0; i < size; ++i) {
std::function<void()> sub_loop = std::bind(&EventLoop::Loop, sub_reactors_[i]);
thread_pool_->Add(std::move(sub_loop));
}
}
Server::~Server() {
delete acceptor_;
delete thread_pool_;
}
void Server::NewConnection(Socket *sock) {
ErrorIf(sock->GetFd() == -1, "new connection error");
uint64_t random = sock->GetFd() % sub_reactors_.size();
Connection *conn = new Connection(sub_reactors_[random], sock);
std::function<void(Socket *)> cb = std::bind(&Server::DeleteConnection, this, std::placeholders::_1);
conn->SetDeleteConnectionCallback(cb);
conn->SetOnConnectCallback(on_connect_callback_);
connections_[sock->GetFd()] = conn;
}
void Server::DeleteConnection(Socket *sock) {
int sockfd = sock->GetFd();
auto it = connections_.find(sockfd);
if (it != connections_.end()) {
Connection *conn = connections_[sockfd];
connections_.erase(sockfd);
delete conn;
conn = nullptr;
}
}
void Server::OnConnect(std::function<void(Connection *)> fn) { on_connect_callback_ = std::move(fn); }
测试程序
程序变得简单,由于我们在Connection中将write和read封装成了方法,因此简便了测试程序。
void OneClient(int msgs, int wait) {
Socket *sock = new Socket();
sock->Connect("127.0.0.1", 1234);
Connection *conn = new Connection(nullptr, sock);
sleep(wait);
int count = 0;
while (count < msgs) {
conn->SetSendBuffer("I'm client!");
conn->Write();
if (conn->GetState() == Connection::State::Closed) {
conn->Close();
break;
}
conn->Read();
std::cout << "msg count " << count++ << ": " << conn->ReadBuffer() << std::endl;
}
delete conn;
}
主程序
int main() {
EventLoop *loop = new EventLoop();
Server *server = new Server(loop);
server->OnConnect([](Connection *conn) {
conn->Read();
if (conn->GetState() == Connection::State::Closed) {
conn->Close();
return;
}
std::cout << "Message from client " << conn->GetSocket()->GetFd() << ": " << conn->ReadBuffer() << std::endl;
conn->SetSendBuffer(conn->ReadBuffer());
conn->Write();
});
loop->Loop();
delete server;
delete loop;
return 0;
}
总结一下
今天将事件处理的部分封装成了Connection的方法,通过Connection对事件进行处理,并根据需求调用不同的处理函数,业务逻辑由Connection控制转交到了自定义控制。
在之前的Echo中可以完成服务器读取并回显到客户端,而现在可以自定义逻辑,如
int main() {
Socket *sock = new Socket();
sock->Connect("127.0.0.1", 1234);
Connection *conn = new Connection(nullptr, sock);
while (true) {
conn->GetlineSendBuffer();
conn->Write();
if (conn->GetState() == Connection::State::Closed) {
conn->Close();
break;
}
conn->Read();
std::cout << "Message from server: " << conn->ReadBuffer() << std::endl;
}
delete conn;
return 0;
}
首先,这里没有服务器和事件循环,仅仅使用了一个裸的Connection类来表示从客户端到服务器的连接。所以此时Read()表示从服务器读取到客户端,而Write()表示从服务器写入到客户端,和之前服务器的Conneciont类方向完全相反。这样Connection就可以同时表示Server->Client或者Client->Server的连接,不需要新建一个类来区分,大大提高了通用性和代码复用。
其次,客户端Connection没有绑定事件循环,所以将第一个参数设置为nullptr表示不使用事件循环,这时将不会有Channel类创建来分配到EventLoop,表示使用一个裸的Connection。因此业务逻辑也不用设置服务器回调函数,而是直接写在客户端代码中。