Muduo网络库源码分析之Reactor模式的关键结构

muduo源码分析--详解muduo多线程模型 6.3     非阻塞网络编程应该用边沿触发(ET)还是电平触发(LT)?如果是电平触发,那么什么时候关注POLLOUT事件?会不会造成busy-loop?如果是边沿触发,如果和防止漏读造成的饥饿? epoll一定比poll快么? 6.4        在finger的测试程序中,没有设置onConnection这个函数,就可以连接? 6.6 详解muduo多线程模型   阅读详情

本文分析一下Reactor模式的实现,关键是三个类:Channel、Poller、EventLoop。

事件分发类 Channel

Channel 是 selectable IO channel,负责注册与响应IO事件,包括注册给Poller的 fd 及其监听的事件,以及事件发生了所调的回调函数。
每个Channel对象自始至终只负责一个 fd 的事件分发,封装了一系列该 fd 对应的操作,使用了回调函数,包括可读、可写、关闭和错误处理四个。
首先给定Channel所属的 loop,及其要处理的 fd;接着注册 fd 上需要监听的事件,如果是常用的读写事件的话,可以直接调用接口函数enableReading或enableWriting来注册对应fd上的事件,disable*是销毁指定的事件;然后通过 set*Callback 来设置事件发生时的回调。
注册事件时函数调用关系,如下:Channel::update()->EventLoop::updateChannel(Channel*)->Poller::updateChannel(Channel*),最终向 poll 系统调用的监听事件表注册或修改事件。

Channel.h

#ifndef MUDUO_NET_CHANNEL_H
#define MUDUO_NET_CHANNEL_H

#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>

#include <muduo/base/Timestamp.h>

namespace muduo
{
namespace net
{

class EventLoop;

///
/// A selectable I/O channel.
///
/// This class doesn't own the file descriptor.
/// The file descriptor could be a socket,
/// an eventfd, a timerfd, or a signalfd
/* 事件分发类,主要包括 fd fd监听的事件、事件回调函数 */
class Channel : boost::noncopyable
{
 public:
  /* 事件回调函数模板 */
  typedef boost::function<void()> EventCallback;
  /* 读操作回调函数,需要传入时间 */
  typedef boost::function<void(Timestamp)> ReadEventCallback;

  /* 
   * 一个Channel只负责一个fd,但Channel不拥有fd
   * EventLoop调用Poller监听事件集合,就绪的事件元素就是Channel
   * Channel不仅是返回就绪事件,还可以处理事件
   */
  Channel(EventLoop* loop, int fd);
  ~Channel();

  /* 
   * Channel的核心
   * 处理事件,一般由Poller通过EventLoop来调用
   * 当fd对应的事件就绪后Channel::handleEvent()执行相应的事件回调
   * 如可读事件执行 readCallback_()
   */
  void handleEvent(Timestamp receiveTime);

  /* 设置四种回调函数 */
  void setReadCallback(const ReadEventCallback& cb)
  { readCallback_ = cb; }
  void setWriteCallback(const EventCallback& cb)
  { writeCallback_ = cb; }
  void setCloseCallback(const EventCallback& cb)
  { closeCallback_ = cb; }
  void setErrorCallback(const EventCallback& cb)
  { errorCallback_ = cb; }
#ifdef __GXX_EXPERIMENTAL_CXX0X__
  /* C++11版本 右值语义 */
  void setReadCallback(ReadEventCallback&& cb)
  { readCallback_ = std::move(cb); }
  void setWriteCallback(EventCallback&& cb)
  { writeCallback_ = std::move(cb); }
  void setCloseCallback(EventCallback&& cb)
  { closeCallback_ = std::move(cb); }
  void setErrorCallback(EventCallback&& cb)
  { errorCallback_ = std::move(cb); }
#endif

  /// Tie this channel to the owner object managed by shared_ptr,
  /// prevent the owner object being destroyed in handleEvent.
  void tie(const boost::shared_ptr<void>&);

