Как повторно подключить сокет на asyncio?

Я хотел бы создать два протокола (TcpClient и UdpServer) с asyncio on app.py где TcpClient будет иметь постоянное соединение с server.py и UdpServer, служащий сервером UDP:

что нужно:
a) что оба протокола для связи: вызов метода друг друга. Это работает только на первом соединении. Если TcpClient повторно подключиться, он не может отправить снова строку " отправить tcp."исходя из UdpServer. Я сверяюсь с print(self) и TcpClient создать новый экземпляр, а старый до сих пор существует, но без связи, но я не знаю как refactory, что. Я думаю, что я использую asyncio неправильно.
b) когда TcpClient отключается от server.py, подождите 5s и попробуйте снова подключиться и так далее. Я попытался к этому используя call_later() из asyncio, но я думаю, что есть родной способ сделать это, а не искусственность.
c) когда я начинаю app.py и если TcpClient не может подключиться, я хотел бы попытаться снова подключиться после 5s и так далее. Я не знаю, как это сделать.

вот мои примеры тестов app.py server.py - ... В server.py это просто для тестов-это будет другой язык.

просто сказать, что я пробовал:
1) Когда я начинаю app.py и server.py вниз, app.py не пытайтесь повторить.
2) Когда app.py подключены к server.py и сервер вниз и быстро вверх, TcpClient снова подключается, но я не могу больше подключать друг к другу методы на новом экземпляре и отправлять строку "отправить в tcp." к в server.py только старые, где нет больше связи.
3) Если я использую asyncio.async() вместо run_until_complete() Я не могу вызывать методы друг от друга протоколов.

Я положил app.py и server.py здесь, так что вы можете просто скопировать и запустить тесты.

Я использую ncat localhost 9000 -u -v для отправки строки " отправить в tcp.". Эта строка должна быть напечатана в классе UdpServer и передана методу send_data_to_tcp в классе TcpClient, и этот метод будет отправлен в строку server.py - ...

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

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

app.py:

import asyncio

#TCP client
class TcpClient(asyncio.Protocol):
    message = 'Testing'

    def connection_made(self, transport):
        self.transport = transport
        self.transport.write(self.message.encode())
        print('data sent: {}'.format(self.message))
        server_udp[1].tcp_client_connected()


    def data_received(self, data):
        self.data = format(data.decode())
        print('data received: {}'.format(data.decode()))
        if self.data == 'Testing':
            server_udp[1].send_data_to_udp(self.data)

    def send_data_to_tcp(self, data):
        self.transport.write(data.encode())

    def connection_lost(self, exc):
        msg = 'Connection lost with the server...'
        info = self.transport.get_extra_info('peername')
        server_udp[1].tcp_client_disconnected(msg, info)


#UDP Server
class UdpServer(asyncio.DatagramProtocol):

    CLIENT_TCP_TIMEOUT = 5.0

    def __init__(self):
        self.client_tcp_timeout = None

    def connection_made(self, transport):
        print('start', transport)
        self.transport = transport

    def datagram_received(self, data, addr):
        self.data = data.strip()
        self.data = self.data.decode()
        print('Data received:', self.data, addr)
        if self.data == 'send to tcp.':
            client_tcp[1].send_data_to_tcp(self.data)

    def connection_lost(self, exc):
        print('stop', exc)

    def send_data_to_udp(self, data):
        print('Receiving on UDPServer Class: ', (data))

    def connect_client_tcp(self):
        coro = loop.create_connection(TcpClient, 'localhost', 8000)
        #client_tcp = loop.run_until_complete(coro)
        client_tcp = asyncio.async(coro)

    def tcp_client_disconnected(self, data, info):
        print(data)
        self.client_tcp_info = info
        self.client_tcp_timeout = asyncio.get_event_loop().call_later(self.CLIENT_TCP_TIMEOUT, self.connect_client_tcp)

    def tcp_client_connected(self):
        if self.client_tcp_timeout:
            self.client_tcp_timeout.cancel()
            print('call_later cancel.')

loop = asyncio.get_event_loop()

#UDP Server
coro = loop.create_datagram_endpoint(UdpServer, local_addr=('localhost', 9000)) 
#server_udp = asyncio.Task(coro)
server_udp = loop.run_until_complete(coro)


#TCP client
coro = loop.create_connection(TcpClient, 'localhost', 8000)
#client_tcp = asyncio.async(coro)
client_tcp = loop.run_until_complete(coro)

loop.run_forever()

server.py:

import asyncio

class EchoServer(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)

        # close the socket
        #self.transport.close()

    #def connection_lost(self):
    #    print('server closed the connection')



loop = asyncio.get_event_loop()
coro = loop.create_server(EchoServer, 'localhost', 8000)
server = loop.run_until_complete(coro)
print(server)
print(dir(server))
print(dir(server.sockets))

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

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

