Reactor 反应堆模式

代码整体围绕 "Reactor 模式" 实现一个网络服务器(以计算器为例),各模块按职责可分为基础工具层网络通信层事件驱动层业务逻辑层四类,具体作用如下:

Listener实例化 → 创建TcpSocket(监听套接字) → 注册到Reactor的Epoller(监控新连接) → (新连接到来时) 创建Channel实例(封装客户端连接) → 注册到Reactor的Epoller(监控客户端I/O) → (客户端发数据) Reactor分发事件到Channel → Channel读取/处理/响应数据

Listener和Channel都继承于connection, Listener负责监听和接受新连接,Channel负责已建立连接的数据读写,在listener监听到有新连接到来时创建Channel,将业务cal绑定到protocol的回调事件上,用智能指针共有该匿名函数回调处理,并将fd给Rwactor,Rwactor然后将事件派发给map中事件就绪的channel I/O处理

项目总览

Channel.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include "Connection.hpp"
#include "Common.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include <sys/types.h>
#include <sys/socket.h>
#include<functional>
#include<memory>

using namespace LogModule;

#define SIZE  1024


// 普通sockfd的封装
class Channel : public Connection
{
public:
    Channel(int sockfd, const InetAddr &client)
        : _sockfd(sockfd), _client_addr(client)
    {
        SetNonBlock(sockfd);
    }
    ~Channel() {}
    int GetSockFd() override
    {
        return _sockfd;
    }

public:
   
    void Recver() override
    {   //1.while保证本轮数据读完,channel只解决读问题
        //2.保证完整报文,解决粘包问题 ---反序列化,引入协议

        // LOG(Loglevel::DEBUG) << "事件被派发到了channel模块";
        // 读到是字符串
        char buffer[SIZE];
        while (true)

        {
            buffer[0] = 0;//清空字符串
            ssize_t n = recv(_sockfd, &buffer, sizeof(buffer)-1, 0);
            if( n > 0)
            {
                //读成功
                buffer[n]= 0;
                _inbuffer+=buffer;//入队列
            }
            else if (n == 0)
            {   //  异常
                Excepter();
                return;
            }
            else
            {
                //可能本轮读完了
                if(errno ==EAGAIN || errno == EWOULDBLOCK)
                {
                    break;
                }
                else if(errno == EINTR)//被信号中断
                {
                    continue;
                }
                else
                {  //真的报错了
                    Excepter();
                    return;
                }
            }
        }
        LOG(Loglevel::DEBUG)<<"Channel :inbuffer:"<<_inbuffer;
        if(!_inbuffer.empty())
        {
          _outbuffer+=_handler(_inbuffer);
        }
        if (!_outbuffer.empty())
        {
            Sender(); // 最佳实践
            //GetOwner()->EnableReadWrite(_sockfd, true, true);
        }

    }
    void Sender() override
    {
        while (true)
        {
            ssize_t n = send(_sockfd, _outbuffer.c_str(), _outbuffer.size(), 0);
            if (n > 0)
            {
                _outbuffer.erase(0, n);
                if (_outbuffer.empty())
                    break;
            }
            else if (n == 0)
            {
                break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                if (errno == EINTR)
                    continue;
                else
                {
                    Excepter();
                    return;
                }
            }
    }
}
    void Excepter() override
    {
    }
  std::string & Inbuffer() 
  {
    return _inbuffer;
  }
  void AppendOutBuffer(const std::string &out) 
  {
    _outbuffer+=out;
  }
private:
    int _sockfd;
    std::string _inbuffer; // 缓冲区
    std::string _outbuffer;

    // client info
    InetAddr _client_addr;

    //handler_t _handler;
};

Common.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include<functional>
#include <string>
#include <cstring>
#include <memory>
#include<unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include<fcntl.h>
#include"Log.hpp"
using namespace LogModule;
enum ExitCode
{

    OK = 0,
    USAGE_ERR,
    SOCKET_ERR,
    BIND_ERR,
    LISTEN_ERR,
    CONNECT_ERR,
    FORK_ERR,
    OPEN_ERR,
    EPOLL_CREATE_ERR,
    EPOLL_CTL_ERR
};

class NoCopy
{

public:
NoCopy(){}
~NoCopy(){}
NoCopy(const NoCopy&)=delete;
const NoCopy& operator= (const NoCopy &)=delete;
};

