gpt4 book ai didi

python - 检测套接字挂断而不发送或接收?

转载 作者:IT老高 更新时间:2023-10-28 22:20:39 26 4
gpt4 key购买 nike

我正在编写一个 TCP 服务器,它可能需要 15 秒或更长时间才能开始生成对某些请求的响应正文。如果响应需要超过几秒钟才能完成,一些客户端喜欢在其结束时关闭连接。

由于生成响应会占用大量 CPU,因此我希望在客户端关闭连接时立即停止任务。目前,直到我发送第一个有效载荷并收到各种挂断错误时,我才发现这一点。

如何检测到对端已关闭连接而没有发送或接收任何数据?这意味着对于 recv 所有数据都保留在内核中,或者对于 send 实际上没有数据传输。

最佳答案

select模块包含您需要的内容。如果您只需要 Linux 支持并且拥有足够新的内核,select.epoll() 应该会为您提供所需的信息。大多数 Unix 系统将支持 select.poll()

如果您需要跨平台支持,标准方法是使用 select.select() 来检查套接字是否被标记为有数据可供读取。如果是,但是recv()返回零字节,说明另一端挂断了。

我总能找到 Beej's Guide to Network Programming好(注意它是为 C 编写的,但通常适用于标准套接字操作),而 Socket Programming How-To有一个不错的 Python 概述。

编辑:下面是一个示例,说明如何编写一个简单的服务器来排队传入命令,但一旦发现远程端的连接已关闭,就立即退出处理。

import select
import socket
import time

# Create the server.
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.bind((socket.gethostname(), 7557))
serversocket.listen(1)

# Wait for an incoming connection.
clientsocket, address = serversocket.accept()
print 'Connection from', address[0]

# Control variables.
queue = []
cancelled = False

while True:
# If nothing queued, wait for incoming request.
if not queue:
queue.append(clientsocket.recv(1024))

# Receive data of length zero ==> connection closed.
if len(queue[0]) == 0:
break

# Get the next request and remove the trailing newline.
request = queue.pop(0)[:-1]
print 'Starting request', request

# Main processing loop.
for i in xrange(15):
# Do some of the processing.
time.sleep(1.0)

# See if the socket is marked as having data ready.
r, w, e = select.select((clientsocket,), (), (), 0)
if r:
data = clientsocket.recv(1024)

# Length of zero ==> connection closed.
if len(data) == 0:
cancelled = True
break

# Add this request to the queue.
queue.append(data)
print 'Queueing request', data[:-1]

# Request was cancelled.
if cancelled:
print 'Request cancelled.'
break

# Done with this request.
print 'Request finished.'

# If we got here, the connection was closed.
print 'Connection closed.'
serversocket.close()

要使用它,请运行脚本并在另一个终端 telnet 到 localhost,端口 7557。我执行的示例运行的输出,将三个请求排队,但在处理第三个请求期间关闭了连接:

Connection from 127.0.0.1
Starting request 1
Queueing request 2
Queueing request 3
Request finished.
Starting request 2
Request finished.
Starting request 3
Request cancelled.
Connection closed.

epoll 替代

另一个编辑:我已经设计了另一个例子,使用 select.epoll 来监控事件。我不认为它提供了比原始示例更多的功能,因为当远程端挂断时我看不到接收事件的方法。您仍然必须监视数据接收事件并检查零长度消息(再次,我很乐意在此声明中被证明是错误的)。

import select
import socket
import time

port = 7557

# Create the server.
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.bind((socket.gethostname(), port))
serversocket.listen(1)
serverfd = serversocket.fileno()
print "Listening on", socket.gethostname(), "port", port

# Make the socket non-blocking.
serversocket.setblocking(0)

# Initialise the list of clients.
clients = {}

# Create an epoll object and register our interest in read events on the server
# socket.
ep = select.epoll()
ep.register(serverfd, select.EPOLLIN)

while True:
# Check for events.
events = ep.poll(0)
for fd, event in events:
# New connection to server.
if fd == serverfd and event & select.EPOLLIN:
# Accept the connection.
connection, address = serversocket.accept()
connection.setblocking(0)

# We want input notifications.
ep.register(connection.fileno(), select.EPOLLIN)

# Store some information about this client.
clients[connection.fileno()] = {
'delay': 0.0,
'input': "",
'response': "",
'connection': connection,
'address': address,
}

# Done.
print "Accepted connection from", address

# A socket was closed on our end.
elif event & select.EPOLLHUP:
print "Closed connection to", clients[fd]['address']
ep.unregister(fd)
del clients[fd]

# Error on a connection.
elif event & select.EPOLLERR:
print "Error on connection to", clients[fd]['address']
ep.modify(fd, 0)
clients[fd]['connection'].shutdown(socket.SHUT_RDWR)

# Incoming data.
elif event & select.EPOLLIN:
print "Incoming data from", clients[fd]['address']
data = clients[fd]['connection'].recv(1024)

# Zero length = remote closure.
if not data:
print "Remote close on ", clients[fd]['address']
ep.modify(fd, 0)
clients[fd]['connection'].shutdown(socket.SHUT_RDWR)

# Store the input.
else:
print data
clients[fd]['input'] += data

# Run when the client is ready to accept some output. The processing
# loop registers for this event when the response is complete.
elif event & select.EPOLLOUT:
print "Sending output to", clients[fd]['address']

# Write as much as we can.
written = clients[fd]['connection'].send(clients[fd]['response'])

# Delete what we have already written from the complete response.
clients[fd]['response'] = clients[fd]['response'][written:]

# When all the the response is written, shut the connection.
if not clients[fd]['response']:
ep.modify(fd, 0)
clients[fd]['connection'].shutdown(socket.SHUT_RDWR)

# Processing loop.
for client in clients.keys():
clients[client]['delay'] += 0.1

# When the 'processing' has finished.
if clients[client]['delay'] >= 15.0:
# Reverse the input to form the response.
clients[client]['response'] = clients[client]['input'][::-1]

# Register for the ready-to-send event. The network loop uses this
# as the signal to send the response.
ep.modify(client, select.EPOLLOUT)

# Processing delay.
time.sleep(0.1)

注意:这只检测正确的关机。如果远程端只是停止监听而没有发送正确的消息,那么在您尝试写入并收到错误之前您不会知道。检查这一点留给读者作为练习。此外,您可能希望对整个循环执行一些错误检查,以便服务器本身在内部出现问题时正常关闭。

关于python - 检测套接字挂断而不发送或接收?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5686490/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com