socket 事件中怎么才算是可读可写呢?

socket 事件中怎么才算是可读可写呢?

最近在研究 IO 多路复用中的 epoll API

看到下面这段代码:

import socket

import 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

回到顶部