Asyncio не отправляет все данные изображения через TCP - PullRequest
0 голосов
/ 11 июня 2018

Я пытаюсь отправить изображение с локального компьютера на компьютер в облаке, используя asyncio с протоколом TCP.Иногда я получаю отправляемое изображение целиком, а иногда отправляется только часть изображения.

код клиента

import os
os.environ['PYTHONASYNCIODEBUG'] = '1'
import asyncio
import logging

logging.basicConfig(level=logging.ERROR)
async def tcp_echo_client(data, loop):
    reader, writer = await asyncio.open_connection(<ip_addr>, <port>,
                                                   loop=loop)
    print('Sending data of size: %r' % str(len(data)))
    writer.write(data)
    await writer.drain()
    #print("Message: %r" %(data))
    print(type(data))
    print('Close the socket')
    writer.write_eof()
    writer.close()

with open('sendpic0.jpg','rb') as f:
    data=f.read()
loop = asyncio.get_event_loop()
loop.run_until_complete(tcp_echo_client(data, loop))
loop.close()

код сервера:

import os
os.environ['PYTHONASYNCIODEBUG'] = '1'
import asyncio
import logging

logging.basicConfig(level=logging.ERROR)

async def handle_echo(reader, writer):

    data = await reader.read()
    addr = writer.get_extra_info('peername')
    #print("Received %r from %r" % (message, addr))
    print("Length of data recieved: %r" % (str(len(data))))
    #with open('recvpic0.jpg','wb') as f:
    #    f.write(data)
    print("Close the client socket")
    writer.close()
    #print("Message: %r" %(data))
    print("Received data of length: %r" %(str(len(data))))


loop = asyncio.get_event_loop()
data=b''
coro = asyncio.start_server(handle_echo, '', <port_number>, loop=loop)
server = loop.run_until_complete(coro)
print("Received data of length: %r" %(str(len(data))))
# Serve requests until Ctrl+C is pressed
print('Serving on {}'.format(server.sockets[0].getsockname()))
try:
    loop.run_forever()
except KeyboardInterrupt:
    pass

# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()

Я не сделалt дать IP-адрес и номер порта специально, но это не должно иметь значения.

Вот вывод:

вывод сервера

Received data of length: '0'
Serving on ('0.0.0.0', 50001)

Length of data recieved: '249216'
Close the client socket
Received data of length: '249216'                                                                              

Length of data recieved: '250624'       
Close the client socket                                                                          
Received data of length: '250624'

Length of data recieved: '256403'                                                                              
Close the client socket                                                  
Received data of length: '256403'                                                                                              

вывод клиента

$ python client.py       
Sending data of size: '256403' 
Close the socket
$ python client.py
<class 'bytes'>                                                                               
Close the socket                                                                              
$ python client.py       
Sending data of size: '256403'                                                                
<class 'bytes'>                                                                               
Close the socket                                                                              

Я использую Python 3.6.

Я не знаю, должен ли я иметь механизм проверки или отправлять данные порциями?Я бы предположил, что все это будет происходить автоматически с помощью функции чтения.

Я изменил код с этого сайта: http://asyncio.readthedocs.io/en/latest/tcp_echo.html

1 Ответ

0 голосов
/ 22 июня 2018

Это похоже на ошибку закрытия записи, подробно описанную в этой статье .

Короче говоря, writer.close не является сопрограммой, поэтому вы не можете ждать закрытия, чтобы фактически сброситьданные из буфера asyncio в ОС.Ожидание writer.drain() до close() не помогает, поскольку оно делает паузу только до тех пор, пока фоновые записи не уменьшат размер буфера до «низкого водяного знака», а не - как можно было ожидать - до тех пор, пока буфер не будет очищен.

ОБНОВЛЕНИЕ : Начиная с Python 3.7, выпущенного в июне 2018 года, простое исправление должно ждать writer.wait_closed() в конце tcp_echo_writer.


В то время, когда ответ был изначально написан, единственное доступное исправление состояло в том, чтобы скопировать реализацию asyncio.open_connection (не так плохо, как кажется, поскольку это короткая удобная функция) и добавить вызов к transport.set_write_buffer_limits(0).Это заставит await writer.drain() фактически ждать, пока все данные будут записаны в ОС (что, по мнению упомянутой статьи, в любом случае будет правильным решением):

@asyncio.coroutine
def fixed_open_connection(host=None, port=None, *,
                          loop=None, limit=65536, **kwds):
    if loop is None:
        loop = asyncio.get_event_loop()
    reader = asyncio.StreamReader(limit=limit, loop=loop)
    protocol = asyncio.StreamReaderProtocol(reader, loop=loop)
    transport, _ = yield from loop.create_connection(
        lambda: protocol, host, port, **kwds)
    ###### Following line added to fix buffering issues:
    transport.set_write_buffer_limits(0)
    ######
    writer = asyncio.StreamWriter(transport, protocol, reader, loop)
    return reader, writer

Странно, что такаяБаг нас скрывать в основной библиотеке asyncio.

Я подозреваю, что большинство людей не видят эту ошибку, потому что они поддерживают цикл обработки событий в течение более длительного времени, делая другие вещи, поэтому после writer.close()данные в конечном итоге записываются и сокет закрывается.

...