  /* 返回该Channel负责的fd*/
  int fd() const { return fd_; }
  /* 返回 fd  注册的事件 */
  int events() const { return events_; }
  /* 
   * 进行poll或者epoll_wait后,根据fd的返回事件调用此函数,设定fd的就绪事件类型
   * handleEvent 根据就绪事件类型(revents_)来决定执行哪个事件回调函数
   */
  void set_revents(int revt) { revents_ = revt; } // used by pollers
  // int revents() const { return revents_; }
  /* 判断fd是不是 没有 事件监听 */
  bool isNoneEvent() const { return events_ == kNoneEvent; }

  /* update 通过eventloop 去更新epoll中fd的监听事件 */
  /* fd 注册可读事件 */
  void enableReading() { events_ |= kReadEvent; update(); }
  /* 销毁读事件 */
  void disableReading() { events_ &= ~kReadEvent; update(); }
  /* fd 注册可写事件 */
  void enableWriting() { events_ |= kWriteEvent; update(); }
  /* 销毁写事件 */
  void disableWriting() { events_ &= ~kWriteEvent; update(); }
  /* 停止监听所有事件 */
  void disableAll() { events_ = kNoneEvent; update(); }
  /* 是否注册了读写事件 */
  bool isWriting() const { return events_ & kWriteEvent; }
  bool isReading() const { return events_ & kReadEvent; }

  // for Poller
  // 还不懂
  int index() { return index_; }
  void set_index(int idx) { index_ = idx; }

  // for debug
  string reventsToString() const;
  string eventsToString() const;

  void doNotLogHup() { logHup_ = false; }

  /* 返回持有本Channel的EventLoop 指针 */
  EventLoop* ownerLoop() { return loop_; }
  /* 将Channel 从EventLoop中移除 */
  void remove();

 private:
  static string eventsToString(int fd, int ev);

  /* 通过调用loop_->updateChannel()来注册或改变本fd在epoll中监听的事件 */
  void update();
  void handleEventWithGuard(Timestamp receiveTime);

  static const int kNoneEvent;  //无事件
  static const int kReadEvent;  //可读事件
  static const int kWriteEvent; //可写事件

  EventLoop* loop_;             //本Channel所属的EventLoop
  const int  fd_;               //本Channel负责的文件描述符,Channel不拥有fd
  int        events_;           //fd 注册的事件
  int        revents_;          //通过poll返回的就绪事件类型 
  int        index_;            //被Poller使用的下标 used by Poller.
  bool       logHup_;           //是否生成某些日志

  boost::weak_ptr<void> tie_;
  bool tied_;
  bool eventHandling_;          //是否正在处理事件
  bool addedToLoop_;
  /* 四种回调函数,使用boost提供的function模板*/
  ReadEventCallback readCallback_;  //读事件回调函数
  EventCallback writeCallback_;     //写事件回调函数
  EventCallback closeCallback_;     //关闭事件回调函数
  EventCallback errorCallback_;     //错误事件回调函数
};

}
}
#endif  // MUDUO_NET_CHANNEL_H

Channel.cc

/* 事件 */
const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = POLLIN | POLLPRI;
const int Channel::kWriteEvent = POLLOUT;

Channel::Channel(EventLoop* loop, int fd__)
  : loop_(loop),
    fd_(fd__),
    events_(0),
    revents_(0),
    index_(-1),
    logHup_(true),
    tied_(false),
    eventHandling_(false),
    addedToLoop_(false)
{
}

Channel::~Channel()
{
  assert(!eventHandling_);
  assert(!addedToLoop_);
  if (loop_->isInLoopThread())
  {
    assert(!loop_->hasChannel(this));
  }
}
/* 通过调用loop中的函数来改变对应fd在epoll中监听的事件 */
void Channel::update()
{
  addedToLoop_ = true;
  /* loop中又会去调用Poller中的函数来实现 */
  loop_->updateChannel(this);
}

/* 和上面类似,通过EventLoop 从epoll/poll 中删除fd*/
void Channel::remove()
{
  assert(isNoneEvent());
  addedToLoop_ = false;
  /* EventLoop 会调用Poller中的函数 */
  loop_->removeChannel(this);
}

void Channel::handleEvent(Timestamp receiveTime)
{
  boost::shared_ptr<void> guard;
  if (tied_)
  {
    guard = tie_.lock();
    if (guard)
    {
      handleEventWithGuard(receiveTime);
    }
  }
  else
  {
    handleEventWithGuard(receiveTime);
  }
}

