socket 事件中怎么才算是可读可写呢?
最近在研究 IO 多路复用中的 epoll
API
看到下面这段代码:
import socketimport select #: epoll包含在select模块中
from datetime import datetime, timedelta, timezone
def get_utc_now_timestamp() -> datetime:
return datetime.utcnow().replace(tzinfo=timezone.utc)
def timestamp_translate_gmt(timestamp: datetime) -> str:
return timestamp.strftime('%a, %d %b %Y %H:%M:%S GMT')
EOL1 = b'\n\n'
EOL2 = b'\n\r\n'
http_response_start_line: bytes = b'HTTP/1.0 200 OK\r\n' # 状态行
http_response_body: bytes = b'Hello, world!' # 响应正文
http_response_headers: bytes = b'\r\n'.join([
f'Date: {timestamp_translate_gmt(get_utc_now_timestamp())}'.encode(
),
b'Content-Type: text/plain',
f'Content-Length: {len(http_response_body)}'.encode()
])+b'\r\n'
http_empty_line = b'\r\n'
http_response: bytes = (
http_response_start_line+http_response_headers+http_empty_line+http_response_body
)
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversocket.bind(('0.0.0.0', 8080))
serversocket.listen(1)
serversocket.setblocking(0) #: 默认情况下,socket处于阻塞模式
epoll = select.epoll() #: 创建一个epoll对象
# 在serversocket上注册读事件,读事件发生在serversocket每次接受socket连接时
epoll.register(serversocket.fileno(), select.EPOLLIN)
try:
# 文件描述符(整数)与其对应的网络连接对象的映射
connections = {}
requests = {}
responses = {}
while True:
# 查询epoll对象,看是否有感兴趣的事件发生
# 参数`1`表明我们最多会等待1秒钟
# 如果在这次查询之前有我们感兴趣的事件发生,这次查询将会立即返回这些事件的列表
events = epoll.poll()
# 事件是一个`(fileno, 事件code)`的元组
for fileno, event in events:
# 如果serversocket上发生了读事件,那么意味着有一个有新的连接
if fileno == serversocket.fileno():
connection, address = serversocket.accept()
# 将新的socket设为非阻塞
connection.setblocking(0)
# 给新创建的socket注册读事件(EPOLLIN),表明将有数据请求
epoll.register(connection.fileno(), select.EPOLLIN)
connections[connection.fileno()] = connection
# 收集各客户端来的请求
requests[connection.fileno()] = b''
responses[connection.fileno()] = http_response
elif event & select.EPOLLIN:
# 如果发生了读事件,从客户端读取数据
requests[fileno] += connections[fileno].recv(1024)
if EOL1 in requests[fileno] or EOL2 in requests[fileno]:
responses[fileno]=http_response
epoll.modify(fileno, select.EPOLLOUT)
elif event & select.EPOLLOUT:
# 如果发生了写事件,向客户端发送响应
# 每次向客户端发送一定长度的响应内容,每次都更新余下待发送的响应内容
byteswritten = connections[fileno].send(responses[fileno])
responses[fileno] = responses[fileno][byteswritten:]
# 响应已经发送完毕,一次请求/响应周期完成,不再监听该socket的事件了
if len(responses[fileno]) == 0:
epoll.modify(fileno, 0)
# 告诉客户端,关闭连接
connections[fileno].shutdown(socket.SHUT_RDWR)
# `HUP`(挂起)事件表明客户端断开了连接
elif event & select.EPOLLHUP:
# 注销对断开客户端socket的事件监听
epoll.unregister(fileno)
# 关闭连接,服务端关闭
connections[fileno].close()
del connections[fileno]
finally:
epoll.unregister(serversocket.fileno())
epoll.close()
serversocket.close()
所以怎么操作系统才算是一个事件是可读的呢?
判断该 socket
对应的 read
内核缓存区内的数据长度不为 0 吗?
- 长度大于
0
为发生可读事件? - 长度等于
0
为没有可读事件发生?
那怎么判断 可写事件 是否发生呢?
操作系统总不能是通过判断该 socket
对应的内核 write
缓冲区内的长度情况吧!
看上面那段代码中,发生可读时间是要专门写代码,显性的调用 epoll.modify(fileno, select.EPOLLOUT)
等于可写事件是程序员手动设置的是吗?
参考文章:
边缘触发和水平触发的轮询 (epoll) 对象
How To Use Linux epoll with Python
以上是 socket 事件中怎么才算是可读可写呢? 的全部内容, 来源链接: utcz.com/p/938256.html