void SetNonBlock(int fd)
{
    int fl = fcntl(fd,F_GETFL);
    if(fl < 0)
    { 
        LOG(Loglevel::DEBUG)<<"设置非阻塞失败";
        return;
       
    }
      fcntl(fd, F_SETFL,fl | O_NONBLOCK);
     LOG(Loglevel::DEBUG)<<"设置非阻塞成功";
}

int defaultport = 8080;
#define CONV(addr) ((struct sockaddr *)&addr)

Connection.hpp

cpp 复制代码
#pragma once
#include <iostream>
#include <string>
#include "InetAddr.hpp"
class Reactor;
class Connection;
using handler_t = std::function<std::string(std::string&)>;

// 封装fd, 保证给每一个fd一套缓冲
class Connection
{
public:
    Connection()
        : _events(0), _owner(nullptr)
    {
    }
    void RegisterHandler(handler_t handler) 
    {
        _handler = handler; 
    }
    virtual void Recver() = 0;
    virtual void Sender() = 0;
    virtual void Excepter() = 0;
    virtual int GetSockFd() = 0;
    void SetEvent(uint32_t events)
    {
        _events = events;
    }
   
    ~Connection() {}
    uint32_t GetEvent()
    {
        return _events;
    }
    void SetOwner(Reactor *owner)
    {
        _owner = owner;
    }
    Reactor *GetOwner()
    {
        return _owner;
    }

private:
    // 关心事件
    uint32_t _events;

    // 回指指针
    Reactor *_owner;

public:
    handler_t _handler;
};

Epoller.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <unistd.h>
#include <sys/epoll.h>
#include "Common.hpp"
#include "Log.hpp"

using namespace LogModule;
class Epoller
{

public:
    Epoller()
    :_epfd(-1)
    {
        _epfd = epoll_create(128);
        if(_epfd < 0 )
        {
            LOG(Loglevel::FATAL) << "epoll_create error!";
            exit(EPOLL_CREATE_ERR);
        }

    LOG(Loglevel::INIF) << "create epoll success!";
    }
     void AddEvent(int sockfd,uint32_t events)
     {
        struct epoll_event ev;
        ev.events=events;
        ev.data.fd=sockfd;

        int n = epoll_ctl(_epfd,EPOLL_CTL_ADD,sockfd,&ev);
        if( n < 0 )
        {
            LOG(Loglevel::ERROR)<<"epoll_ctl error!";
            return;
        }
       LOG(Loglevel::INIF)<<"epoll_ctl success!  epds:" << _epfd;
       
     }
     void DelEvent()
     {

     }
     void ModEvent()
     {

     }
     int WaitEvents(struct epoll_event revs[],int maxnum,int timeout)
     {

        int n = epoll_wait(_epfd,revs,maxnum,timeout);
        if(n < 0)
        {
            LOG(Loglevel::WARNING)<<"epoll_wait error";
        }
        else if(n ==0)
        {
            
        }
        
        return n;
     }
    ~Epoller()
    {
        if(_epfd >= 0)
        {
            close(_epfd);
        }
    }

private:
    int _epfd;


};

InetAddr.hpp

cpp 复制代码
#pragma once
#include "Common.hpp"

class InetAddr
{

public:
    InetAddr()
    {
        
    }
    InetAddr(struct sockaddr_in &addr)
    {   
        SetAddr(addr);
    }
    InetAddr(const std::string &ip,uint16_t port)
        :_ip(ip)
        ,_port(port)
    {
        //主机转网络
       memset(&_addr,0,sizeof(_addr));
       _addr.sin_family=AF_INET;
       inet_pton(AF_INET,_ip.c_str(),&_addr.sin_addr);
       _addr.sin_port=htons(_port);
       
    }
    InetAddr(uint16_t port)
        :_port(port),_ip("0")
    {   //端口转
       memset(&_addr,0,sizeof(_addr));
       _addr.sin_family=AF_INET;
       _addr.sin_addr.s_addr=INADDR_ANY;
       _addr.sin_port = htons(_port);
    }
   

    uint16_t Port() { return _port; }

    std::string Ip() { return _ip; }

    void SetAddr(struct sockaddr_in &addr) 
    {  //网络转主机
        _addr=addr;
        _port = ntohs(addr.sin_port);
        //_ip = inet_ntoa(addr.sin_addr);
        char ipbuffer[64];
        inet_ntop(AF_INET,&_addr.sin_addr,ipbuffer,sizeof(_addr));
        _ip=ipbuffer;
    }
    const struct sockaddr_in &NetAddr() { return _addr; }
    const struct sockaddr *NetAddrPtr() { return CONV(_addr); }