/* 处理各种事件 */
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
  eventHandling_ = true;
  LOG_TRACE << reventsToString();
  if ((revents_ & POLLHUP) && !(revents_ & POLLIN))
  {
    if (logHup_)
    {
      LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLHUP";
    }
    if (closeCallback_) closeCallback_();
  }

  if (revents_ & POLLNVAL)
  {
    LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLNVAL";
  }

  if (revents_ & (POLLERR | POLLNVAL))  //错误事件处理
  {
    if (errorCallback_) errorCallback_();
  }
  if (revents_ & (POLLIN | POLLPRI | POLLRDHUP))    //可读
  {
    if (readCallback_) readCallback_(receiveTime);
  }
  if (revents_ & POLLOUT)   //可写
  {
    if (writeCallback_) writeCallback_();
  }
  eventHandling_ = false;
}

IO multiplexing 类 Poller

Poller 类是IO复用类的基类,muduo 同时支持poll 和 epoll 两种IO multiplexing 机制,它有两个PollPoller 和 EpollPoller 两个子类,内部分别采用 poll 和 epoll 实现。它的职责仅仅是IO复用,事件分发交给 Channel 完成,生命期和 EventLoop 一样长。
拿 epoll 对 Poller 的实现来说,基本是 epoll 功能的封装,poll 函数调用 epoll_wait 来监听注册了的文件描述符,将返回的就绪事件装入 activeChannels 数组,还可以控制 channel 中事件的增删改。

Poller.h

#ifndef MUDUO_NET_POLLER_H
#define MUDUO_NET_POLLER_H

#include <map>
#include <vector>
#include <boost/noncopyable.hpp>

#include <muduo/base/Timestamp.h>
#include <muduo/net/EventLoop.h>

namespace muduo
{
namespace net
{

class Channel;

///
/// Base class for IO Multiplexing
///
/// This class doesn't own the Channel objects.
class Poller : boost::noncopyable //不拥有Channel
{
 public:
  typedef std::vector<Channel*> ChannelList;
  /* 用于返回就绪事件集合 */
  Poller(EventLoop* loop);
  virtual ~Poller();

  /// Polls the I/O events.
  /// Must be called in the loop thread.
  /* Poller的核心功能,将就绪事件加入到 activeChannels 中 */
  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels) = 0;

  /// Changes the interested I/O events.
  /// Must be called in the loop thread.
  /* 更新 fd 的监听事件
   * Channel::update()->EventLoop::updateChannel(Channel* channel)->Poller::updateChannel(Channel* channel)
   */
  virtual void updateChannel(Channel* channel) = 0;

  /// Remove the channel, when it destructs.
  /// Must be called in the loop thread.
  /* 从poll/epoll 中移除fd 停止监听此fd 
   * EventLoop::removeChannel(Channel*)->Poller::removeChannel(Channel*)
   */
  virtual void removeChannel(Channel* channel) = 0;
  /* 判断该poll//epoll 模型是否监听了Channel对应的fd */
  virtual bool hasChannel(Channel* channel) const;

  /* */
  static Poller* newDefaultPoller(EventLoop* loop);

  /* 断言 确保没有跨线程 */
  void assertInLoopThread() const
  {
    ownerLoop_->assertInLoopThread();
  }

 protected:
  /*
   * 记录fd到Channel的对应关系 
   * 底层的epoll每次监听完fd,要根据这个映射关系去寻找对应的Channel
   */
  typedef std::map<int, Channel*> ChannelMap;
  ChannelMap channels_;//保存epoll监听的fd,及其对应的Channel指针

 private:
  /* 这个Poller对象所属的 EventLoop */
  EventLoop* ownerLoop_;
};

}
}
#endif  // MUDUO_NET_POLLER_H

epoll对Poller的实现 :poller/EPollPoller.h

#ifndef MUDUO_NET_POLLER_EPOLLPOLLER_H
#define MUDUO_NET_POLLER_EPOLLPOLLER_H

#include <muduo/net/Poller.h>

#include <vector>

struct epoll_event;

namespace muduo
{
namespace net
{

///
/// IO Multiplexing with epoll(4).
///
class EPollPoller : public Poller
{
 public:
  EPollPoller(EventLoop* loop);
  virtual ~EPollPoller();

