微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 asyncio 服务器发送多个文件

如何解决使用 asyncio 服务器发送多个文件

这里是 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))

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

解决方法

服务器使用 protocols - 它测量的规则描述了一侧必须发送的内容以及如何在另一种尺寸上读取它。

例如 HTTP 首先发送标题、下一个空行和下一个数据(正文)。标题之一包含正文有多长的信息。

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


同样的方式你可以创建自己的协议。

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

可以发送文件数量+\n,接下来使用for循环发送文件名+\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,item)
        name = item.decode()
        print('name:',name)

        # get size + '\n'
        start = end+1
        end = data.find(b'\n',item)
        size = int(item.decode())
        print('size:',size)
        
        # get data
        start = end+1
        end = start+size-1
        item = data[start:end]
        print('[DEBUG] start,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))

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。