1 ответов


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

@asyncio.coroutine
def do_connect():
    global tcp_server  # Make sure we use the global tcp_server
    while True:
        try:
            tcp_server = yield from loop.create_connection(TcpClient, 
                                                           'localhost', 8000)
        except OSError:
            print("Server not up retrying in 5 seconds...")
            yield from asyncio.sleep(5)
        else:
            break

затем мы используем это, чтобы запустить все:

loop = asyncio.get_event_loop()

#UDP Server
coro = loop.create_datagram_endpoint(UdpServer, local_addr=('localhost', 9000)) 
server_udp = loop.run_until_complete(coro)

#TCP client
loop.run_until_complete(do_connect())

loop.run_forever()

следующая часть обрабатывает сервер, идущий вниз / возвращающийся после app.py запускается. Мы должны исправить tcp_client_disconnected и connect_client_tcp чтобы справиться с этим должным образом:

def connect_client_tcp(self):
    global client_tcp
    task = asyncio.async(do_connect())
    def cb(result):
        client_tcp = result
    task.add_done_callback(cb)

def tcp_client_disconnected(self, data, info):
    print(data)
    self.client_tcp_info = info
    self.client_tcp_timeout = loop.call_later(self.CLIENT_TCP_TIMEOUT, self.connect_client_tcp)

интересная часть connect_client_tcp. У тебя было две проблемы с оригиналом. версия:

  1. вы назначали client_tcp непосредственно к результату asyncio.async(coro), что означает client_tcp была назначена asyncio.Task. Это было не то, чего ты хотела, ты хотела!--7--> присваивается результату завершенного asyncio.Task. Мы достигаем этого, используя task.add_done_callback присвоить client_tcp в результате Task после ее завершения.

  2. вы забыли global client_tcp в верхней части метода. Без этого вы просто создаете локальная переменная client_tcp, который был выброшен в конце connect_client_tcp.

как только эти проблемы будут исправлены, я смогу запустить app.py, start / stop serv.py всякий раз, когда я хочу, но всегда видеть все сообщения правильно доставлены из ncat to serv.py когда все три компонента работают вместе.

вот app.py, для легкого копирования/вставки:

import asyncio

#TCP client
class TcpClient(asyncio.Protocol):
    message = 'Testing'

    def connection_made(self, transport):
        self.transport = transport
        self.transport.write(self.message.encode())
        print('data sent: {}'.format(self.message))
        server_udp[1].tcp_client_connected()


    def data_received(self, data):
        self.data = format(data.decode())
        print('data received: {}'.format(data.decode()))
        if self.data == 'Testing':
            server_udp[1].send_data_to_udp(self.data)

    def send_data_to_tcp(self, data):
        self.transport.write(data.encode())

    def connection_lost(self, exc):
        msg = 'Connection lost with the server...'
        info = self.transport.get_extra_info('peername')
        server_udp[1].tcp_client_disconnected(msg, info)


#UDP Server
class UdpServer(asyncio.DatagramProtocol):

    CLIENT_TCP_TIMEOUT = 5.0

    def __init__(self):
        self.client_tcp_timeout = None

    def connection_made(self, transport):
        print('start', transport)
        self.transport = transport

    def datagram_received(self, data, addr):
        self.data = data.strip()
        self.data = self.data.decode()
        print('Data received:', self.data, addr)
        if self.data == 'send to tcp.':
            client_tcp[1].send_data_to_tcp(self.data)

    def connection_lost(self, exc):
        print('stop', exc)

    def send_data_to_udp(self, data):
        print('Receiving on UDPServer Class: ', (data))

    def connect_client_tcp(self):
        global client_tcp
        coro = loop.create_connection(TcpClient, 'localhost', 8000)
        task = asyncio.async(do_connect())
        def cb(result):
            client_tcp = result
        task.add_done_callback(cb)

    def tcp_client_disconnected(self, data, info):
        print(data)
        self.client_tcp_info = info
        self.client_tcp_timeout = loop.call_later(self.CLIENT_TCP_TIMEOUT, self.connect_client_tcp)

    def tcp_client_connected(self):
        if self.client_tcp_timeout:
            self.client_tcp_timeout.cancel()
            print('call_later cancel.')

@asyncio.coroutine
def do_connect():
    global client_tcp
    while True:
        try:
            client_tcp = yield from loop.create_connection(TcpClient, 'localhost', 8000)
        except OSError:
            print("Server not up retrying in 5 seconds...")
            yield from asyncio.sleep(1)
        else:
            break

loop = asyncio.get_event_loop()

#UDP Server
coro = loop.create_datagram_endpoint(UdpServer, local_addr=('localhost', 9000))
server_udp = loop.run_until_complete(coro)

#TCP client
loop.run_until_complete(do_connect())

loop.run_forever()