  /* 内部调用 epoll_wait,初始化对应的channel,加入到activeChannels */
  virtual Timestamp poll(int timeoutMs, ChannelList* activeChannels);
  virtual void updateChannel(Channel* channel);
  virtual void removeChannel(Channel* channel);

 private:
  static const int kInitEventListSize = 16;

  static const char* operationToString(int op);

  /* 将epoll_wait 返回的活跃事件填充到activeChannels */
  void fillActiveChannels(int numEvents,
                          ChannelList* activeChannels) const;
  /* 更改 channel ,调用epoll_ctl */
  void update(int operation, Channel* channel);

  typedef std::vector<struct epoll_event> EventList;

  int epollfd_;         //epollfd
  EventList events_;    //epoll事件数组
};

}
}
#endif  // MUDUO_NET_POLLER_EPOLLPOLLER_H

EventLoop 类

EventLoop类是Reactor模式的核心,一个线程一个事件循环,即one loop per thread,EventLoop 对象的生命周期通常与其所属的线程一样长。其主要功能是运行事件循环,等待事件发生,然后调用回调处理发生的事件。EventLoop::loop() -> Poller::poll() 填充就绪事件集合 activeChannels,然后遍历该容器,执行每个 channel 的 Channel::handleEvent() 完成对应就绪事件回调。

EventLoop.h

#ifndef MUDUO_NET_EVENTLOOP_H
#define MUDUO_NET_EVENTLOOP_H

#include <vector>

#include <boost/any.hpp>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>

#include <muduo/base/Mutex.h>
#include <muduo/base/CurrentThread.h>
#include <muduo/base/Timestamp.h>
#include <muduo/net/Callbacks.h>
#include <muduo/net/TimerId.h>

namespace muduo
{
namespace net
{

class Channel;  //前向声明,事件分发器主要用于事件注册与回调
class Poller;   //IO复用类,监听事件集合,即 epoll /poll 的功能
class TimerQueue;

///
/// Reactor, at most one per thread.
///
/// This is an interface class, so don't expose too much details.
/* EventLoop 是不可拷贝的 muduo中的大多数class都是不可拷贝的 */
class EventLoop : boost::noncopyable
{
 public:
  typedef boost::function<void()> Functor;  //回调函数

  EventLoop();
  ~EventLoop();  // force out-line dtor, for scoped_ptr members.

  ///
  /// Loops forever.
  ///
  /// Must be called in the same thread as creation of the object.
  ///
  /* 
   * IO线程创建了EventLoop对象,是这个类的核心接口
   * 用来启动事件循环 
   * EventLoop::loop()->Poller::Poll()获得就绪的事件集合
   * 再通过Channel::handleEvent()执行就绪事件回调
   */
  void loop();

  /// Quits loop.
  ///
  /// This is not 100% thread safe, if you call through a raw pointer,
  /// better to call through shared_ptr<EventLoop> for 100% safety.
  //终止事件循环
  void quit();

  ///
  /// Time when poll returns, usually means data arrival.
  ///
  Timestamp pollReturnTime() const { return pollReturnTime_; }

  int64_t iteration() const { return iteration_; }

  void runInLoop(const Functor& cb);

  void queueInLoop(const Functor& cb);

  size_t queueSize() const;

#ifdef __GXX_EXPERIMENTAL_CXX0X__
  void runInLoop(Functor&& cb);
  void queueInLoop(Functor&& cb);
#endif

  // timers

  //在某个绝对时间点执行定时回调
  TimerId runAt(const Timestamp& time, const TimerCallback& cb);
  ///
  ///
  //相对时间 执行定时回调
  TimerId runAfter(double delay, const TimerCallback& cb);
  ///
  //每隔interval执行定时回调
  TimerId runEvery(double interval, const TimerCallback& cb);
  ///
  ///
  //删除某个定时器
  void cancel(TimerId timerId);

#ifdef __GXX_EXPERIMENTAL_CXX0X__
  TimerId runAt(const Timestamp& time, TimerCallback&& cb);
  TimerId runAfter(double delay, TimerCallback&& cb);
  TimerId runEvery(double interval, TimerCallback&& cb);
#endif