    socklen_t NetAddrLen() { return sizeof(_addr); }
    bool operator==(const InetAddr &addr)
    {
        return addr._ip == _ip && addr._port == _port;
    }
    std::string StringAddr()
    {
        return _ip + ":" + std::to_string(_port);
    }
    ~InetAddr() {}
private:
    struct sockaddr_in _addr;
    std::string _ip;
    uint16_t _port;
};

Listener.hpp

cpp 复制代码
#pragma once
#include <iostream>
#include <memory>
#include "Epoller.hpp"
#include "Socket.hpp"
#include "Common.hpp"
#include "Connection.hpp"
#include "Channel.hpp"

using namespace SocketModule;
// 专门用来获取新连接
class Listener : public Connection // 可以用基类指向派生类来处理

{
public:
    Listener(int port = defaultport)
        : _port(port),
          _listensock(std::make_unique<TcpSocket>())
    {
        _listensock->BUildTcpLIstenSocketMethod(_port);
        SetEvent(EPOLLIN | EPOLLET);
        SetNonBlock(_listensock->Fd());
    }

    ~Listener() {}
    int GetSockFd() override
    {
        return _listensock->Fd();
    }
    void Recver() override
    {
       
        InetAddr client;
        // 新连接就绪,可能有多个,一次性把所有的链接全部获取上来
        while (true)
        {
            int sockfd = _listensock->Accept(&client);
            if (sockfd == ACCEPT_ERR)
                break;
            else if (sockfd == ACCEPT_CONTINUE)
                continue;
            else if (sockfd == ACCEPT_DOWN)
                break;
            // 合法fd
            else
            {
                std::shared_ptr<Connection> conn = std::make_shared<Channel>(sockfd,client);
                conn->SetEvent(EPOLLIN | EPOLLET);
                if(_handler!=nullptr)
                conn->RegisterHandler(_handler);
                GetOwner()->AddConnection(conn);
            }
        }
    }

    void Sender() override
    {  }
    void Excepter() override
    { }

private:
    int _port;
    std::unique_ptr<Socket> _listensock;
};

Log.hpp

cpp 复制代码
#ifndef __LOG_HPP__
#define __LOG_HPP__

#include <iostream>
#include <string>
#include "Mutex.hpp"
#include <filesystem>
#include <fstream>
#include <memory>
#include <unistd.h>
#include <sstream>
#include<ctime>

namespace LogModule
{
    const std::string sep = "\r\n";
    using namespace  MutexModule ;
    
    // 2.刷新策略
    class LogStrategy
    {
    public:
        ~LogStrategy() = default;
        virtual void SyncLog(const std::string &message) = 0;
    };

    // 显示器刷新日志的策略
    class ConsoleLogStrategy : public LogStrategy
    {
    public:
        ConsoleLogStrategy() {}
        ~ConsoleLogStrategy() {}
        void SyncLog(const std::string &message) override
        {
            LockGuard lockguard(_mutex);
            std::cout << message << sep;
        }

    private:
        Mutex _mutex;
    };

    // 缺省文件路径以及文件本身
    const std::string defaultpath = "./log";
    const std::string defaultfile = "my.log";
    // 文件刷新日志的策略
    class FileLogStrategy : public LogStrategy
    {
    public:
        FileLogStrategy(const std::string &path = defaultpath, const std::string &file = defaultfile)
            : _path(path), _file(file)
        {
            LockGuard lockguard(_mutex);
            if (std::filesystem::exists(_path)) // 判断路径是否存在
            {
                return;
            }
            try
            {
                std::filesystem::create_directories(_path);
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << '\n';
            }
        }

        void SyncLog(const std::string &message) override
        {
            LockGuard lockguard(_mutex);
            std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;
            std::ofstream out(filename, std::ios::app); // 追加写入
            if (!out.is_open())
            {
                return;
            }
            out << message << sep;
            out.close();
        }
        ~FileLogStrategy() {}

    private:
        Mutex _mutex;
        std::string _path; // 日志文件的路径
        std::string _file; // 要打印的日志文件
    };

    // 形成日志等级
    enum class Loglevel
    {
        DEBUG,
        INIF,
        WARNING,
        ERROR,
        FATAL
    };
    std::string Level2Str(Loglevel level)
    {
        switch (level)
        {
        case Loglevel::DEBUG:
            return "DEBUG";
        case Loglevel::INIF:
            return "INIF";
        case Loglevel::WARNING:
            return "WARNING";
        case Loglevel::ERROR:
            return "ERROR";
        case Loglevel::FATAL:
            return "FATAL";
        default:
            return "UNKNOWN";
        }
    }

