我正在开发一个使用异步服务器和多个客户端的基本 Python 应用程序,但我在与第二个客户端通信时遇到一些问题。
client.py - 轮询目录中的新文件并将内容发送到服务器
# Currently just sends the contents of a known file to the server.
# The polling portion I plan on adding later
import asyncio
async def send_file(filename, server_host, server_port):
reader, writer = await asyncio.open_connection(server_host, server_port)
writer.write(filename.encode())
with open(filename, 'rb') as file:
while True:
data = file.read(1024)
if not data:
break
writer.write(data)
writer.close()
if __name__ == '__main__':
server_host = '127.0.0.1' # Change to the server's IP address or hostname.
server_port = 8888
filename = 'temp.txt' # Change to the file you want to send.
asyncio.run(send_file(filename, server_host, server_port))
server.py - 等待来自客户端 #1 的数据并将其写入文件。完成后提醒客户端#2
import asyncio
async def handle_client(reader, writer):
try:
data = await reader.read(1024)
if not data:
return
# Writes data from Client #1
filename = data.decode().strip()
print(f"Receiving file: {filename}")
with open(filename, 'wb') as file:
while True:
data = await reader.read(1024)
if not data:
break
file.write(data)
print(f"Received file: {filename}")
# Send message to the receiver that the server is done writing
response = "Server has written data from client.py"
writer.write(response.encode())
await writer.drain()
except asyncio.CancelledError:
pass
finally:
writer.close()
async def main():
server = await asyncio.start_server(
handle_client, '127.0.0.1', 8888)
addr = server.sockets[0].getsockname()
print(f'Serving on {addr}')
async with server:
await server.serve_forever()
if __name__ == '__main__':
asyncio.run(main())
receiver.py - 从服务器打印完成状态
import asyncio
async def receive_data():
reader, writer = await asyncio.open_connection('127.0.0.1', 8888)
response = await reader.read(100)
message = response.decode()
print(f"Received: {message}")
print("Closing the connection")
writer.close()
await writer.wait_closed()
async def main():
await receive_data()
if __name__ == '__main__':
server_host = '127.0.0.1' # Change to the server's IP address or hostname.
server_port = 8888
filename = 'temp.txt' # Change to the file you want to send.
asyncio.run(main())
我能够让 client.py 和 server.py 成功通信,从而将位于 client 目录中的 temp.txt 的内容基本上复制到 server.py 的目录中,但我的问题是接收者.py 从不打印来自服务器的完成消息。我认为这可能与我设置连接的方式有关,但我不确定,因为我对异步任务还很陌生。
非常感谢任何帮助,谢谢!
每个程序都将作为不同客户端连接到您的服务器程序 - 这意味着“响应”消息将写入“client.py”持有的连接中,并且根本不会在单独的连接中传输任何字节,由“receiver.py”持有。
Asyncio“做它的事情”,以便连接到服务器的每个客户端程序将对“handle_client”进行不同的调用,该调用将与任何其他调用同时运行。 因此,在您的代码中,第二个并发调用将在 server.py 中对
handle_client
进行,这可能会永远等待一些数据到达该连接,因为您的“receiver.py”不发送任何内容 - (或者只是完成,因为没有数据 - 不确定)。
如果您需要两个不同的程序与服务器进行通信,服务器将必须“知道”它们正在与其中的哪一个进行通信,并且需要某种内部机制来跨不同的“任务”传输其内部数据 - 每个任务处理一个连接。
您可以在不同的端口号中启动单独的服务器,以便具有不同角色的程序将每个服务器连接到适当的端口号,或者向您的临时协议添加一些带内信息,以便服务器知道识别“接收者”程序,而不是客户端。
因此,我更改了您的代码来做到这一点:如果发送的文件名是“接收者”,服务器会在另一个仅发送“已完成”消息的函数中单独处理它。我还包括一个最小的计数器机制,以便任意数量的“receiver.py”程序可以同时连接到服务器,并且当第一个“客户端”程序连接并发送文件时,所有接收者都会收到通知。
(client.py 有一个最小的修改,只需在文件名后发送一个换行符。“receiver.py”也仅更改为发送“receiver”作为文件名,因此服务器将相应地对待它)
服务器.py
import asyncio
from queue import Empty
receiver_count = 0
receiver_msg_queue = asyncio.Queue()
async def handle_client(reader, writer):
global receiver_count
try:
# read only one line of text as the file name (readline(), not "read(1024)"
data = await reader.readline()
if not data:
return
# Writes data from Client #1
filename = data.decode().strip()
# if the connection is from a receiver program, handle it in a different function
# altogether:
if filename == "receiver":
receiver_count += 1
print("receiver connected")
await handle_receiver(writer)
return
print(f"Receiving file: {filename}")
# I added a sufix to the file, so I can run things on the same directory:
with open(filename + ".copy", 'wb') as file:
while True:
data = await reader.read(1024)
if not data:
break
file.write(data)
print(f"Received file: {filename}")
# Send message to the receiver that the server is done writing
response = "Server has written data from client.py"
receiver_msg_queue.put_nowait((response, 0))
writer.write(response.encode())
await writer.drain()
except asyncio.CancelledError:
pass
finally:
writer.close()
async def handle_receiver(writer):
"""will be called once, concurrently, for each receiver program connected"""
try:
message, count = await receiver_msg_queue.get()
writer.write(message.encode())
if count < receiver_count - 1:
# replenish the queue so other connected receivers get the message
receiver_msg_queue.put_nowait((message, count + 1))
except asyncio.CancelledError:
writer.close()
async def main():
server = await asyncio.start_server(
handle_client, '127.0.0.1', 8888)
addr = server.sockets[0].getsockname()
print(f'Serving on {addr}')
async with server:
await server.serve_forever()
if __name__ == '__main__':
asyncio.run(main())
客户端.py
...
async def send_file(filename, server_host, server_port):
reader, writer = await asyncio.open_connection(server_host, server_port)
writer.write(filename.encode() + b"\r\n")
...
...
接收器.py
...
reader, writer = await asyncio.open_connection('127.0.0.1', 8888)
writer.write(b"receiver\r\n")
response = await reader.readline()
...