# EchoServer **Repository Path**: dodoba/echo-server ## Basic Information - **Project Name**: EchoServer - **Description**: 封装一个简单的单线程Reactor模式+基于对象的线程池实现的网络库 - **Primary Language**: C++ - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2022-05-26 - **Last Updated**: 2022-05-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### v3.1 1. 修复v3需要全局变量的小bug:构建EchoServer类,三个事件也是以类成员函数形式出现 ```c++ class EchoServer { public: EchoServer(const string &ip, unsigned short port) : threadPool_(4, 10), tcpServer_(ip, port) {} void start(); // 注意std::placeholders::_1的使用 // 自定义回调函数传参 void onConnection(const TcpConnectionPtr &ptr); void onMessage(const TcpConnectionPtr &ptr); void onClose(const TcpConnectionPtr &ptr); private: ThreadPool threadPool_; TcpServer tcpServer_; }; ``` 2. 把网络库和线程池分目录存放,重构makefile #### 使用方法 ```c++ EchoServer echoServer("172.25.40.81", 9999); echoServer.start(); ``` --- ### v3 加入线程池,把业务逻辑处理交给线程池子线程处理, 处理完,子线程把send函数包装成回调函数交给I/O线程执行, 线程间通信通过读写eventfd文件描述符。 1. EventLoop类新增: 成员: eventfd_; // 用于线程间通信,构造时注册进epoll vector functors_; // 线程池完成任务后把**send函数**包装成回调,交给I/O线程执行 MutexLock mutex_; 方法: int eventfdCreate(); void eventfdRead(); void eventfdWrite(); void doPendingFunctors(); // 执行functors_ void runInEventLoop(Functor &&cb); // 执行线程池子线程交过来的回调函数 2. TcpConnection类新增: 成员: EventLoop *loop_; // 构造时传参 方法: void sendInEventLoop(const string &msg); // 把send包装成回调给I/O线程执行,用于调用loop->runInEventLoop(std::bind(TCP对象的send方法, msg)); #### 使用方法 ```c++ ThreadPool threadpool(4, 10); // 4线程,10队列长度 threadpool.start(); gThreadpool = &threadpool; // 全局变量 ThreadPool *gThreadpool = nullptr; TcpServer echo("172.25.40.81", 9999); echo.setConnectionCallBack(onConnection); echo.setMessageCallBack(onMessage); echo.setCloseCallBack(onClose); echo.start(); ``` --- ### v2.1 在v2的基础上,把EventLoop和Acceptor封装成一个TcpServer类, 无明显区别。 --- ### v2 解决v1出现的问题,支持多客户端同时访问server 1. 新增EventLoop类,本质是一个事件循环 > 处理三类事件:新连接到来;信息处理;连接关闭 > 三类事件通过回调函数注册给EventLoop > EventLoop把回调函数交给TCPConnection注册、执行 2. InetAdress -- 网络地址类 3. Socket -- 负责套接字的创建和销毁 RAII技术 4. SocketIO -- I/O线程,即Reactor线程 5. Acceptor -- 处理新连接的请求 6. TcpConnection -- Accept之后产生一个TCP对象(四元组:local ip, port, peer ip, port) ```c++ 处理新连接: peerfd = accept(); epollAddRead(peerfd); shared_ptr p = new TCP对象; 注册三个回调函数给tcp; 加入map; // {peerfd, TCP} 调用NewConnectonCallBack; 处理消息(int fd): TCP连接 = map.find(fd); 如果连接断开:执行TCP->CloseCallBack; // 如何判断连接断开? recv返回值为0 否则:执行TCP->MessageCallBack; ``` ### 使用方法 ```c++ Acceptor acceptor("172.25.40.81", 9999); acceptor.ready(); cout << "Acceptor is ready!" << endl; EventLoop loop(acceptor); // 传acceptor引用构造 // 设置回调函数 loop.setConnectionCallBack(onConnection); loop.setMessageCallBack(onMessage); loop.setCloseCallBack(onClose); loop.loop(); // 开始事件循环 ``` ---- ### v1 #### 简单来说,包含五个类 1. InetAdress -- 网络地址类 2. Socket -- 负责套接字的创建和销毁 RAII技术 3. SocketIO -- I/O线程,即Reactor线程 4. Acceptor -- 处理新连接的请求 5. TcpConnection -- Accept之后产生一个TCP对象(四元组:local ip, port, peer ip, port) #### 使用方法 ```c++ Acceptor acceptor("172.25.40.81", 9999); acceptor.ready(); TcpConnection tcp(acceptor.accept()); cout << tcp.printTcp() << " has connected!" << endl; tcp.send("Welcome to EchoServer\n"); // 发送 cout << ">> client data:" << endl; cout << tcp.recvLine(); // 接收一行数据 ``` #### bug 1. 用while(1)不断接收客户端数据时,客户端退出,服务器崩或没反应。 2. 无法多客户端同时访问服务器