基于linux epoll的聊天室

在ustc网络程序设计这门课中,设计实现了一个基于linux epoll的聊天室。

本文就记录一下在设计实现该聊天室的过程中学到的知识,以及讲讲这个小项目的实现。

IO多路复用

IO多路复用是一种基于事件驱动的编程模型,它允许单个进程或线程同时监听多个文件描述符(sockets或其他文件描述符),并在其中任何一个文件描述符就绪时进行处理。这样可以避免使用多线程或多进程进行并发处理,提高程序的效率。

在IO多路复用中,常见的有三种机制:select、poll和epoll。

1. select

  • 原理:

select 使用三个位图集合来表示文件描述符,通过对这些位图的轮询来判断文件描述符是否就绪。

  • 限制:

select 有一些限制,最主要的是文件描述符数量的限制,通常是 1024 个,因为使用三个位图,每个位图可以表示 1024 个文件描述符。

2. poll

  • 原理:

poll 也是通过轮询来判断文件描述符是否就绪,但是它使用一个结构数组来存储文件描述符的状态,避免了 select 中的位图限制。

  • 效率:

在文件描述符较多的情况下,poll 的效率相对较高。

3. epoll

  • 原理:

epoll 使用一个内核事件表来存储文件描述符的状态,通过系统调用将感兴趣的文件描述符加入到事件表中,从而实现了高效的事件通知。

  • 优势:

相较于 select 和 poll,epoll 具有更高的性能,特别在文件描述符较多的情况下,因为它避免了轮询,而是通过回调机制在事件发生时触发回调。

epoll把用户关心的文件描述符上的事件放在内核里的一个事件表中,从而无须像select和poll那样每次调用都要重复传入文件描述符集或事件集。但epoll需要使用一个额外的文件描述符,来唯一标识内核中的这个事件表。

epoll的主要函数

  1. epoll_create:创建一个 epoll 实例

int epoll_create(int size);

    • 用途:创建一个 epoll 实例,返回一个用于标识该实例的文件描述符。
    • 参数:
      • size:在内核中初始分配的事件表的大小,已经不再具有实际意义。
  1. epoll_ctl:控制 epoll 实例中的事件

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

    • 用途:向 epoll 实例中添加或删除事件。
    • 参数:
      • epfd epoll 实例的文件描述符。
      • op 操作类型,可以是 EPOLL_CTL_ADD、EPOLL_CTL_MOD 或 EPOLL_CTL_DEL。
      • fd 要添加、修改或删除的文件描述符
      • event 指向 epoll_event 结构的指针,描述要监控的事件类型以及用户数据。
  1. epoll_wait :等待就绪事件

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

    • 用途:等待 epoll 实例中的文件描述符上有事件发生。
    • 参数:
      • epfd epoll 实例的文件描述符。
      • events 指向 epoll_event 结构数组的指针,用于存储就绪事件。
      • maxevents events 数组的大小,表示最多等待多少个事件。
      • timeout 等待超时时间,单位为毫秒。传入 -1 表示一直等待,传入 0 表示立即返回,传入正整数表示等待指定毫秒数。
  1. struct epoll_event:表示就绪事件的结构

struct epoll_event { __uint32_t events; // 表示事件类型,可以是 EPOLLIN、EPOLLOUT、EPOLLRDHUP 等 epoll_data_t data; // 用户数据,可以是文件描述符等 };

    • 用途:表示就绪事件的结构,包含事件类型和相关的用户数据。

阻塞socket和非阻塞socket

阻塞 Socket:

在阻塞模式下,当执行一个 I/O 操作时,程序会一直等待,直到操作完成为止。对于阻塞套接字:

  1. 阻塞的 read 操作: 如果没有数据可读,read 调用将一直阻塞,直到有数据到达为止。这意味着程序会一直停在 read 函数这一行代码,无法进行其他任务。
  2. 阻塞的 write 操作: 如果输出缓冲区已满,write 调用将一直阻塞,直到有足够的空间写入为止。

非阻塞 Socket:

在非阻塞模式下,I/O 操作的调用会立即返回,而不会一直等待操作完成。程序可以继续执行其他任务或者轮询 I/O 操作的状态。

  1. 非阻塞的 read 操作: 如果没有数据可读,read 调用会立即返回,可能返回一个特定的错误码(如 EAGAIN 或 EWOULDBLOCK),表示当前没有数据可读。程序可以在后续的轮询中再次尝试读取。
  2. 非阻塞的 write 操作: 如果输出缓冲区已满,write 调用会立即返回,可能返回一个错误码,表示当前无法写入更多数据。程序可以在后续的轮询中再次尝试写入。

聊天室的实现

主要代码在码云仓库中,epoll-chatroom: 基于linux epoll的聊天室 (gitee.com)

代码中已经加入详细的注释便于理解

运行效果

make后启动服务器

添加三个终端运行客户端

每次有新用户加入,服务器就会通知其他所有用户

在客户端输入要发送的消息并回车,其他用户都会收到该消息

当有用户退出时,也会通知其他所有用户,该用户退出聊天

所有的操作在服务器端也会有日志输出

课程总结

从课程内容来说,网络编程,rpc,protobuf等技术知识对于找实习的同学来说有很大帮助。从考核标准来说,自愿形式的检查以及最后基于兴趣选择的大作业,在能让同学在学到知识的同时减轻了同学们的压力。

总结下来,收获很大

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值