网络编程(5)—— Reactor实现(v1)

文章目录

回顾

先回顾一下我们整个的实现框架,所以第一步应该是实现的是------封装系统调用,另外为了方便打印调试信息,可以把日志系统用单例模式先实现出来。

关于用单例模式实现日志系统我们在之前的文章中已经实现过了,这里就不过多赘述了。详情可以跳转到该文章中查看

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中的数据成员和成员函数也要随之变动。

相关推荐
YWL38 分钟前
OpenLayers测距测面:完整测量工具
前端·javascript·vue.js·信息可视化·openlayers
拾光Ծ1 小时前
Linux五种I/O模型与fcntl非阻塞控制
java·linux·服务器·i/o模型
半旧5181 小时前
【Java Web02】WEB服务器内部实现技术揭秘
java·服务器·前端
张宇Joaquin1 小时前
高性能计算之MPI——集合通信漫谈
服务器·microsoft·asp.net
数据知道1 小时前
网络安全实战:信息收集实战——子域名、端口、指纹、社工信息聚合
网络·安全·web安全·网络安全·servlet
超级赛博搬砖工1 小时前
什么是原生IP与广播IP?如何区分?原生IP有什么优势?
网络
fiveym11 小时前
01 - iPXE + Clonezilla 网络装机原理解析
linux·运维·服务器·网络
虎头金猫11 小时前
如何在群晖NAS上通过Docker部署CloudSaver?群晖部署CloudSaver教程|聚合资源搜索并实现远程访问
运维·服务器·网络·python·docker·容器·pandas
dog25011 小时前
网络优化,还是降低各阶矩
服务器·网络·php