  // internal usage
  // 唤醒IO线程
  void wakeup();
  // 更新某个事件分发器
  // 调用poller->updateChannel(channel)完成 fd 向事件集合注册事件及事件回调函数
  void updateChannel(Channel* channel);
  // 删除某个事件分发器
  void removeChannel(Channel* channel);
  bool hasChannel(Channel* channel);

  // pid_t threadId() const { return threadId_; }
  void assertInLoopThread()
  {
    if (!isInLoopThread())  //若运行线程不拥有EventLoop则退出,保证one loop per thread
    {
      abortNotInLoopThread();
    }
  }
  /* 判断当前线程是否为拥有此 EventLoop 的线程 */
  bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); }
  // bool callingPendingFunctors() const { return callingPendingFunctors_; }
  bool eventHandling() const { return eventHandling_; }

  void setContext(const boost::any& context)
  { context_ = context; }

  const boost::any& getContext() const
  { return context_; }

  boost::any* getMutableContext()
  { return &context_; }

  /* 返回此线程的EventLoop对象 */
  static EventLoop* getEventLoopOfCurrentThread();

 private:
  /* 在不拥有EventLoop 线程中终止 */
  void abortNotInLoopThread();
  /* wakeupFd_ 上可读事件回调 */
  void handleRead();  // waked up
  /* 执行队列pendingFunctors 中的用户任务回调 */
  void doPendingFunctors();

  void printActiveChannels() const; // DEBUG

  typedef std::vector<Channel*> ChannelList;

  bool looping_; /* atomic */ //运行标志
  bool quit_; /* atomic and shared between threads, okay on x86, I guess. */ //退出循环标志
  bool eventHandling_; /* atomic */
  bool callingPendingFunctors_; /* atomic *///是否正在执行用户任务回调
  int64_t iteration_;
  const pid_t threadId_;    //EventLoop 的附属线程ID
  Timestamp pollReturnTime_;
  boost::scoped_ptr<Poller> poller_;        //多路复用类Poller
  boost::scoped_ptr<TimerQueue> timerQueue_;//定时器队列用于存放定时器

  int wakeupFd_;    //eventfd返回的eventfd,用于唤醒EventLoop所在的线程
  // unlike in TimerQueue, which is an internal class,
  // we don't expose Channel to client.
  // 通过wakeupChannel_观察wakeupFd_上的可读事件
  // 当可读表明需要唤醒EventLoop所在线程执行用户回调
  boost::scoped_ptr<Channel> wakeupChannel_;
  boost::any context_;

  // scratch variables
  ChannelList activeChannels_;      //活跃的事件集合,类似epoll的就绪事件集合
  Channel* currentActiveChannel_;   //当前活跃的事件

  mutable MutexLock mutex_;
  std::vector<Functor> pendingFunctors_; // @GuardedBy mutex_ //存放用户任务回调
};

}
}
#endif  // MUDUO_NET_EVENTLOOP_H

下面主要看一下 loop() 和 quit() 的实现:

/* 主循环,监听事件集合,执行就绪事件的处理函数 */
void EventLoop::loop()
{
  assert(!looping_);
  assertInLoopThread();
  looping_ = true;
  quit_ = false;  // FIXME: what if someone calls quit() before loop() ?
  LOG_TRACE << "EventLoop " << this << " start looping";

  while (!quit_)
  {
    activeChannels_.clear();
    /* activeChannels_ 为就绪事件集合 */
    pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_);
    ++iteration_;
    if (Logger::logLevel() <= Logger::TRACE)
    {
      printActiveChannels();
    }
    // TODO sort channel by priority
    eventHandling_ = true;
    for (ChannelList::iterator it = activeChannels_.begin();
        it != activeChannels_.end(); ++it)
    {
      currentActiveChannel_ = *it;  //取一个就绪事件
      currentActiveChannel_->handleEvent(pollReturnTime_);  //执行相应事件回调
    }
    currentActiveChannel_ = NULL;
    eventHandling_ = false;
    doPendingFunctors();
  }

  LOG_TRACE << "EventLoop " << this << " stop looping";
  looping_ = false;
}

void EventLoop::quit()
{
  quit_ = true;     //设置标志位,有延迟不会马上停止循环,当下次检查while(!quit_)时起效
  // There is a chance that loop() just executes while(!quit_) and exits,
  // then EventLoop destructs, then we are accessing an invalid object.
  // Can be fixed using mutex_ in both places.
  if (!isInLoopThread())
  {
    wakeup();   //其他线程唤醒 EventLoop线程并终止
  }
}

