使用异步IO服务器发送多个文件

2024-05-16 05:47:10 发布

您现在位置:Python中文网/ 问答频道 /正文

这是server.py

import asyncio
import aiofile

async def handler(reader, writer):
    data = await reader.read(n=-1)
    addr = writer.get_extra_info('peername')
    print('Addr', addr)
    # photo_120721_215652.jpg
    name = data[:23].decode()
    async with aiofile.async_open(name, 'wb') as afp:
        await afp.write(data[23:])
    print("handler end")


async def main():
    server = await asyncio.start_server(handler, '0.0.0.0', 8888)
    addr = server.sockets[0].getsockname()
    print(f'Serving on {addr}')
    async with server:
        await server.serve_forever()

asyncio.run(main())

这是client.py

async def send_photos_to_server(filelist):
    retry = 0
    for file in filelist:
        while True:
            try:
                reader, writer = await asyncio.open_connection('192.168.1.100', 8888)
            except OSError:
                while retry != 5:
                    await asyncio.sleep(3)
                    retry += 1
            else:
                break # exit the loop 
        if retry == 5:
            print("No connection after {} retries")
            break

        name = file.split('/')[-1].encode()
        writer.write(name)
        await writer.drain()

        async with aiofile.async_open(file, 'rb') as afp:
            afp_reader = await afp.read(length=-1)
        writer.write(afp_reader)
        await writer.drain()
        writer.close()
        await writer.wait_closed()

filelist = ['./photos/photo_120721_215652.jpg',
            './photos/photo_120721_215654.jpg',
            './photos/photo_120721_215656.jpg']

asyncio.run(send_photos_to_server(filelist))

所以这个代码是有效的。但我不确定这样做是否合适。每次发送新文件时,我们都会创建一个新连接。当然,我可以把这三个文件都打包成一个档案,但我不确定它是否方便。另外,在发送消息时使用文件名也很不方便,因为在服务器端,我必须以某种方式找到它。那么,有人能解释一下如何修改此代码以获得更好的实践吗?我刚刚开始学习异步io,但并不擅长


Tags: nameasyncioasyncserverawaitreaderwriterjpg
1条回答
网友
1楼 · 发布于 2024-05-16 05:47:10

服务器使用protocols——这是一种规则,描述了一方必须发送什么以及如何在另一方上读取

例如HTTP首先发送头、下一个空行和下一个数据(body)。其中一个标题包含正文的长度信息

另一方首先必须读取数据byte after byte,直到它找到空行('n\n'),然后它有头,它可以找到正文的长度,并读取正确的字节数


与创建自己的协议的方法相同

我假设您希望在一个连接中发送所有文件

您可以发送文件数+\n,然后使用for-loop发送文件名+\n,文件大小+\n,文件数据

另一方也应该做类似的事情——首先读取所有到\n的数据以获得文件数,然后使用for-loop再次读取所有到\n的数据以获得文件名,读取所有到\n的数据以获得大小,使用size读取文件数据


它表明,使用zip压缩数据并发送数据可能更简单,而另一方将在这个zip文件中包含所有信息——文件名、大小、数据

当然,您可以减少它,并且仍然以单独的连接发送每个文件


示例代码

server.py

import asyncio
import aiofile

async def handler(reader, writer):

    #  - before for-loop  -
    
    data = await reader.read(n=-1)
    addr = writer.get_extra_info('peername')
    print('Addr', addr)

    # get number of images + `\n`
    start = 0
    end = data.find(b'\n', start)
    item = data[start:end]
    print('[DEBUG] start,end:', start, end, item)
    number = int(item.decode())
    print('number:', number)
    
    print('    ')

    #  - for-loop  -

    for _ in range(number):
        # get filename + '\n' 
        start = end+1
        end = data.find(b'\n', start)
        item = data[start:end]
        print('[DEBUG] start,end:', start, end, item)
        name = item.decode()
        print('name:', name)

        # get size + '\n'
        start = end+1
        end = data.find(b'\n', start)
        item = data[start:end]
        print('[DEBUG] start,end:', start, end, item)
        size = int(item.decode())
        print('size:', size)
        
        # get data
        start = end+1
        end = start+size-1
        item = data[start:end]
        print('[DEBUG] start,end:', start, end, item[:10])
        async with aiofile.async_open(name, 'wb') as afp:
            await afp.write(item)
            
        print('    ')

    #  - after for-loop  -
        
    print("handler end")


async def main():
    server = await asyncio.start_server(handler, '0.0.0.0', 8888)
    addr = server.sockets[0].getsockname()
    print(f'Serving on {addr}')
    async with server:
        await server.serve_forever()

asyncio.run(main())

client.py

import asyncio
import aiofile

async def send_photos_to_server(filelist):

    #  - before for-loop  -
    
    retry = 0

    while True:
        try:
            reader, writer = await asyncio.open_connection('0.0.0.0', 8888)
        except OSError:
            while retry != 5:
                await asyncio.sleep(3)
                retry += 1
        else:
            break # exit the loop 
    if retry == 5:
        print("No connection after {} retries")
        return
    
    # send number of images + '\n'
    text = str(len(filelist)) + '\n'
    writer.write(text.encode())
    await writer.drain()
    
    #  - for-loop  -
    
    for filename in filelist:

        # send filename + '\n'
        name = filename.split('/')[-1]
        text = name + '\n'
        writer.write(text.encode())
        await writer.drain()

        async with aiofile.async_open(filename, 'rb') as afp:
            afp_reader = await afp.read(length=-1)

        # send size + '\n'
        size = str(len(afp_reader)) 
        text = size + '\n'
        writer.write(text.encode())
        await writer.drain()

        # send data
        writer.write(afp_reader)
        await writer.drain()
        
        
    #  - after for-loop  -
    
    writer.close()
    await writer.wait_closed()

filelist = [
    '/home/furas/test/image1.png',
    '/home/furas/test/image2.png',
    '/home/furas/test/image3.png',
]

asyncio.run(send_photos_to_server(filelist))

相关问题 更多 >