• 一、重写muduo网络库之服务器编程及测试


    目录

    一、基于muduo网络库开发服务器程序的基本步骤

    1、组合TcpServer对象

    2、创建EventLoop事件循环对象的指针

    3、明确TCPServer构造函数需要的参数,输出ChatServer的构造函数

    4、在当前服务器类的构造函数当中,注册处理连接的回调函数和处理读写事件的回调函数

    5、设置合适的服务端线程数量,muduo库会自己分配I/O线程和work线程

    二、测试代码

    今天开始,小鱼将和大家一起学习陈硕大佬的优秀开源项目------muduo网络库,并重写muduo网络库的一些重要的函数、类等代码,学习优秀的开源代码和编码思维。

    在学习muduo网络库之前,我们首先要知道muduo库如何使用,今天,我们一起来用muduo库写一下简单的服务器和客户端的代码,并测试一下。

    首先,我们要知道muduo网络库提供了两个重要的类

    TcpServer:用于编写服务器程序

    TCPClient:用于编写客户端程序

    mudou网络库使用了epoll+线程池,这样的做的好处是可以把网络I/O的代码和业务代码区分开。

    接下来,我们就开始用muduo网络库编写代码。

    一、基于muduo网络库开发服务器程序的基本步骤

    1、组合TcpServer对象

    1. EventLoop* loop, //事件循环
    2. const InetAddress &listenAddr, //IP+Port
    3. const string &nameArg //服务器的名字

    这里的EventLoop *loop就是事件循环,也可以理解为reactor模型的反应堆。const InetAddress &listenAddr表示服务器的端口,const string &nameArg 指的是服务器的名字,也就是给线程绑定一个名字。

    2、创建EventLoop事件循环对象的指针

    3、明确TCPServer构造函数需要的参数,输出ChatServer的构造函数

    1. class ChatServer{
    2. public:
    3. ChatServer(EventLoop* loop, //事件循环
    4. const InetAddress &listenAddr, //IP+Port
    5. const string &nameArg) //服务器的名字
    6. : _server(loop, listenAddr, nameArg), _loop(loop)
    7. {
    8. //给服务器注册用户连接的创建和断开回调
    9. _server.setConnectionCallback(std::bind(&ChatServer::onConnection,this,_1));
    10. //给服务器注册用户读写时间回调
    11. _server.setMessageCallback(std::bind(&ChatServer::onMessage, this,_1,_2,_3));
    12. //设置服务器端的线程数量,1个IO线程,3个work线程
    13. _server.setThreadNum(4);
    14. }
    15. //开启事件循环
    16. void start(){
    17. _server.start();
    18. }

    这里的setConnectionCallback(std::bind(&ChatServer::onConnection,this,_1))函数绑定了onConnection的this。当底层监测到有用户的连接和断开的时候就会帮我们调用这个函数,给服务器注册用户连接的创建和断开回调。

    下面的setMessageCallback(std::bind(&ChatServer::onMessage, this,_1,_2,_3))函数也是同样的意思。

    4、在当前服务器类的构造函数当中,注册处理连接的回调函数和处理读写事件的回调函数

    1. //专门处理用户的连接创建和断开 epoll listenfd accept
    2. void onConnection(const TcpConnectionPtr &conn){
    3. if(conn->connected()){
    4. cout << conn->peerAddress().toIpPort() << "->" <<
    5. conn->localAddress().toIpPort() << "state:online" << endl;
    6. }
    7. else{
    8. cout << conn->peerAddress().toIpPort() << "->" <<
    9. conn->localAddress().toIpPort() << "state:offline" << endl;
    10. conn->shutdown(); //close(fd)
    11. //_loop->quit(); 连接断开
    12. }
    13. }

    这是专门处理用户的连接创建和断开的函数。

    1. //专门处理用户读写事件
    2. void onMessage(const TcpConnectionPtr &conn, //连接
    3. Buffer *buffer, //缓冲区
    4. Timestamp time) //接收到数据的时间信息
    5. {
    6. string buf = buffer->retrieveAllAsString();
    7. cout << "recv data:" << buf << "time:" << time.toString() << endl;
    8. conn->send(buf);
    9. }

    这是专门处理用户读写事件的函数。

    5、设置合适的服务端线程数量,muduo库会自己分配I/O线程和work线程

    1. //设置服务器端的线程数量,1个IO线程,3个work线程
    2. _server.setThreadNum(4);

    设置服务器端的线程数量,1个IO线程,3个worker线程

    第一个工作线程做耗时的I/O操作,主要是传输文件、音视频等二进制文件的操作;

    第二个线程处理已连接事件的读写事件;

    第三个线程处理用户的连接和断开;

    二、测试代码

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. using namespace std;
    7. using namespace muduo;
    8. using namespace muduo::net;
    9. /*基于muduo网络库开发服务器程序
    10. 1、组合TcpServer对象
    11. 2、创建EventLoop事件循环对象的指针
    12. 3、明确TCPServer构造函数需要什么参数,输出ChatServer的构造函数
    13. 4、在当前服务器类的构造函数当中,注册处理连接的回调函数和处理读写事件的回调函数
    14. 5、设置合适的服务端线程数量,muduo库会自己分配I/O线程和work线程
    15. */
    16. class ChatServer{
    17. public:
    18. ChatServer(EventLoop* loop, //事件循环
    19. const InetAddress &listenAddr, //IP+Port
    20. const string &nameArg) //服务器的名字
    21. : _server(loop, listenAddr, nameArg), _loop(loop)
    22. {
    23. //给服务器注册用户连接的创建和断开回调
    24. _server.setConnectionCallback(std::bind(&ChatServer::onConnection,this,_1));
    25. //给服务器注册用户读写时间回调
    26. _server.setMessageCallback(std::bind(&ChatServer::onMessage, this,_1,_2,_3));
    27. //设置服务器端的线程数量,1个IO线程,3个work线程
    28. _server.setThreadNum(4);
    29. }
    30. //开启事件循环
    31. void start(){
    32. _server.start();
    33. }
    34. private:
    35. //专门处理用户的连接创建和断开 epoll listenfd accept
    36. void onConnection(const TcpConnectionPtr &conn){
    37. if(conn->connected()){
    38. cout << conn->peerAddress().toIpPort() << "->" <<
    39. conn->localAddress().toIpPort() << "state:online" << endl;
    40. }
    41. else{
    42. cout << conn->peerAddress().toIpPort() << "->" <<
    43. conn->localAddress().toIpPort() << "state:offline" << endl;
    44. conn->shutdown(); //close(fd)
    45. //_loop->quit(); 连接断开
    46. }
    47. }
    48. //专门处理用户读写事件
    49. void onMessage(const TcpConnectionPtr &conn, //连接
    50. Buffer *buffer, //缓冲区
    51. Timestamp time) //接收到数据的时间信息
    52. {
    53. string buf = buffer->retrieveAllAsString();
    54. cout << "recv data:" << buf << "time:" << time.toString() << endl;
    55. conn->send(buf);
    56. }
    57. TcpServer _server; //#1
    58. EventLoop *_loop; //#2 epoll
    59. };
    60. int main(){
    61. EventLoop loop; //epoll
    62. InetAddress addr("192.168.18.128",6000);
    63. ChatServer server(&loop, addr, "ChatServer");
    64. server.start(); //listenfd epoll_ctl=>epoll
    65. loop.loop(); //epoll_wait以阻塞的方式等待新用户的连接,已连接的用户读写事件等
    66. return 0;
    67. }

    运行一下

  • 相关阅读:
    2022“杭电杯”中国大学生算法设计超级联赛(7) 2022杭电多校第七场
    关于Fragment的生命周期,你知道多少?
    栈的应用场景(三)
    2069;【例2.12 】糖果游戏(信奥一本通)
    【老生谈算法】matlab实现遗传算法学习和全局化算法——遗传算法
    【组件专题】初识COM组件
    MyBatis动态SQL
    Rust 语言学习杂谈 (end) (各种工作中遇到的疑难杂症)
    [python][学习]回文数
    Redis分布式锁
  • 原文地址:https://blog.csdn.net/weixin_52967653/article/details/126431331