主要是先了解 Reactor 模式的关键结构,所以上面注释的内容是 muduo 的 Reactor 模式的核心内容,一些细节没有详细说明。流程图如下:
这里写图片描述

Redis源码剖析(一)服务器与客户端交互流程 Redis中的C/S模型Redis底层还是基于网络请求的,对于单机数据库而言,网络请求仅仅是在一台机器上交互,即服务器客户端都在一台计算机上当在终端输入redis-serve时,便启动了一个Redis服务器,随后开始初始化内部数据,对于Redis而言包括 读取配置文件初始化内部参数 创建默认数据库(默认为16个) 创建监听套接字并绑定回调函数(接收客户端连接请求) 执行事件驱动循环,开始响应客户端请 阅读详情

相关推荐

C++开源项目学习指南:从入门到精通的实战路径

C++作为一门支撑操作系统、游戏引擎、数据库等核心系统的基础编程语言,其高性能和底层控制能力是构建复杂软件系统的基石。理解其核心原理,如内存管理、编译过程和并发模型,对于掌握系统级编程至关重要。现代C++标准(C++11/14/17/20)引入了智能指针、移动语义等特性,极大地提升了开发效率和代码安全性,其技术价值在于能够构建出既高效又可靠的大型软件。在实际应用场景中,无论是开发**高性能网络服务器**、**实时图形渲染引擎**,还是构建分布式存储系统,深入学习和借鉴优秀的开源项目都是快速提升工程能力的捷径

weixin_30647065的博客 408

Muduo源码剖析--整体架构

git: GitHub - chenshuo/muduo: Event-driven network library for multi-threaded Linux server in C++11git clone https://github.com/chenshuo/muduo.git安装依赖库:编译:如何在自己的程序中使用muduomuduo是静态链接的c++程序库, 使用muduo的时候,只需要设置好头文件的路径和库文件路径并链接相应的静态库文件(0lmuduo_net -lmuduo_base)

Birate的博客 1398

muduo日志库原理以及源码分析

muduo的这些特点使得其作为高性能日志库被广泛使用。

m0_70418130的博客 903

Muduo源码剖析

muduo源码剖析

YanWenCheng_的博客 1215

基于PLC的冲压件自动生产线电气系统设计【附数据】

考虑到生产线的高效运行和维护简便性,选择了三菱FX系列的可编程逻辑控制器(PLC)作为核心控制单元,该型号的PLC因其高可靠性和强大的功能集,在工业自动化领域有着广泛的应用。最后,在完成初步的软件开发后,进行了多轮次的模拟测试和现场调试,逐步修正了存在的问题,最终达到了预期的功能要求。例如,对于进料环节,选用了光电传感器以精确检测物料的位置;此外,为了适应不同尺寸的工件加工需求,还特别设计了一套可调节的夹具系统,不仅提高了生产线的灵活性,同时也降低了更换模具的成本。✅ 感恩科研路上每一位志同道合的伙伴!

lagougongzuoshi的博客 1291

FPGA网络调试实战:基于88E1111和DP83867两款PHY芯片的SGMII-UDP数据回环对比与避坑指南

本文深入对比了88E1111与DP83867两款PHY芯片在SGMII-UDP数据回环中的性能差异,涵盖硬件设计、Vivado工程配置及调试技巧。通过实测数据揭示DP83867在吞吐量、延迟和功耗方面的优势,并提供FPGA网络调试中的常见问题解决方案,助力工程师高效完成项目开发。

weixin_31682031的博客 383

muduo库源码分析和总结

陈硕大神的muduo库设计巧,但是难读懂,这里简单做个总结,注意回调函数太多,需仔细研究回调的传递和调用。、,其次陈硕认为网络编程的本质是处理三个半事件,重点关注之连接的建立连接的断开包括主动断开和被动断开消息到达,文件描述符可读消息发送完毕。这个算半个事件。...

woodslay的博客 1678

HWSD2.0土壤数据处理实战:从下载到属性提取全流程解析