    std::string GetTimeStamp()
    {
       time_t cuur =time(nullptr);
       struct tm curr_tm;
       localtime_r(&cuur,&curr_tm);
       char buffer[128];
       snprintf(buffer,sizeof(buffer),"%4d-%02d-%02d %02d:%02d:%02d",
       curr_tm.tm_year+1900,
       curr_tm.tm_mon+1,
       curr_tm.tm_mday,
       curr_tm.tm_hour,
       curr_tm.tm_min,
       curr_tm.tm_sec
       );
       return buffer;
    }
    class Logger
    {
    public:
        Logger()
        {
            EnableConsoleLogStrategy();
        }
        // 选择某种策略
        // 1.文件
        void EnableFileLogStrategy()
        {
            _ffush_strategy = std::make_unique<FileLogStrategy>();
        }
        // 显示器
        void EnableConsoleLogStrategy()
        {
            _ffush_strategy = std::make_unique<ConsoleLogStrategy>();
        }

        
        // 表示的是未来的一条日志
        class LogMessage
        {
        public:
            LogMessage(Loglevel &level, std::string &src_name, int line_number, Logger &logger)
                : _curr_time(GetTimeStamp())
                , _level(level)
                , _pid(getpid())
                , _src_name(src_name)
                , _line_number(line_number)
                , _logger(logger)
            {
                // 合并左半部分
                std::stringstream ss;
                ss << "[" << _curr_time << "] "
                   << "[" << Level2Str(_level) << "] "
                   << "[" << _pid << "] "
                   << "[" << _src_name << "] "
                   << "[" << _line_number << "] "
                   << "- ";
                _loginfo = ss.str();
            }
            template <typename T>
            LogMessage &operator<<(const T &info)
            {
                // 右半部分,可变
                std::stringstream ss;
                ss << info;
                _loginfo += ss.str();
                return *this;
            }
            ~LogMessage()
            {
                if (_logger._ffush_strategy)
                {
                    _logger._ffush_strategy->SyncLog(_loginfo);
                }
            }
           

        private:
            std::string _curr_time; // 日志时间
            Loglevel _level;        // 日志状态
            pid_t _pid;             // 进程pid
            std::string _src_name;  // 文件名称
            int _line_number;       // 对应的行号
            std::string _loginfo;   // 合并之后的一条完整信息
            Logger &_logger;
        };
         LogMessage operator()(Loglevel level, std::string src_name, int line_number)
            {
                return LogMessage(level, src_name, line_number, *this);
            }
        ~Logger() {}

    private:
        std::unique_ptr<LogStrategy> _ffush_strategy;
    };
    //全局日志对象
    Logger logger;

    //使用宏,简化用户操作,获取文件名和行号
    // __FILE__  一个宏,替换完成后目标文件的文件名
    // __LINE__  一个宏,替换完成后目标文件对应的行号
    #define LOG(level) logger(level,__FILE__,__LINE__) 
    #define Enable_Console_Log_Strategy() logger.EnableConsoleLogStrategy()
    #define Enable_File_Log_Strategy()    logger.EnableFileLogStrategy()
     
}

#endif

Main.cc

cpp 复制代码
#include <iostream>
#include <string>
#include "Reactor.hpp"
#include "Listener.hpp"
#include"Channel.hpp"
#include "Log.hpp"
#include "Common.hpp"
#include"Protocol.hpp"
#include"NetCal.hpp"

void Usage(std::string proc)
{
std::cerr<<"Usage: "<<proc<<"prot"<<std::endl;
}
// ./server port
int main(int argc,char * argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }

    LogModule::ConsoleLogStrategy();

    uint16_t port = std::stoi(argv[1]);

    //1.构建业务模块
    std::shared_ptr<Cal> cal  = std::make_shared<Cal>();

    //2.构建协议对象
    std::shared_ptr<Protocol> protocol = std::make_shared<Protocol>([&cal](Request &req)->Response{
        LOG(Loglevel::DEBUG)<<"进入到了protocol";
        return cal->Execute(req);
    });

    //3.构建listener对象
    std::shared_ptr<Connection> conn = std::make_shared<Listener>(port);
    conn ->RegisterHandler([&protocol](std::string &inbuffer)->std::string{
        LOG(Loglevel::DEBUG)<<"进入到匿名函数";
        std::string response_str;
        while (true)
        {
            std::string package;
            if (!protocol->Decode(inbuffer, &package))
                break;
            // packge一定是一个完整的请求,是字节流的
            response_str += protocol->Execute(package);
        }
       LOG(Loglevel::DEBUG)<<"结束匿名函数...response_str:"<<response_str;
       return response_str;
    });

    //4.构建事件派发模块Reactor
    std::unique_ptr<Reactor> R = std::make_unique<Reactor>();

    R->AddConnection(conn);

    R->Loop();
    return 0;
}

