Как гарантировать передачу данных tcp с использованием python asyncio?

У меня есть клиент, который подключается к серверу и отправляет все сообщения из списка, и каждое отправленное сообщение удаляется из списка.

Но когда я заставляю сервер закрыться, клиент продолжает отправлять и удалять сообщения из списка. Я бы хотел, чтобы, если соединение не работает, клиент прекращал отправлять сообщения или не удалял из списка, если нет гарантии, что сервер получил сообщение.

Я проверяю, что, когда я отправил более четырех сообщений после разрыва соединения, отображается ошибка «socket.send () подняло исключение». Но я не знаю, как получить эту ошибку, я думаю, что это асинхронно. В любом случае, если я получаю эту ошибку, ошибка не возникает, если в списке меньше пяти сообщений для отправки после разрыва соединения.

Ps: Сервер я написал только для своих тестов, но доступа к серверу у меня не будет. Итак, клиент, который должен сделать все, чтобы гарантировать отправку данных.

Большое спасибо.

клиент.py

import asyncio

class Client(asyncio.Protocol):
    TIMEOUT = 1.0
    event_list = []
    for i in range(10):
        event_list.append('msg' + str(i))

    def __init__(self):
        self.client_tcp_timeout = None
        print(self.event_list)

    def connection_made(self, transport):
        print('Connected to Server.')
        self.transport = transport
        self.client_tcp_timeout = loop.call_later(self.TIMEOUT, self.send_from_call_later)

    def data_received(self, data):
        self.data = format(data.decode())
        print('data received: {}'.format(data.decode()))

    def send_from_call_later(self):
        self.msg = self.event_list[0].encode()
        self.transport.write(self.msg)
        print('data sent: {}'.format(self.msg))
        print('Removing data: {}'.format(self.event_list[0]))
        del self.event_list[0]
        print(self.event_list)
        print('-----------------------------------------')
        if len(self.event_list) > 0:
            self.client_tcp_timeout = loop.call_later(self.TIMEOUT, self.send_from_call_later)
        else:
            print('All list was sent to the server.')

    def connection_lost(self, exc):
        print('Connection lost!!!!!!.')

loop = asyncio.get_event_loop()

coro = loop.create_connection(Client, 'localhost', 8000)
client = loop.run_until_complete(coro)

loop.run_forever()

сервер.py

import asyncio

class Server(asyncio.Protocol):
    def connection_made(self, transport):
        peername = transport.get_extra_info('peername')
        print('connection from {}'.format(peername))
        self.transport = transport

    def data_received(self, data):
        print('data received: {}'.format(data.decode()))
        #self.transport.write(data)

loop = asyncio.get_event_loop()
coro = loop.create_server(Server, 'localhost', 8000)
server = loop.run_until_complete(coro)

print('serving on {}'.format(server.sockets[0].getsockname()))

try:
    loop.run_forever()
except KeyboardInterrupt:
    print("exit")
finally:
    server.close()
    loop.close()

вывод сервера (принудительно (CTRL+C) закрыть сервер после получения msg4):

$ python3 server.py
serving on ('127.0.0.1', 8000)
connection from ('127.0.0.1', 56119)
data received: msg0
data received: msg1
data received: msg2
data received: msg3
data received: msg4
^Cexit

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

$ python3 client.py 
['msg0', 'msg1', 'msg2', 'msg3', 'msg4', 'msg5', 'msg6', 'msg7', 'msg8', 'msg9']
Connected to Server.
data sent: b'msg0'
Removing data: msg0
['msg1', 'msg2', 'msg3', 'msg4', 'msg5', 'msg6', 'msg7', 'msg8', 'msg9']
-----------------------------------------
data sent: b'msg1'
Removing data: msg1
['msg2', 'msg3', 'msg4', 'msg5', 'msg6', 'msg7', 'msg8', 'msg9']
-----------------------------------------
data sent: b'msg2'
Removing data: msg2
['msg3', 'msg4', 'msg5', 'msg6', 'msg7', 'msg8', 'msg9']
-----------------------------------------
data sent: b'msg3'
Removing data: msg3
['msg4', 'msg5', 'msg6', 'msg7', 'msg8', 'msg9']
-----------------------------------------
data sent: b'msg4'
Removing data: msg4
['msg5', 'msg6', 'msg7', 'msg8', 'msg9']
-----------------------------------------
Connection lost!!!!!!.
data sent: b'msg5'
Removing data: msg5
['msg6', 'msg7', 'msg8', 'msg9']
-----------------------------------------
data sent: b'msg6'
Removing data: msg6
['msg7', 'msg8', 'msg9']
-----------------------------------------
data sent: b'msg7'
Removing data: msg7
['msg8', 'msg9']
-----------------------------------------
data sent: b'msg8'
Removing data: msg8
['msg9']
-----------------------------------------
socket.send() raised exception.
data sent: b'msg9'
Removing data: msg9
[]
-----------------------------------------
All list was sent to the server.

person Beyonlo Sam    schedule 01.10.2014    source источник


Ответы (2)


Запись в сокет TCP не гарантирует получение данных. Он отправляет данные только ядру ОС, которое затем будет изо всех сил пытаться отправить данные другой стороне. Но вызов записи уже вернет успех, как только данные будут отправлены в ядро ​​​​ОС. Если затем данные будут получены ядром ОС однорангового узла, оно подтвердит их на уровне TCP. Но это означает только то, что данные принимаются ядром, а не обрабатываются приложением.

Если вы хотите иметь гарантированную доставку сообщений и иметь дело с возможным закрытием однорангового узла, вы должны реализовать какие-то подтверждения внутри своего протокола и удалять данные только после того, как вы получите явное подтверждение от своего однорангового приложения после того, как оно успешно обработало данные.

person Steffen Ullrich    schedule 02.10.2014

Я бы хотел, чтобы, если соединение не работает, клиент прекращал отправлять сообщения или не удалял из списка, если нет гарантии, что сервер получил сообщение.

Так что напишите код, который это делает. Не удаляйте сообщение, пока сервер не укажет, что он получил сообщение. Вы можете реализовать это любым способом, который вам нравится, но если это то поведение, которое вам нужно, вы должны его закодировать.

person David Schwartz    schedule 02.10.2014