muduo网络库初识

2020-10-17  本文已影响0人  老杜振熙

一、前言

关于网络编程的几个核心:

网络库存在的意义:业务逻辑和网络框架相分离(很重要)

用户需要做的,就是将业务逻辑进行填充,将Callback注册到网络框架中(这也就是Reactor模式的思路)。Reactor事件循环所在的线程即被称为IO线程。网络库负责读写socket,用户代码负责编写解码、计算、编码的部分。

二、muduo网络库

基于Reactor模式,用一个EventLoop去响应计时器和IO事件

基于事件的非阻塞网络,被动等待事件+网络库调用之前注册的事件处理函数,遵循one loop per thread的网络模式。
(这里一定要总结到位)相关引用[1] [2]

IO Multiplexing

IO多路复用最重要的作用就是使得程序能够同时阻塞在多个文件描述符上, 并在其中一个可以读写时得到通知。单个线程,通过记录和跟踪各个IO流的状态,从而同时管理多个IO流[3]。在Linux中,提供了3种不同的IO 多路复用方式, select(), poll(), epoll()

epoll()自不必说是性能最优的方法。对于select()poll()而言,它们之间最核心的区别就在于静态和动态select()的底层是一个固定大小的事件数组(静态),数组的元素下标对应了文件描述符的大小,而元素本身对应了该文件描述符所关心的事件(以及返回的激活事件),这就造成了三个负面的影响:

可以看出,poll()使用动态数组,只需要关注那些需要关心的文件描述符,比如说我如果只关心fd==1000这个文件描述符,那只需要创建一个struct pollfd即可。但必须要说的是,这也只是治标不治本,当单个线程需要关注的文件描述符达到上百个时,即使是poll(),其应对这样的吞吐量也只是捉襟见肘。

所以性能最优越,但实现也最复杂的epoll()诞生了,全称即event pollepoll()的核心就是一句话:将监听注册和实际监听相互分离

muduo中的5个关键类

  1. Buffer
    数据的读写通过Buffer进行操作,这样用户就不需要使用read()/write()等系统调用了。

  2. EventLoop
    其实就是一个Reactor,用于注册和分发IO事件。可以共用单个EventLoop,也可以分配多个,来发挥多核的性能。

  3. TcpConnection
    作为TCP连接的实现。需要记住几点:

  1. TcpClient

  2. TcpServer

使用muduo网络库的几个简单例子

  1. echo服务器
#ifndef ECHO_MYSELF

#define ECHO_MYSELF
#include <muduo/net/TcpServer.h>
#include <muduo/base/Logging.h>

using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
/*! \class echo_myself
*  \brief a simple echo server
*
*  just send back all the receiced data
*/
class echo_myself
{
public:
    // loop: eventloop, a reactor;
    // addr: internet address that this server should listen;
    echo_myself(muduo::net::EventLoop *loop, const muduo::net::InetAddress &addr)
        : loop_(loop),
          server_(loop, addr, "EchoServer")
    {
        server_.setConnectionCallback(std::bind(&echo_myself::onConnection, this, _1));
        server_.setMessageCallback(std::bind(&echo_myself::onMessage, this, _1, _2, _3));
    }

    void start()
    {
        server_.start();
    }

protected:
    muduo::net::EventLoop *loop_; // we need event loop to define a reactor;
    muduo::net::TcpServer server_; // use tcp server to define a echo server;

    // callback:
    void onConnection(muduo::net::TcpConnectionPtr conn);
    void onMessage(muduo::net::TcpConnectionPtr conn, muduo::net::Buffer *buf, muduo::Timestamp time_);
};

#endif
#include "echo_myself.h"
    void echo_myself::onConnection(muduo::net::TcpConnectionPtr conn)
    {
        LOG_INFO << conn->peerAddress().toIpPort() << " -> " \
            << conn->localAddress().toIpPort() << " is " \
            << (conn->connected() ? "on" : "off");
    }

    void echo_myself::onMessage(muduo::net::TcpConnectionPtr conn, muduo::net::Buffer *buf, muduo::Timestamp time_)
    {
        muduo::string buf_(buf->retrieveAllAsString());
        LOG_INFO << conn->peerAddress().toIpPort() << " -> " \
            << conn->localAddress().toIpPort() << " send " << buf_.size() << " data "
            << " at time: " << time_.toFormattedString();
        conn->send(buf_); // 这里也就是核心的业务逻辑
    }
  1. filetransfer

其他

  1. 编译之前的注意事项:
  1. 前向声明(Forward declaration):简化头文件之间的依赖关系

  2. StringPeace: 专门用于传送字符串参数(包括char *和string)的class。

  3. 一切的一切,重点均是回调
    回调到底调的是什么呢?在C++的机制中,可以回调自己的类里面的函数,也可以回调用户提供的函数,甚至可以回调其他类里面的函数,所有的途径均通过std::function<>的注册机制就可以实现,而这,也是muduo网络库的特点。

Reference


  1. https://zhuanlan.zhihu.com/p/93612337

  2. https://www.zhihu.com/question/26943938

  3. IO 多路复用是什么意思? - 知乎 (zhihu.com)

上一篇下一篇

猜你喜欢

热点阅读