Makefile

cpp 复制代码
ReactorServer:Main.cc
	g++ -o $@ $^  -lpthread  -ljsoncpp -std=c++17 
.PHONY:clean
clean:
	rm -f ReactorServer

Mutex.hpp

cpp 复制代码
#pragma once
#include <pthread.h>
#include <iostream>
namespace MutexModule
{ 
    class Mutex
    {
    public:
        Mutex()
        {
            pthread_mutex_init(&_mutex, nullptr);
        }

        void Lock()
        {
            int n = pthread_mutex_lock(&_mutex);
            (void)n;
        }
        void Unlock()
        {
            int n = pthread_mutex_unlock(&_mutex);
            (void)n;
        }
        ~Mutex()
        {
            pthread_mutex_destroy(&_mutex);
        }
        pthread_mutex_t *get()
        {
            return &_mutex;
        }
    private:
        pthread_mutex_t _mutex;
    };
    class LockGuard
    {
        public:
        LockGuard(Mutex &mutex):_mutex(mutex)
        {
        _mutex.Lock();
        }
        ~LockGuard()
        {
            _mutex.Unlock();
        }
        private:
        Mutex &_mutex;
    };
}

NetCal.hpp

cpp 复制代码
#pragma once

#include "Protocol.hpp"
#include <iostream>

class Cal
{
public:
    Response Execute(Request &req)
    {
        Response resp(0, 0); // code 0正常 1 除零错误 2 mod零 3 非法错误
        switch (req.Oper())
        {
        case '+':
            resp.SetResult(req.X() + req.Y());
            break;
        case '-':
            resp.SetResult(req.X() - req.Y());
            break;
        case '*':
            resp.SetResult(req.X() * req.Y());
            break;
        case '/':
            if (req.Y() == 0)
            {
                resp.SetCode(1);
            }
            resp.SetResult(req.X() / req.Y());
            break;
        case '%':
            if (req.Y() == 0)
            {
                resp.SetCode(2);
            }
            resp.SetResult(req.X() % req.Y());
            break;
        default:
            resp.SetCode(3);
            break;
        }
        return resp;
    }

private:
};

Protocol.hpp

cpp 复制代码
#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <jsoncpp/json/json.h>
#include "Socket.hpp"
#include <functional>

// 实现网络版本的计算器
using namespace LogModule;
using namespace SocketModule;

// client ->server

class Request
{
public:
    Request() {}
    Request(int x, int y, char oper)
        : _x(x), _y(y), _oper(oper)
    {
    }
    std::string Serialize()
    {
        // {
        //  "x" : _x
        //  "y"  : _y
        //  "oper" :_oper
        // }
        Json::Value root;
        root["x"] = _x;
        root["y"] = _y;
        root["oper"] = _oper;

        Json::FastWriter writer;
        std::string s = writer.write(root);
        return s;
    }
    bool Deserialize(std::string &in)
    {
        //{"x":10,"y":20,"oper":'+'}
        Json::Value root;
        Json::Reader reader;
        bool ok = reader.parse(in, root);
        if (ok)
        {
            _x = root["x"].asInt();
            _y = root["y"].asInt();
            _oper = root["oper"].asInt();
        }
        return ok;
    }
    ~Request() {}
    int X() { return _x; }
    int Y() { return _y; }
    char Oper() { return _oper; }

private:
    int _x;
    int _y;
    char _oper;
};

// server -> client
class Response
{
public:
    Response() {}
    Response(int result, int code)
        : _result(result), _code(code)
    {
    }
    void SetResult(int res)
    {
        _result = res;
    }
    void SetCode(int code)
    {
        _code = code;
    }
    std::string Serialize()
    {
        Json::Value root;
        root["result"] = _result;
        root["code"] = _code;

        Json::FastWriter writer;
        return writer.write(root);
    }
    bool Deserialize(std::string &in)
    {
        Json::Value root;
        Json::Reader reader;
        bool ok = reader.parse(in, root);
        if (ok)
        {
            _result = root["result"].asInt();
            _code = root["code"].asInt();
        }
        return ok;
    }
    ~Response() {}
    bool ShowResult()
    {
        std::cout << "结果是: " << _result << "[" << _code << "]" << std::endl;
        return true;
    }

private:
    int _result; // 运算结果
    int _code;   // 0 正常 1,2,3,4 不同的异常
};

