当前位置: 首页 > wzjs >正文

做外汇模拟的网站技术培训机构排名前十

做外汇模拟的网站,技术培训机构排名前十,中央新一届领导任命公告公示,如何制作网站后台目录 由联合体epoll_data引出类Channel 结构体epoll_data_t Channel类 Channel类的使用 Epoll类的改变 由联合体epoll_data引出类Channel 在之前使用epoll时,有使用到一个结构体epoll_event // 这是联合体,多个变量共用同一块内存 typedef union…

目录

由联合体epoll_data引出类Channel

结构体epoll_data_t

Channel类

Channel类的使用

Epoll类的改变


由联合体epoll_data引出类Channel

在之前使用epoll时,有使用到一个结构体epoll_event

// 这是联合体,多个变量共用同一块内存  
typedef union epoll_data {  void *ptr;        // 指针类型,可以指向任意数据类型,通常用于存储自定义数据结构的指针  int fd;          // 文件描述符,常用于指代与 epoll_ctl 操作相关的文件描述符  uint32_t u32;    // 32 位无符号整形,可用于存储无符号整数类型的用户数据  uint64_t u64;    // 64 位无符号整形,适合存储较大的整数或数据  
} epoll_data_t;  // epoll_event 结构体用于描述要监控的事件及其相关的数据  
struct epoll_event {  uint32_t events;      /* epoll 事件类型 */  epoll_data_t data;    /* 用户数据变量 */  
};  // 调用 epoll_ctl 函数来控制与 epoll 相关的事件  
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);  
结构体epoll_data_t

epoll_event中有个成员变量data,这是一个联合体。初学情况下,我们会使用data的成员变量fd。

回想一下,我们使用epoll_event的时候

// 调用 epoll_wait,等待事件发生  
// epfd: epoll 实例的文件描述符  
// events: 用于存储被激活事件的数组  
// MAX_SIZE: 事件数组的最大容量  
// -1: 阻塞模式,直到有事件发生  
int nums = epoll_wait(epfd, events, MAX_SIZE, -1);  // 遍历所有返回的事件  
for (int i = 0; i < nums; ++i) {  // 检查当前事件的文件描述符是否为监听文件描述符 (lfd)  if (events[i].data.fd == lfd) {  handl1(); // 处理连接操作,例如接受新的客户端连接  }  // 检查是否为可读事件  else if (events[i].events & EPOLLIN) {  handl2(); // 处理通信操作,例如读取客户端发送的数据  }  
}

这样我们只使用联合体中成员fd,只能得到文件描述符。

当时当我们想要获取关于该fd更多的信息时候,就不妥了。比如想获取该用户连接成功的时间。这时可能有朋友想到可以使用std::unordered_map来存储,但这就需要借助外部的变量。

那么,我们换一种思路,联合体epoll_data中还有个变量ptr,其是个void*指针。那就可以指向任何一块地址空间,也可以指向一个类对象,这就可以包含关于这个文件描述符的更多信息。

按照上面的思路,我们可以把firstTime和fd构成一个结构体,之后就使用联合体epoll_data的ptr,不使用fd。

// 用户自定义的数据结构,用于存储与文件描述符相关的详细信息  
struct moreMsg {  int fd;               // 文件描述符,标识特定的套接字或文件  int64_t firstTime;    // 连接成功的时间,通常用于记录客户端的连接时间  int events;           // epoll_wait 返回的活跃事件,用于存储事件类型(如可读、可写等)  
};  

 在新用户连接成功的时候,就创建moreMesg结构体,给fd和firstTime和events都赋值。这个epoll_data 会随着 epoll_wait 返回的 epoll_event 一并返回。那附带的自定义消息,就是ptr指向这个自定义消息。

那么我们可以把一个文件描述符封装成一个类,这个类里面有更多的关于这个文件描述符的信息,我们把他叫做Channel类,用这个void*指针指向Channel类对象

Channel类