本文详细解析了HWSD2.0全球土壤数据库从数据下载、属性筛选到GIS空间连接与提取的全流程实战方法。重点介绍了如何利用ArcGIS Pro等工具,将土壤单元编码与黏粒含量等属性表关联,最终生成全球土壤属性专题栅格图,为农业规划与生态研究提供关键数据支撑。

weixin_29214691的博客 610

muduo 网络库源码解析和使用

Muduo库是陈硕开发的Tcp网络编程库,采用了Reactor模型,非常适合网络编程初学者。出于个人学习目的阅读了其源码实现,并梳理了其中核心模块的执行流程,以及试图揣测作者一些部分的实现心得,并从中学到了很多工程实践知识,首次切实感受到多线程的种种困境;

weixin_44835655的博客 1767

基于Python的船舶AIS数据爬取与分析:从数据采集到可视化实战

本文详细介绍了如何使用Python构建一个完整的船舶AIS数据爬取与分析系统。文章从AIS系统的基本原理讲起,逐步深入到数据采集、存储、清洗、分析和可视化的全流程。我们将使用最新的Python技术栈,包括aiohttp异步爬虫、MongoDB存储、Pandas数据处理以及Pydeck三维可视化等。本文提供了完整的代码实现,并探讨了在实际应用中可能遇到的问题及其解决方案。关键词:AIS数据、Python爬虫、异步IO、数据可视化、海事分析。

2201_76125261的博客 811

YOLO数据集实现数据增强的方法(裁剪、平移 、旋转、改变亮度、加噪声等)

数据集样本太少怎么办?数据集优质图像不够怎么办?如何做到更好的数据预处理?一文带你学会数据增强,还可实现带标签的扩充。

路人贾的博客 4万+

【muduo源码学习】源码分析之Channel、EventLoop和Selector

在本篇本章中,将重点介绍该模型中的IO事件处理部分在 muduo 网络库中是如何实现的。

升级打怪记录 869

【知识图谱】neo4j安装与配置_社区版_2025(附完整安装软件包)