const std::string ssep = "\r\n";

using func_t = std::function<Response(Request &req)>;

class Protocol
{
public:
    Protocol() : _func() {}
    Protocol(func_t func) : _func(func) {}
    std::string Encode(const std::string Jsonstr)
    {
        // 包装
        //  50\r\n{"x":10,"y":20,"oper":'+'}\r\n
        std::string len = std::to_string(Jsonstr.size());
        return len + ssep + Jsonstr + ssep; // 应用层封装报头
    }
    bool Decode(std::string &buffer, std::string *package)
    { 
        LOG(Loglevel::DEBUG)<<"进入到了protocol中Decode";
        // 1.判断报文完整性
        ssize_t pos = buffer.find(ssep);
        if (pos == std::string::npos)
        {
            return false; // 让调用方出去继续读
        }

        // 2.提取至少一个报文请求并移除在回调提取下一个

        std::string packge_len_str = buffer.substr(0, pos);
        int package_len_int = std::stoi(packge_len_str);
        // buffer 有长度,但是不一定包含整个报文,得先判断
        int target_len = packge_len_str.size() + package_len_int + ssep.size() * 2;
        if (buffer.size() < target_len)
            return false;
        // 至少包含一个完整报文
        *package = buffer.substr(pos + ssep.size(), package_len_int);
        // 移除已读报文,并且传递给输出型参数,然后返回等待下次回调处理
        buffer.erase(0, target_len);
        return true;
    }
    std::string Execute(std::string &package)
    {
        LOG(Loglevel::DEBUG)<<"进入到了protocol中Execute";
        // 读取
        Request req;
        bool ok = req.Deserialize(package);
        if (!ok)
        {
            return std::string();
        }
        Response resp = _func(req);

        // 序列化
        std::string jion_str = resp.Serialize();

        // //根据自定义协议添加封装 size\r\n{...}\r\n
        std::string send_str = Encode(jion_str);
        // 返回运算结果
        return send_str;
    }

    std::string BuildRequsetString(int x, int y, char oper)
    {
        // 1.构建请求
        Request req(x, y, oper);
        // 2.序列化
        std::string json_req = req.Serialize();
        std::cout << json_req << std::endl;
        // 3.封装报头
        return Encode(json_req);
    }
    ~Protocol() {}

private:
    func_t _func;
};

Reactor.hpp

cpp 复制代码
#pragma once

#include<iostream>
#include<memory>
#include<unordered_map>
#include"Epoller.hpp"
#include"Connection.hpp"
#include"Log.hpp"
using namespace LogModule;
class Reactor
{
    static const int revs_num = 128;
private:
 bool IsConnectionExists(const std::shared_ptr<Connection> &conn)
 {
    return  IsConnectionExistsHelper( conn->GetSockFd());
 }
bool IsConnectionExistsHelper(int sockfd)
{
    auto iter = _connections.find(sockfd);
    if(iter ==_connections.end())
    {
        return false;
    }
    else
    {
        return true;
    }
}
bool IsConnectionExists(int sockfd)
{
    return IsConnectionExistsHelper(sockfd);
}
 bool IsConnectionEmpty()
 {
    return _connections.empty();
 }
public:
Reactor():_epoller_ptr(std::make_unique<Epoller>()),_isrunning(false)
{}

//事件派发器
void Dispatcher(int n)
{
   
 for(int i =0;i<n;i++)
        {
            int sockfd = _revs[i].data.fd;  //就绪fd
            uint32_t revents = _revs[i].events; //就绪事件
            //1.将所有的异常统一转化为IO错误
            //2.所有的IO异常处理全部转化为一个异常处理函数
          if(revents & EPOLLERR)    revents |=(EPOLLIN|EPOLLOUT); //异常处理
          if(revents & EPOLLHUP)    revents |=(EPOLLIN|EPOLLOUT); //处理关闭链接
          
          //走到这不用区分是否异常,因为多态也不用区分listenfd还是普通sockfd,
            if(revents &EPOLLIN)
            {

                //读时间就绪
           if (IsConnectionExists(sockfd))
                    _connections[sockfd]->Recver();

            }
            if(revents & EPOLLOUT)
            {
                //写事件就绪
                if (IsConnectionExists(sockfd))
                    _connections[sockfd]->Sender();
            }
        }
}

int LoopOnce(  int timeout)
{
   
     return  _epoller_ptr->WaitEvents(_revs,revs_num,timeout);
}

void Loop()
{  
    if(IsConnectionEmpty())
    {
        return;
    }
     _isrunning = true;
    while(_isrunning)
    {
        int timeout = 1000;
        int n = LoopOnce(timeout); 
        Dispatcher(n);       
    }
    _isrunning = false;
}
void Stop()
{
    _isrunning = false;
}
//将新连接全部添加到_connections,并且添加到内核
void AddConnection(std::shared_ptr<Connection> & conn)
{
    //0.防止重复添加
    if(IsConnectionExists(conn))
    {
        LOG(Loglevel::WARNING)<<"conn is exists:"<<conn ->GetSockFd();
        return ;
    }
 //1.conn对应的fd和他关心的事件写入到内核
 uint32_t events = conn->GetEvent();
 int sockfd =conn->GetSockFd();
 _epoller_ptr->AddEvent(sockfd,events);

 //2.设置当前conn的拥有者回指指针
 conn->SetOwner(this);

 //3.将具体的connection添加到_connections;
 _connections[sockfd]=conn;
}

~Reactor(){}

private:
//1.epoll模型
std::unique_ptr<Epoller> _epoller_ptr;

//2.管理所有的connection,本质是管理未来所有我获取到的fd
//fd : Commection
std::unordered_map<int ,std::shared_ptr<Connection>> _connections;

//3.就绪的所有事件
struct epoll_event _revs[revs_num];

//是否启动
bool _isrunning;
};

