Muduo网络库的实现Channel class(二)

本文详细解析Muduo网络库中的Channel类,探讨其在事件分发机制中的作用,包括Channel对象与Event Loop的关系、回调函数的实现(如ReadCallback、WriteCallback)以及如何利用boost::function和boost::bind实现不同IO事件的通用回调。同时,讨论了在Muduo项目中如何处理不同IO事件回调的任务差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

源码下载以及安装点击链接https://blog.youkuaiyun.com/YoungSusie/article/details/90021742

分类 Muduo网络库编程 学习笔记

Reactor 最核心的 事件分发机制,即将IO复用拿到的IO事件分发给各个对应的文件描述符的事件处理函数。

每个Channel对象自始自终只属于一个Event Loop,因此每个Channel 对象只属于一个IO线程。 每个Channel 对象自始自终只负责一个文件描述符的IO事件分发, 但是Channel 对象并不拥有这个文件描述符,不会在析构的时候关闭这个文件描述符。 Channel 会将不同的IO事件分发为不同的回调,例如 Readcallback 、 writecallback 等,回调采用的是 boost::function,用通用的函数模板,实现不同fd的事件回调函数 表示,用户无需继承Channel, Channel不是基类。

1、学习笔记

  1. boost::function 和 boost::bind 头文件<booost/function.hpp>
    boost.function 库用来提供一个对象化的函数指针。这个对象用于封装另一个函数。

boost::function 是一个函数包装器(也叫函数模板) ,可以用来代替拥有相同返回类型,相同参数类型,以及相同参数个数的各个不同的函数。函数的具体实现可以根据需要改变,类似C++中的多态。

typedef boost::function<int(int,char)> fun; //函数返回值为int ,参数分别为int char
fun f;
f = &test;//test 为具体实现的函数名。

当function f 首次创建时,不保存任何函数,是空的。不可调用一个没有保存任何函数或者函数对象的 function,会抛出错误。 需要将另外的函数的指针赋值给 f 。

  1. 类成员函数------转载自https://blog.youkuaiyun.com/benny5609/article/details/2324474

Boost.Function 不支持参数绑定,这在每次调用一个function 就要调用同一个类实例的成员函数时是需要的。幸运的是,如果这个类实例被传递给function的话,可以直接调用它的成员函数。这个function 必须包含类的类型以及成员函数的签名。换言之,显示传入的 类实例要作为隐式传递的第一个参数,this。

  • 传递值
class some_class {
public:
  void do_stuff(int i) const {
    std::cout << "OK. Stuff is done. " << i << '/n';
  }
};

如果成员函数 do_stuff要从一个boost::function 实例里被调用,需要function接受一个some_class实例,签名的其他部分为一个void 返回以及一个int参数。

boost::function<void(some_class,int)> f;

调用的时候:

f=&some_class::do_stuff;
f(some_class(),2);
  • 传递引用
boost::function<void(some_class&,int)> f;
f=&some_class::do_stuff;
some_class s;
f(s,1);
  • 传递指针
boost::function<void(some_class*,int)> f;
f=&some_class::do_stuff;
some_class s;
f(&s,3);
  1. 与Boost.Function 一起使用 Boost.Bind

Boost.Bind 为普通函数、成员函数以及成员变量提供参数绑定,非常适合于Boost.Function 。 由于我们使用的类本身并不是函数对象,那么,可以用Boost.Bind 将它们转变为函数对象,然后用Boost.Function 来保存以便稍后调用。

区别于基于函数指针 (typedef void (*function_type)(int); )的事件回调,基于函数指针的事件回调,回调类型收到限制。

通过使用 Boost.Function,我们可以在当与某个支持参数绑定的库结合使用时,轻而易举地把上下文提供给调用的函数。这是本库最常见的用途之一,把业务逻辑从表示层分离出来。

class button
{
public:
    boost::function<void()> onClick;
};

class player
{
public:
    void play();
    void stop();
};

button playButton, stopButton;
player thePlayer;

void connect()
{
    playButton.onClick = boost::bind(&player::play, &thePlayer);  //
    stopButton.onClick = boost::bind(&player::stop, &thePlayer);
}
  1. muduo项目中需要考虑的问题

需要考虑的问题: 不同的IO事件回调需要处理不同的任务,这些任务的返回值类型,参数类型,参数个数不一定相同,如何像上面一样定义一个函数模板呢?

typedef boost::function<void()>;
//这里的两种定义都可以
typedef boost::function<void(void)>;

void 类型(空类型) 其实是C 中四种数据类型之一(基本数据类型,构造类型,指针类型,空类型)。空类型主要是用来修饰返回类型与函数参数的,注意空类型不能用来定义变量,eg :void i 是错误的。 可以认为空类型是一个抽象的基类,可以表示所有的类型。

参数的传入可以使用boost::bind,形成一个通用的函数模板。

2、Channel class

Channel class 的成员函数只能在IO线程被调用,因此更新数据成员不需要加锁,不存在跨线程访问的问题。

#ifndef _CHANNEL_H
#define _CHANNEL_H

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

class  EventLoop;

class Channel : boost::noncopyable
{
  public:
	typedef boost::function<void()> EventCallback;
	Channel(EventLoop * loop,int fd); 
	void handleEvent();	
	
	void setReadCallback(const EventCallback& cb)
	{ readCallback_ = cb;}
	void setWriteCallback(const EventCallback& cb)
	{ writeCallback_ = cb;}
	void setErrorCallback(const EventCallback& cb) 
	{ errorCallback_ = cb;}

	int fd() const
	{ return fd_;} 
	int events() const
	{ return events_;}
	void set_revents(int revt)
	{ revents_ = revt;}
	bool isNoneEvent() const
	{ return events_ == kNoneEvent;} 

	void enableReading()
	{ events_ |= kReadEvent;
	  update();} 

	int index()
	{ return index_;}
	void set_index(int idx)
	{ index_ = idx;}

	EventLoop * ownerLoop()
	{ return loop_;} 

  private:
	void update();


	static const int kNoneEvent;
	static const int kReadEvent;
	static const int kWriteEvent;

	EventLoop * loop_;
	const int fd_;
	int events_;
	int revents_;
	int index_; //POLL用到的索引


	EventCallback readCallback_;
	EventCallback writeCallback_;
	EventCallback errorCallback_;
};

#endif
#include "Channel.h"
#include "EventLoop.h"
#include <poll.h>

const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = POLLIN | POLLPRI;
const int Channel::kWriteEvent = POLLOUT;

Channel::Channel(EventLoop* loop, int fdArg)
	:loop_(loop),fd_(fdArg),events_(0),revents_(0),index_(-1)
{
}

void Channel::update()
{
	loop_updateChannel(this);
}

void Channel::handleEvent()
{
	if(revents_ & POLLNVAL){
		std::cout<< "Channel::handle_event fd is not open" << endl;
	if(revents_ & (POLLERR | POLLNVAL)){
		if(errorCallback_) errorCallback_();} 
	if(revents_ & (POLLIN | POLLPRI | POLLRDHUP)){
		if(readCallback_) readCallback_();}
	if(revents_ & POLLOUT){
		if(writeCallback_) writeCallback_();}
}

分类 Muduo网络库编程 学习笔记

本文摘自陈硕老师的linux多线程服务端编程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值