为什么我在这里收到 ConnectionResetError?
Why am I getting a ConnectionResetError here?
我是 Python 3 的新手,正在玩 asyncio。因此,我遇到了以下服务器端代码的奇怪行为:
import asyncio
@asyncio.coroutine
def handle_client(reader, writer):
print('Client connected.')
client_connected = True
while client_connected:
print('Waiting for client event.')
line = yield from reader.readline()
if line:
print('Got: {}'.format(line))
if line.decode() == 'echo\n':
print('Sending back echo.')
writer.write(line)
else:
print('Not sending back anything.')
else:
print('Client disconnected.')
client_connected = False
if __name__ == '__main__':
asyncio.async(asyncio.start_server(handle_client, 'localhost', 8888))
asyncio.get_event_loop().run_forever()
当我 运行 这个客户端代码时(编辑:客户端代码手动输入到 IPython 会话中,服务器肯定有时间在我关闭套接字之前写入)...
import socket
client = socket.create_connection(('localhost', 8888))
client.sendall('echo\n'.encode())
client.close()
...我从服务器收到错误回溯:
C:\Users\Gnar\Anaconda3\python.exe C:/Users/Gnar/Code/echo.py
Client connected.
Waiting for client event.
Got: b'echo\n'
Sending back echo.
Waiting for client event.
Task exception was never retrieved
future: <Task finished coro=<handle_client() done, defined at C:/Users/Gnar/Code/echo.py:4> exception=ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None)>
Traceback (most recent call last):
File "C:\Users\Gnar\Anaconda3\lib\asyncio\tasks.py", line 234, in _step
result = coro.throw(exc)
File "C:/Users/Gnar/Code/echo.py", line 10, in handle_client
line = yield from reader.readline()
File "C:\Users\Gnar\Anaconda3\lib\asyncio\streams.py", line 425, in readline
yield from self._wait_for_data('readline')
File "C:\Users\Gnar\Anaconda3\lib\asyncio\streams.py", line 393, in _wait_for_data
yield from self._waiter
File "C:\Users\Gnar\Anaconda3\lib\asyncio\futures.py", line 386, in __iter__
yield self # This tells Task to wait for completion.
File "C:\Users\Gnar\Anaconda3\lib\asyncio\tasks.py", line 287, in _wakeup
value = future.result()
File "C:\Users\Gnar\Anaconda3\lib\asyncio\futures.py", line 275, in result
raise self._exception
File "C:\Users\Gnar\Anaconda3\lib\asyncio\selector_events.py", line 662, in _read_ready
data = self._sock.recv(self.max_size)
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host
这个问题肯定与writer.write
有某种关系,因为当我调用以下客户端代码(这使得服务器跳过写入)时,没有错误:
import socket
client = socket.create_connection(('localhost', 8888))
client.sendall('foo\n'.encode())
client.close()
对应的服务器日志:
C:\Users\Gnar\Anaconda3\python.exe C:/Users/Gnar/Code/echo.py
Client connected.
Waiting for client event.
Got: b'foo\n'
Not sending back anything.
Waiting for client event.
Client disconnected.
我错过了什么?我是否错误地使用了 asyncio?
谢谢!
您遇到异常是因为您试图在服务器端将一些数据写回客户端,但客户端在发送 [=11= 后立即关闭 socket
],实际上并没有收到服务器的响应。如果套接字连接在线路上有未接收到的数据时关闭,您将在发送端收到错误,因此您知道远程端可能没有收到您上次发送的任何内容。
如果您在调用 socket.close()
之前在客户端添加对 socket.recv(1024)
的调用,那么问题就会消失,这样客户端在关闭套接字之前会等待服务器的响应。如果您只是想优雅地处理异常,即使客户端做了错误的事情,您也可以在服务器端的 write 调用周围使用 try
/except
。
我是 Python 3 的新手,正在玩 asyncio。因此,我遇到了以下服务器端代码的奇怪行为:
import asyncio
@asyncio.coroutine
def handle_client(reader, writer):
print('Client connected.')
client_connected = True
while client_connected:
print('Waiting for client event.')
line = yield from reader.readline()
if line:
print('Got: {}'.format(line))
if line.decode() == 'echo\n':
print('Sending back echo.')
writer.write(line)
else:
print('Not sending back anything.')
else:
print('Client disconnected.')
client_connected = False
if __name__ == '__main__':
asyncio.async(asyncio.start_server(handle_client, 'localhost', 8888))
asyncio.get_event_loop().run_forever()
当我 运行 这个客户端代码时(编辑:客户端代码手动输入到 IPython 会话中,服务器肯定有时间在我关闭套接字之前写入)...
import socket
client = socket.create_connection(('localhost', 8888))
client.sendall('echo\n'.encode())
client.close()
...我从服务器收到错误回溯:
C:\Users\Gnar\Anaconda3\python.exe C:/Users/Gnar/Code/echo.py
Client connected.
Waiting for client event.
Got: b'echo\n'
Sending back echo.
Waiting for client event.
Task exception was never retrieved
future: <Task finished coro=<handle_client() done, defined at C:/Users/Gnar/Code/echo.py:4> exception=ConnectionResetError(10054, 'An existing connection was forcibly closed by the remote host', None, 10054, None)>
Traceback (most recent call last):
File "C:\Users\Gnar\Anaconda3\lib\asyncio\tasks.py", line 234, in _step
result = coro.throw(exc)
File "C:/Users/Gnar/Code/echo.py", line 10, in handle_client
line = yield from reader.readline()
File "C:\Users\Gnar\Anaconda3\lib\asyncio\streams.py", line 425, in readline
yield from self._wait_for_data('readline')
File "C:\Users\Gnar\Anaconda3\lib\asyncio\streams.py", line 393, in _wait_for_data
yield from self._waiter
File "C:\Users\Gnar\Anaconda3\lib\asyncio\futures.py", line 386, in __iter__
yield self # This tells Task to wait for completion.
File "C:\Users\Gnar\Anaconda3\lib\asyncio\tasks.py", line 287, in _wakeup
value = future.result()
File "C:\Users\Gnar\Anaconda3\lib\asyncio\futures.py", line 275, in result
raise self._exception
File "C:\Users\Gnar\Anaconda3\lib\asyncio\selector_events.py", line 662, in _read_ready
data = self._sock.recv(self.max_size)
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host
这个问题肯定与writer.write
有某种关系,因为当我调用以下客户端代码(这使得服务器跳过写入)时,没有错误:
import socket
client = socket.create_connection(('localhost', 8888))
client.sendall('foo\n'.encode())
client.close()
对应的服务器日志:
C:\Users\Gnar\Anaconda3\python.exe C:/Users/Gnar/Code/echo.py
Client connected.
Waiting for client event.
Got: b'foo\n'
Not sending back anything.
Waiting for client event.
Client disconnected.
我错过了什么?我是否错误地使用了 asyncio?
谢谢!
您遇到异常是因为您试图在服务器端将一些数据写回客户端,但客户端在发送 [=11= 后立即关闭 socket
],实际上并没有收到服务器的响应。如果套接字连接在线路上有未接收到的数据时关闭,您将在发送端收到错误,因此您知道远程端可能没有收到您上次发送的任何内容。
如果您在调用 socket.close()
之前在客户端添加对 socket.recv(1024)
的调用,那么问题就会消失,这样客户端在关闭套接字之前会等待服务器的响应。如果您只是想优雅地处理异常,即使客户端做了错误的事情,您也可以在服务器端的 write 调用周围使用 try
/except
。