Socket.hpp

cpp 复制代码
#pragma once
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdlib>
#include <unistd.h>
#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"

namespace SocketModule
{
    using namespace LogModule;
    const static int gbacklog =16;
    const  static int defaultfd =-1;
    // 基类socket
    class Socket
    {
    public:
        virtual ~Socket() {}
        virtual void SocketOrDie() = 0;
        virtual void BindOrDie(uint16_t port) = 0;
        virtual void ListenOrDie(int backlog) = 0;
        virtual  int Accept(InetAddr * client)= 0;
        virtual void Close()=0;
        virtual int Recv(std::string * out) = 0;
        virtual int Send(std::string  &message) = 0;
        virtual int Connect(const std::string &server_ip ,uint16_t port) =0;
        virtual int Fd() = 0;
    public:
    void BuildTcpClientSocketMethod()
        {
            SocketOrDie();
        }
        void BUildTcpLIstenSocketMethod(uint16_t port,int backlog = gbacklog)
        {
            SocketOrDie();
            BindOrDie(port);
            ListenOrDie(backlog);
        }
        // void BUildUdpSocketMethod()
        // {
        //     SocketOrDie();
        //     BindOrDie();
        // }
    };

    class TcpSocket : public Socket
    {
    public:
       TcpSocket()
       :_sockfd(defaultfd)
       {}
       TcpSocket(int fd): _sockfd(fd) {}
        
        ~TcpSocket() {}
        void SocketOrDie() override
        {
            _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                LOG(Loglevel::FATAL) << "创建套接字失败!";
                exit(SOCKET_ERR);
            }
            LOG(Loglevel::INIF) << "创建套接字成功!"<< _sockfd;
        }
        void BindOrDie(uint16_t port) override
        {
            InetAddr localaddr(port);
            int n = ::bind(_sockfd, localaddr.NetAddrPtr(), localaddr.NetAddrLen());
            if (n < 0)
            {
                LOG(Loglevel::FATAL) << "绑定失败!";
                exit(BIND_ERR);
            }
            LOG(Loglevel::INIF) << "绑定成功!";
        }
        void ListenOrDie(int backlog) override
        {
            int n = ::listen(_sockfd, backlog);
            if (n < 0)
            {
                LOG(Loglevel::FATAL) << "监听失败!";
                exit(LISTEN_ERR);
            }
            LOG(Loglevel::INIF) << "监听成功!";
        }

    #define ACCEPT_ERR -3
    #define ACCEPT_DOWN -1
    #define ACCEPT_CONTINUE -2

