epoll多路复用

为了实现及时聊天,即两方可以任意向对方发送连续的多条消息的功能,需要使用epoll。在内核中,socket对象缓冲区recv()、标准输入缓冲区input()都分配了一段内存,内存对应一个整型编号(数组下标),这个编号就是文件描述符file describer

我们创建epoll对象,注册要监控的fd和事件类型,让epoll去监控哪几个缓冲区发生了指定事件,以列表的形式主动报告给用户进程。

1.使用epoll编写即时聊天

服务器代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import socket
import select
import sys

def tcp_server():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s_addr = ('', 2000)
server.bind(s_addr)
server.listen(128) # 被动监听,激活端口
new_client, new_client_addr = server.accept()
print(new_client_addr)
epoll = select.epoll() # 创建一个epoll对象
# 注册要监控的缓冲区,发生指定事件向用户进程汇报
epoll.register(new_client.fileno(), select.EPOLLIN)
epoll.register(sys.stdin.fileno(), select.EPOLLIN)
while True:
# 一直等只会在这里卡住,谁的缓冲区有数据,就填写到eventslist,列表里变存元组 (fd, 事件)
events_list = epoll.poll(-1)
for fd,event in events_list:
if fd == new_client.fileno():
# recv缓冲区有数据
data = new_client.recv(100).decode('utf8')
if data:
print(data)
else:# 一旦对端断开,recv不会卡主,会返回空,内核会把client标记为一直可读
print('对方断开了')
return
elif fd == sys.stdin.fileno():
# input缓冲区有数据
try: # 按ctrl d让服务器断开
data = input()
except Exception as e:
print('不想输入了,I want leave')
return
new_client.send(data.encode('utf8'))
server.close()


if __name__ == '__main__':
tcp_server()

即时聊天测试结果

2.使用epoll实现聊天室

实现多人聊天,

  1. 新增客户端断开后可以再次连接,服务器端不会退出(epoll解除绑定、关闭client对象);
  2. 新建一个client列表,存储每个客户端对象,循环遍历查看缓冲区有无数据;
  3. 文件协议设计,聊天室增加用户名功能。

聊天室测试结果

3.持续发送多个文件,协议设计

使用TCP连接发送文件时,首先要以字节流形式传送,如果持续发送多个文件,文件名1+文件1内容+文件名2+文件2内容+。。。存在粘包问题,两次发送的报文挨在一起,分不开。

我们采用开火车的方式解决粘包,如下所示。

  1. 小火车
  • 火车头填写长度:字节数,python需pack为4字节整型数
  • 火车车厢填写内容:字符串字节流
文件名 文件内容
车头 文件名长度(4B) 文件内容总长度(4B)
车厢 文件名 文件内容

Python的struct模块,提供了一种机制,能将int、float等基本数据类型打包成字符串(实际上相当于其他语言的字节流),可以在网络上传输,而接收端也可以通过解包还原出初始的数据。

  • pack(fmt, var1, var2,…)
    按照给定的格式(fmt),把数据封装成字符串(实际上类似于C结构体的字节流);

  • unpack(fmt, string)
    按照给定的格式(fmt)解析字节流(string),返回解析出来的tuple元组;

  • calcsize(fmt)
    计算给定的格式(fmt)占用多少字节的内存

  • Python中的struct模块

4. 总结

  • 写代码的时候对不确定的代码实现效果,自己动手写一个简单的例子验证一下就可以,这样就不至于代码写了一大堆,不确定错误在哪里。

.png


epoll多路复用
http://paopaotangzu.icu/2024/08/22/3.epoll多路复用/
作者
paopaotangzu
发布于
2024年8月22日
许可协议