(经历过各种版本NEO4J,遇见杂七杂八的各种问题,也看过非常多很好的教程,特此来分享一下,为大家排排坑。

知识靠谱的博客 2万+

Muduo 网络库源码分析 之 关键技术点总结

最近又把muduo网络库仔细研究了一遍,收获良多。本文将对muduo中的设计思想以及关键的技术细节进行总结和分析,当然由于篇幅的原因这里更多的是对关键技术的简略提及,具体细节还需要读者自己去查找学习资料。muduo/base Date类 日期类的封装,使用Julian(儒略日)可以方便的计算日期差。具体公式和思想见 儒略日的计算 Exception类 异常类的封装,对外提供what()输出错误信

NK_test的博客 3328

YOLOv11 改进 - C2PSA | C2PSA融合TSSA(Token Statistics Self-Attention)令牌统计自注意力,优化遮挡目标感知

本文介绍了Token Statistics Self-Attention(TSSA)机制,并将其集成到YOLOv11中。传统自注意力计算复杂度高,TSSA进行了范式转变,基于token统计特征实现高效注意力交互。它通过“算法展开”推导得出,以“最大编码率降低”为目标,实现特征学习。TSSA包含动态分组和低秩投影优化两步创新,具备线性复杂度。我们将TSSA代码集成到YOLOv11的C2PSA模块中。实验表明,改进后的YOLOv11在目标检测任务中表现良好,验证了TSSA机制的有效性。

魔改工程师的博客 668

YOLOv11改进策略【Neck】| 替换RT-DETR中的CCFF跨尺度特征融合颈部结构,优化计算瓶颈与冗余问题

RT-DETR中的CCFF模块用于优化多尺度特征融合,解决传统方法的计算瓶颈与冗余问题,提升模型速度与精度,以适应实时检测需求。

Limiiiing的博客 1830

muduo网络库源码分析(1) —— Reactor的关键结构

主要参考了陈硕的书《Linux多线程服务端编程》,结合自己的理解摘了一些重要的的句子和函数、数据成员进行分析。 使用的代码自己用C++11抄了一个muduo库,简单版本,文章的代码删除了所有的Log和assert。 TCP网络编程最本质是的处理三个半事件 连接建立:服务器accept(被动)接受连接,客户端connect(主动)发起连接 连接断开:主动断开(close、shutdown),被动断开(read返回0) 消息到达:文件描述符可读 消息发送完毕:这算半个。对于低流量的服务,可不必关心这个事件;

qq_53111905的博客 990

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

muduo_net源码分析(1):Reactor的关键结构

Muduo网络库简介 muduo是一个基于Reactor模式的现代c++网络库。它主要采用非阻塞_IO模型,基于时间驱动和回调,原生支持多核多线程,适合编写Linux服务端多线程网络应用程序 Reactor的关键结构 Reacor的核心是事件分发机制,也就是将IO复用模型(poll/epoll)那里拿到的io事件分发给各文件描述符的事件处理函数。为了实现这个机制,我们需要编写Channel class和Poller class。下面说明一下这两个类。 1 Channel class Channel是为文件

wenxingxingxing的博客 189

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Muduo网络库源码分析(一) EventLoop事件循环(Poller和Channel)

从这一篇博文起,我们开始剖析Muduo网络库的源码,主要结合《Linux多线程服务端编程》和网上的一些学习资料! (一)TCP网络编程的本质:三个半事件 1. 连接的建立,包括服务端接受(accept) 新连接和客户端成功发起(connect) 连接。TCP 连接一旦建立,客户端和服务端是平等的,可以各自收发数据。 2. 连接的断开,包括主动断开(close 或shutdown) 和被动断开

NK_test的博客 7025

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

muduo_net源码分析(2):定时器timer的实现

传统的Reactor通过控制select和poll的等待时间来实现定时,而现在在Linux中有了timerfd,我们可以用和处理IO事件相同的方式来处理定时,代码的一致性更好。 为什么选择timerfd 常见的定时函数有如下几种: sleep alarm usleep nanosleep clock_nanosleep getitimer / setitimer timer_create / timer_settime / timer_gettime / timer_delete timerf

wenxingxingxing的博客 270

从零实现高并发C++网络库

实现一个基于Reactor模式的高性能网络库,模仿muduo的核心设计思想,支持多线程并发处理TCP连接,具备事件驱动、非阻塞IO、线程池等特性。该实现需要深入理解Linux网络编程、多线程编程、设计模式等知识,建议分模块逐步实现,先从单线程Reactor开始,逐步添加线程池和高级特性。

tb8EWlsQS4的博客 310

Muduo源码阅读

最近简单读了下muduo的源码,本文对其主要实现/结构简单总结下。 muduo的主要源码位于net文件夹下,base文件夹是一些基础代码,不影响理解网络部分的实现。muduo主要类包括: EventLoopChannelPollerTcpConnectionTcpClientTcpServerConnectorAcceptorEventLoopThreadEventLoopThreadPo

铁匠Smith先生的专栏 5732

Linux多线程服务器端编程

目录 Linux多线程服务器端编程 线程安全的对象生命期管理 对象的销毁线程比较难 线程同步精要 借shared_ptr实现写时拷贝(copy-on-write) 多线程服务器的适用场合与常用编程模型 单...

angbao4913的博客 1037

Muduo - Reactor模式

Muduo - Reactor模式 一、Reactor 是什么 wiki的中文定义:Reactor模式是事件驱动的,有一个或多个并发输入源,有一个Service Handler,有多个Request Handler,这个Service Handler会同步的将输入的请求(Event)多路复用的分发给相应的Request Handler。 从上述文字中我们可以看出以下关键点: 事件驱动(event handling) 可以处理一个或多个输入源(one or more inputs) 通过Servic

mohuak的博客 690

muduo源码学习(一):Reactor模式与muduo

Reactor模式 reactor模式一共有5种角色构成: Handle(句柄或者描述符) : 本质上是一种资源,由操作系统提供。因为再windows中我们叫句柄handle,在linux中我们可以认为就是描述符。作为一个事件代号。举例说,网络编程中的socket描述符。事件既可以来自于外部(如:客户端的连接请求,客户端的发送的数据可读事件),也可以来自于内部(操作系统产生的定时器事件等)。它本...

qq_22121229的博客 299
上一篇: FastCGI 协议分析与C语言实现实例
下一篇: Muduo网络库源码分析之定时器的实现
Tanswer_
博客等级 码龄11年 231粉丝 91原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值