        int Accept(InetAddr * client) override
        {
            struct sockaddr_in peer;
            socklen_t len  = sizeof(peer);
            int fd =::accept(_sockfd,CONV(peer),&len);
            if (fd < 0)
            {
                if(errno == EAGAIN ||errno ==EWOULDBLOCK)
                {
                    return -1;//底层没有新连接
                }
                else if(errno == EINTR)
                {
                    return -2;//继续读
                }
                else
                {
                    LOG(Loglevel::WARNING)<<"连接失败!";
                    return -3; //连接失败
                }
                
            }
              LOG(Loglevel::WARNING)<<"连接成功!sockfd:"<< _sockfd;
               client->SetAddr(peer);
               return  fd;
        }
        void Close() override
        {
            if(_sockfd >=0)
            {
                ::close(_sockfd);
            }
        }
         int Recv(std::string * out) override
         {
            //流式读取,不关心读到的是什么
            char buffer[4096*2];
            ssize_t n =::recv(_sockfd,buffer,sizeof(buffer)-1,0);
            if (n >0)
              {
                buffer[n]=0;
                *out+=buffer;
                return n;
              }
            return n;

         }
         int Send(std::string  &message) override
         {
           return send(_sockfd,message.c_str(),message.size(),0);
         }
         int Connect(const std::string &server_ip ,uint16_t port) override
         {
           InetAddr server(server_ip,port);
           return ::connect(_sockfd,server.NetAddrPtr(),server.NetAddrLen()) ;    
         }
         int Fd()
        {
            return _sockfd;
        }
    private:
        int _sockfd; //
    };

    // class UdpSocket : public Socket
    // {

    // };
}

TcpClient.cc(部分)

cpp 复制代码
#include <iostream>
#include <memory>
#include "Socket.hpp"
#include <string>
#include "Protocol.hpp"

using namespace SocketModule;
void Usage(std::string proc)
{

    std::cerr << "Usage: " << proc << "prot" << std::endl;
}

void GetDataFromStdin(int *x, int *y, char *oper)
{
    std::cout << "Please Enter x: ";
    std::cin >> *x;
    std::cout << "Please Enter y: ";
    std::cin >> *y;
    std::cout << "Please Enter oper: ";
    std::cin >> *oper;
}
int main(int argc, char *argv[])
{

    if (argc != 3)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    //
    std::string server_ip = argv[1];
    uint16_t server_port = std::stoi(argv[2]);
    std::shared_ptr<Socket> client = std::make_shared<TcpSocket>();
    client->BuildTcpClientSocketMethod();

    if (client->Connect(server_ip, server_port) != 0)
    {
        // 失败
        std::cerr << "客户端连接失败" << std::endl;
        exit(CONNECT_ERR);
    }
    // 连接成功

    std::unique_ptr<Protocol> protocol = std::make_unique<Protocol>();
    std::string resp_buffer;
    while (true)
    {
        // 从标准输入中获取数据
        int x, y;
        char oper;
        GetDataFromStdin(&x, &y, &oper);

        // 构建一个请求 ->可以发送的字符串
        std::string req_str = protocol->BuildRequsetString(x, y, oper);
        std::cout << "--------req_str --------" << std::endl;
        std::cout << req_str << std::endl;
        std::cout << "------------------------" << std::endl;

        // 3.发送请求
        client->Send(req_str);

        // 4.获取应答
        Response resp;
        bool res = protocol->GetResponse(client, resp_buffer, &resp);
        if (res == false)
            break;
        // 显示结果
        resp.ShowResult();
    }
    client->Close();

    return 0;
}

server

client

相关推荐
qq_364371721 小时前
Docker 常见命令
运维·docker·容器
张太行_1 小时前
网络SSL/TLS协议详解
网络·web安全·ssl
重启的码农1 小时前
NAT穿透技术:原理、实现与应用全景解析
网络协议
zhysunny1 小时前
Day22: Python涡轮增压计划:用C扩展榨干最后一丝性能!
c语言·网络·python
hhzz1 小时前
重温 K8s 基础概念知识系列八( K8S 高级网络)
网络·容器·kubernetes
VVVVWeiYee4 小时前
TCP/UDP详解(一)
运维·网络·tcp/ip·udp·信息与通信
Xの哲學4 小时前
Linux PCI 子系统:工作原理与实现机制深度分析
linux·网络·算法·架构·边缘计算
谢尔登4 小时前
【计算机网络】 IPV4和IPV6区别
运维·服务器·计算机网络
@Demi4 小时前
vsCode或Cursor 使用remote-ssh插件链接远程终端
服务器·ide·vscode·ssh
努力努力再努力wz5 小时前
【c++进阶系列】:万字详解多态
java·linux·运维·开发语言·c++