class Channel {  
public:  // 构造函数,初始化与 Epoll 对象的指针和文件描述符  Channel(Epoll* ep, int fd);  // 设置关注的事件类型  void setEvent(uint32_t events);  // 获取关注的事件类型  uint32_t Event() const;  // 设置从 epoll_wait 返回的事件类型  void setRevent(uint32_t revents);  // 获取从 epoll_wait 返回的事件类型  uint32_t Revent() const;  // 检查通道是否在 epoll 中  bool isInepoll();  // 设置通道是否在 epoll 中的状态  void setInEpoll(bool in);  // 获取该通道关联的文件描述符  int fd();  // 启用读取事件  void enableReading();  private:  Epoll* ep_;          // 指向关联的 Epoll 对象的指针,用于更新事件  int fd_;             // 文件描述符,表示该通道关联的文件或 socket  uint32_t events_;    // 用户关心的事件类型,例如可读或可写  uint32_t revents_;   // 从 epoll_wait 返回的活跃事件类型  bool isInEpoll_;     // 标记该通道是否在 epoll 的红黑树中  
};  

成员变量:

  • 那一定会有文件描述符fd,每个Channel对象自始至终只负责一个fd,但是它不拥有这个fd,即也不会在析构的时候关闭这个fd
  • 接着有Epoll类对象,我们要通过epoll把fd添加到epoll上,一个channel只跟着一个Epoll,通过enableReading()函数添加fd到该epoll上。
  • events_是channel关心的IO事件,由用户设置(即用户想监听该fd的哪类事件,如读事件)
  • revents_是通过epoll_wait()返回的目前该fd活动的事件(即我们要根据这个类型执行相对的操作,返回读事件就执行读操作)。
  • isInEpoll_是用于判断该fd是否在epoll红黑树上,进而判断是使用EPOLL_CTL_ADD还是使用EPOLL_CTL_MOD。

一个channel就对应了一个客户端,或者服务器端。

Channel类的使用
// 创建一个 Socket 对象,用于网络通信  
Socket serv_socket;  // 创建一个 Epoll 对象,用于管理多个文件描述符的事件  
Epoll ep;  // 创建一个 Channel 对象,关联先前创建的 Epoll 对象和 Socket 的文件描述符  
Channel* ch = new Channel(&ep, serv_socket.fd());  // 说明:Channel 的构造函数需要传入一个指向 Epoll 对象的指针  
// 以及要监视的文件描述符(即 serv_socket.fd()),  
// 这样 Channel 才能在事件被触发时有效地与 Epoll 进行交互。  

那接下来我们需要把fd添加到epoll红黑树上,之前是使用ep.update(fd,EPOLLIN,EPOLL_CTL_ADD);

那现在是通过Channel去把fd添加到红黑树上

// 启用读取事件的成员函数  
void enableReading() {  // 设置关注的事件类型为可读事件(EPOLLIN)  setEvent(EPOLLIN);  // 更新 epoll 中的通道,将这个通道的新事件添加到 epoll 中  ep_->updateChannel(this);  
}  // 设置关注的事件类型  
void setEvent(uint32_t events) {  // 将传入的事件类型存储到成员变量 events_ 中  events_ = events;  
}  

其主要是做了两件事,将要监听的事件events设置为读事件,然后用成员变量ep_去更新channel。

Epoll类的改变

Channel::enableReading()函数内部调用了Epoll::updateChannel(Channel*)。updateChannel成员函数的参数类型是Channel*,猜想函数内部是对channel做了一些操作的。

EpoLL类的updateChannel()的实现如下:

// 更新与 epoll 关联的通道  
void updateChannel(Channel* ch) {  // 获取通道的文件描述符  int fd = ch->fd();  // 创建 epoll_event 结构体,用于描述要修改的事件  struct epoll_event ev;  // 清空结构体  memset(&ev, 0, sizeof(ev));  // 将通道的指针存储在 epoll_event 结构中  ev.data.ptr = ch; // 这里很重要,我们使用指针而不是文件描述符  ev.events = ch->Event(); // 获取通道当前设置的事件类型  // 检查通道是否已在 epoll 树中  if (ch->isInepoll()) {  // 如果通道已经在 epoll 树中,修改其事件  epoll_ctl(epfd_, EPOLL_CTL_MOD, fd, &ev);  } else {  // 如果通道不在 epoll 树中,添加该通道  epoll_ctl(epfd_, EPOLL_CTL_ADD, fd, &ev);  ch->setInEpoll(true); // 设置通道已在 epoll 树上  }  
}  // 注意:原函数省略了错误判断,实际使用中应该检查  
// epoll_ctl 的返回值,处理可能的错误情况  

