文章目录
回顾

先回顾一下我们整个的实现框架,所以第一步应该是实现的是------封装系统调用,另外为了方便打印调试信息,可以把日志系统用单例模式先实现出来。
关于用单例模式实现日志系统我们在之前的文章中已经实现过了,这里就不过多赘述了。详情可以跳转到该文章中查看
Reactor网络模型本质上就是使用epoll的并发网络模型,通过连接器连接到服务器网络,然后注册三个事件(对应epoll的三个接口函数),通过事件驱动(事件循环和事件分发)来响应客户端的请求。
Reactor_v1实现
在写代码之前要整理一下思绪,框架图中已经很明确的写出了有哪些类,我们在第一个版本的内容中实际上就是对系统调用的封装,下面就画一个类图,整理一下具体实现的内容。

Acceptor类中可以将地址复用、端口复用、bind、listen这些写法固定的流程封装在一个ready函数中。关于其它类行该可以比较容易的理清思路。需要着重考虑的是IO操作,因为使用send/recv或write/read一次处理的字符数量是由上限的。所以我们应该设置一个合适大小的缓冲,用来接收数据流。
Socket类实现
头文件
cpp
/**
* 封装文件描述符
*/
#ifndef _SOCKET_H
#define _SOCKET_H
#include<sys/types.h>
#include<sys/socket.h>
class Socket {
public:
Socket();
/**
* @param fd
* 禁止隐式转换防止出现问题
*/
explicit Socket(int fd);
~Socket();
int getFd();
private:
int _fd;
};
#endif //_SOCKET_H
源文件
cpp
/**
* 封装文件描述符
*/
#include "Socket.h"
#include <stdio.h>
#include <stdlib.h>
/**
* Socket implementation
*/
Socket::Socket() {
_fd = ::socket(AF_INET, SOCK_STREAM, 0);
if(_fd == -1){
perror("socket error");
exit(EXIT_FAILURE);
}
}
/**
* @param fd
*/
Socket::Socket(int fd):_fd(fd){
}
Socket::~Socket() {
}
/**
* @return int
*/
int Socket::getFd() {
return _fd;
}
InetAddr类的实现
头文件
cpp
/**
* 地址相关操作
*/
#ifndef _INETADDRESS_H
#define _INETADDRESS_H
#include<string>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<sys/socket.h>
#include<arpa/inet.h>
using std::string;
class InetAddress {
public:
/**
* @param ip
* @param port
*/
InetAddress(const string & ip, unsigned short port);
/**
* @param addr
*/
InetAddress(const struct sockaddr_in & addr);
~InetAddress();
string getIp();
unsigned short getPort();
const struct sockaddr_in * getInetAddressPtr();
private:
struct sockaddr_in _addr;
};
#endif //_INETADDRESS_H
源文件
cpp
/**
* 地址相关操作
*/
#include "InetAddress.h"
/**
* InetAddress implementation
*/
/**
* @param ip
* @param port
*/
InetAddress::InetAddress(const string & ip, unsigned short port) {
_addr.sin_family = AF_INET;
_addr.sin_port = htons(port);
_addr.sin_addr.s_addr = ::inet_addr(ip.c_str());
}
/**
* @param addr
*/
InetAddress::InetAddress(const struct sockaddr_in & addr):_addr(addr) {
}
InetAddress::~InetAddress() {
}
/**
* @return string
*/
string InetAddress::getIp() {
string temp = ::inet_ntoa(_addr.sin_addr);
return temp;
}
/**
* @return unsigned short
*/
unsigned short InetAddress::getPort() {
return ::ntohs(_addr.sin_port);
}
/**
* @return const struct sockaddr_in *
*/
const struct sockaddr_in * InetAddress::getInetAddressPtr() {
return &_addr;
}
Acceptor类的实现
头文件
cpp
/**
* 连接器的实现
*/
#ifndef _ACCEPTOR_H
#define _ACCEPTOR_H
#include"InetAddress.h"
#include"Socket.h"
using std::string;
class Acceptor {
public:
/**
* @param ip
* @param port
*/
Acceptor(const string & ip, unsigned short port);
~Acceptor();
void ready();
int accept();
private:
Socket _sock;
InetAddress _addr;
void setReuseAddr();
void setReusePort();
void bind();
void listen();
};
#endif //_ACCEPTOR_H
源文件
cpp
/**
* 连接器的实现
*/
#include "Acceptor.h"
/**
* Acceptor implementation
*/
/**
* @param ip
* @param port
*/
Acceptor::Acceptor(const string & ip, unsigned short port):
_sock(),
_addr(ip, port){
}
Acceptor::~Acceptor() {
}
/**
* @return void
*/
void Acceptor::ready() {
setReuseAddr();
setReusePort();
bind();
listen();
return;
}
/**
* @return int
*/
int Acceptor::accept() {
int clientfd = ::accept(_sock.getFd(), NULL, NULL);
if(clientfd == -1){
perror("accept error");
return -1;
}
return clientfd;
}
/**
* @return void
*/
void Acceptor::setReuseAddr() {
int opt = 1;
if(::setsockopt(_sock.getFd(), SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1){
perror("reuse sockaddr error");
exit(EXIT_FAILURE);
}
return;
}
/**
* @return void
*/
void Acceptor::setReusePort() {
int opt = 1;
if(::setsockopt(_sock.getFd(), SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) == -1){
perror("reuse port error");
exit(EXIT_FAILURE);
}
return;
}
/**
* @return void
*/
void Acceptor::bind() {
if(::bind(_sock.getFd(), (struct sockaddr *)_addr.getInetAddressPtr(), sizeof(struct sockaddr)) == -1){
perror("bind error");
exit(EXIT_FAILURE);
}
return;
}
/**
* @return void
*/
void Acceptor::listen() {
if(::listen(_sock.getFd(), 100) == -1){
perror("listen error");
exit(EXIT_FAILURE);
}
return;
}
SocketIO类的实现
头文件
cpp
/**
* IO操作
*/
#ifndef _SOCKETIO_H
#define _SOCKETIO_H
#include<unistd.h>
#include<errno.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
class SocketIO {
public:
/**
* @param fd
*/
explicit SocketIO(int fd);
~SocketIO();
/**
* @param buf
* @param len
*/
int readn(char * buf, int len);
/**
* @param buf
* @param len
*/
int readLine(char * buf, int len);
/**
* @param buf
* @param len
*/
int writen(const char * buf, int len);
private:
int _fd;
};
#endif //_SOCKETIO_H
源文件
cpp
/**
* IO操作
*/
#include "SocketIO.h"
/**
* SocketIO implementation
*/
/**
* @param fd
*/
SocketIO::SocketIO(int fd):_fd(fd) {
}
SocketIO::~SocketIO() {
if(_fd != -1){
close(_fd);
_fd = -1;
}
}
/**
* @param buf
* @param len
* @return int
*/
int SocketIO::readn(char * buf, int len) {
int remain = len; // 剩余没读的字节
int ret = 0;
char *pstr = buf;
while(remain > 0){
ret = ::read(_fd, pstr, sizeof(remain));
if(ret == -1 && errno == EINTR){
::perror("read error");
return -1;
}
else if(ret == 0){
break;
}
else{
pstr += ret;
remain -= ret;
}
}
return len - remain;
}
/**
* @param buf
* @param len
* @return int
*/
int SocketIO::readLine(char * buf, int len) {
int remain = len - 1;
int ret = 0, total = 0;
char *pstr = buf;
while(remain > 0){
ret = ::recv(_fd, pstr, remain, MSG_PEEK);
if(ret == -1 && errno == EINTR){
::perror("readLine error");
return -1;
}
else if(ret == 0){
break;
}
else{
for(int i = 0; i < ret; i++){
if(pstr[i] == '\n'){
int sz = i + 1;
readn(pstr, sz);
pstr += sz;
*pstr = '\0';
return sz + total;
}
}
readn(pstr, ret);
total += ret;
pstr += ret;
remain -= ret;
}
}
*pstr = '\0';
return total;
}
/**
* @param buf
* @param len
* @return int
*/
int SocketIO::writen(const char * buf, int len) {
int remain = len;
const char *pstr = buf;
int ret = 0;
while(remain > 0){
ret = write(_fd, pstr, remain);
if(ret == -1 && errno == EINTR){
::perror("writen error");
return -1;
}
else if(ret == 0){
break;
}
else{
pstr += ret;
remain -= ret;
}
}
return len - remain;
}
TcpConnection类的实现
头文件
cpp
/**
* 连接管理
*/
#ifndef _TCPCONNECTION_H
#define _TCPCONNECTION_H
#include<string>
#include"SocketIO.h"
using std::string;
class TcpConnection {
public:
/**
* @param fd
*/
explicit TcpConnection(int fd);
~TcpConnection();
string receive();
/**
* @param msg
*/
void send(const string & msg);
private:
SocketIO _sockIO;
};
#endif //_TCPCONNECTION_H
源文件
cpp
/**
* 连接管理
*/
#include "TcpConnection.h"
/**
* TcpConnection implementation
*/
/**
* @param fd
*/
TcpConnection::TcpConnection(int fd):_sockIO(fd) {
}
TcpConnection::~TcpConnection() {
}
/**
* @return string
*/
string TcpConnection::receive() {
char buffer[1024] = {0};
_sockIO.readLine(buffer, sizeof(buffer));
return string(buffer);
}
/**
* @param msg
* @return void
*/
void TcpConnection::send(const string & msg) {
_sockIO.writen(msg.c_str(), msg.size());
return;
}
以上就是Reactor的全部函数,完成了对系统调用的封装。下面再写一个测试文件
cpp
#include "Acceptor.h"
#include "TcpConnection.h"
#include <iostream>
using std::cout;
using std::endl;
void test0()
{
Acceptor acceptor("0.0.0.0",8080);
acceptor.ready();
TcpConnection con(acceptor.accept());
while(1)
{
/* cout << ">>recv msg from client: " << con.receive() << endl; */
/* con.send("hello,world\n"); */
string msg = con.receive();
cout << ">>recv msg from client: " << msg << endl;
con.send("msg:" + msg);
}
}
int main()
{
test0();
return 0;
}
下面再开一个终端自测一下

基本上还是符合我们预期的也就是说明,我们对于底层的封装没有了太大问题。我们的Reactor第一代基本完成了,当然可以把之前封装的log4cpp的代码替代perror()。
下面的工作就是完成核心层,事件驱动的代码,就是对epoll的封装,另外,TcpConnection是用来管理连接的,所以,当我们使用服务器采用事件驱动的形式时,TcpConnection中的数据成员和成员函数也要随之变动。