EPOLL的epoll_wait()函数代码也有修改,有了Channel之后,返回的就是vector<Channel*>

// 使用 Channel 类的 Epoll_wait 函数  
void Epoll_wait(vector<Channel*>& active, int timeout = 10) {  // 调用 epoll_wait,等待事件发生  int nums = epoll_wait(epfd_, events_, SIZE, timeout);  // 遍历返回的事件数量  for (int i = 0; i < nums; ++i) {  // 从 events_ 中获取 Channel 对象的指针  Channel* ch = static_cast<Channel*>(events_[i].data.ptr);  // 设置从 epoll_wait 返回的事件类型  ch->setRevents(events_[i].events);  // 将活跃的通道添加到 active 列表中  active.emplace_back(ch);  }  
}  // 以前的写法  
/*  
void Epoll_wait(vector<epoll_event>& active, int timeout = 10) {  // 调用 epoll_wait,等待事件发生  int nums = epoll_wait(epfd_, events_, SIZE, timeout);  // 遍历返回的事件数量  for (int i = 0; i < nums; ++i) {  // 将 events_ 中的每个事件添加到 active 列表中  active.emplace_back(events_[i]);  }  
}  
*/  

那么在编写服务端程序的时候,我们不再用epoll_events结构体,而是使用Channel。

// 创建一个 Epoll 对象  
Epoll ep;  // 事件循环,持续等待和处理 I/O 事件  
while (true) {  // 创建一个用于存储活动通道的向量  vector<Channel*> activeChannel;  // 调用 Epoll 的 Wait 函数,获取活跃的通道  ep.Epoll_Wait(activeChannel);  // 获取活跃通道的数量  int nums = activeChannel.size();  // 遍历所有活跃通道  for (int i = 0; i < nums; ++i) {  // 获取当前活跃通道关联的文件描述符  int fd = activeChannel[i]->fd();  // 如果文件描述符是监听文件描述符 (lfd),处理连接请求  if (fd == lfd) {  handle1(); // 处理新连接  }  // 检查当前通道是否有可读事件  else if (activeChannel[i]->Event() & EPOLLIN) {  handle2(); // 处理通信操作  }  }  
}  

这一节我们还没有使用回调函数,这不符合我们的要求的,这将在下一节中进行修改。

通过这一节的修改,我们可以获得关于epoll返回的活跃的文件描述符的更多信息,添加了Channel类。接下来也会逐渐实现Reator模式。

http://www.dtcms.com/wzjs/72861.html

相关文章:

  • 万户网络seo优化是什么
  • 行远金华网站建设公司免费b站推广短视频
  • 怎么能看出别人的网站是哪一家做公司网站模版
  • 潍坊个人做网站的公司b2b网站源码
  • 网络营销的特点主要有哪些初学seo网站推广需要怎么做
  • 动漫网站建设规划书模板中国网络推广网站排名
  • 网站建设模板一次收费全国疫情最新情况公布
  • 建设网站交纳党费百度小说排行榜
  • 网站分屏布局设计竞价推广的企业
  • 网站建设百度推广商品推广
  • 南通做网站ntwsd廊坊seo排名公司
  • 广州建网站价格外贸网站建设
  • 杭州建设局网站首页品牌推广的方式
  • 最好用的素材网站网页设计需要学什么
  • html制作音乐网站营销型网站的分类不包含
  • 百度的网站名济南seo怎么优化
  • 外国人学做中国菜 网站济南网站seo优化
  • 网站建设名词免费b2b网站推广
  • 微信上的小说网站是怎么做的北京百度seo服务
  • 自己建个电影网站可以吗推客平台
  • 局网站建设工作总结软文关键词排名推广
  • 强的小企业网站建设百度热门搜索排行榜
  • 如何制作餐馆网站杭州网站优化公司哪家好
  • 网站开发怎么切换多种语言快速排名优化推广手机
  • 自己做的网站怎么给别人访问成人职业技能培训班
  • 乐东黎族自治县住房建设局网站竞价排名点击
  • 山西省建设厅网站官网如何建立自己的博客网站
  • 手机网站设计框架怎样有效的做网上宣传
  • 网站建设的id调用怎么操作核心关键词和长尾关键词举例
  • 网站建设需要个体营业执照吗电商培训学校