Отправить сообщение на существующее TCP-соединение с помощью Twisted

Я пишу TCP-сервер для прослушивания TCP-пакетов, содержащих информацию о состоянии с удаленных компьютеров. Удаленные машины поддерживают соединение TCP после его установления. Вот основные части моего кода:

#!/usr/bin/python
from twisted.internet import reactor, protocol
class FactoryProcess(protocol.Protocol):
    def dataReceived(self, data):
        # Process received data
    def send_data(self, message):
        # Reply to message etc
        self.transport.write(message)
factory = protocol.ServerFactory()
factory.protocol = FactoryProcess
reactor.listenTCP(8256,factory)
reactor.run()

Машины могут подключаться и отправлять свои данные, а я могу отправлять подтверждения обратно в блок send_data. Все идет нормально. Я не могу понять, как асинхронно отправлять данные на одно из устройств из-за пределов кода протокола. Очевидно, мне нужно каким-то образом получить доступ к экземпляру класса Factory для конкретного соединения, которое я хочу использовать, но я не понимаю, как это сделать. Берегите себя и большое спасибо.

РЕДАКТИРОВАТЬ После того, как @notorious.no предоставил очень полезный пример, я изменил свой код для захвата IP-адресов и портов, а также объектов подключения подключенных устройств:

from twisted.internet import endpoints, protocol, reactor

device_ips = []
device_ports = []
connections = []

class ChatProtocol(protocol.Protocol):
    def connectionMade(self):
        global device_ips, device_ports, connections
        # Append client
        self.factory.clientList.append(self)
        print('client connected. Connection Count = ' + str(len(self.factory.clientList)))
        connections.append(self)
        ip, port = self.transport.client
        device_ips.append(ip)
        device_ports.append(port)
        print('ips:' + str(device_ips) + ', ports:' + str(device_ports) + ', connections:' + str(connections))


    def connectionLost(self, _):
        # Remove client
        self.factory.clientList.remove(self)
        print('client lost. Connection Count = ' + str(len(self.factory.clientList)))

    def dataReceived(self, data):
        print('Data received:' + str(data))
        # Send message to all connected clients
        for client in self.factory.clientList:
            if client == self:
                continue

            client.transport.write(data)

class ChatFactory(protocol.Factory):
    protocol = ChatProtocol
    clientList = []

def main():
    epServer = endpoints.serverFromString(reactor, "tcp:8123")
    epServer.listen(ChatFactory())
    reactor.run()

main()

Когда я запускаю это, а затем подключаю два тестовых устройства, я получаю:

client connected. Connection Count = 1
ips:['redacted'], ports:[54182], connections:[<__main__.ChatProtocol instance at 0x7f5a835afcd0>]
client connected. Connection Count = 2
ips:['redacted', 'redacted'], ports:[54182, 57437], connections:[<__main__.ChatProtocol instance at 0x7f5a835afcd0>, <__main__.ChatProtocol instance at 0x7f5a835c2140>]

Итак, теперь у меня есть списки IP-адресов и портов подключенных устройств, и, предположительно, я могу использовать объекты соединений для асинхронной отправки сообщения при необходимости. Пожалуйста, не могли бы вы посоветовать, как мне это сделать? Берегите...


person Jonathan    schedule 18.05.2020    source источник


Ответы (1)


Не совсем уверен, что вы подразумеваете под "devices from outside the Protocol code", но я предполагаю, что вы имеете в виду доступ к другим клиентам, которые подключились к тому же серверу (пожалуйста, прокомментируйте, если это не так). Одна вещь, которую вы можете сделать, это иметь список подключенных протоколов в фабричном объекте. Factory.buildProtocol (по умолчанию, если вы не перегрузите его) установит параметр factory в файле protocol.

from twisted.internet import endpoints, protocol, reactor

class ChatProtocol(protocol.Protocol):
    def connectionMade(self):
        # Append client
        self.factory.clientList.append(self)
        print(len(self.factory.clientList))

    def connectionLost(self, _):
        # Remove client
        self.factory.clientList.remove(self)
        print(len(self.factory.clientList))

    def dataReceived(self, data):
        # Send message to all connected clients
        for client in self.factory.clientList:
            if client == self:
                continue

            client.transport.write(data)

class ChatFactory(protocol.Factory):
    protocol = ChatProtocol
    clientList = []

def main():
    epServer = endpoints.serverFromString(reactor, "tcp:8256:interface=0.0.0.0")
    epServer.listen(ChatFactory())
    reactor.run()

main()
person notorious.no    schedule 18.05.2020
comment
Большое спасибо, что нашли время, чтобы помочь. Извините, я не очень хорошо объяснил... Поэтому я мог бы создать clientList, когда машины подключаются, затем просмотреть его и найти нужного клиента для отправки моей команды (используя client.transport.write(data) линия). Как я смогу идентифицировать определенный элемент clientList из моего сохраненного IP-адреса однорангового узла? Берегите... - person Jonathan; 18.05.2020
comment
Я отредактировал свой вопрос, чтобы показать, как я реализовал ваш пример с несколькими корректировками, и еще один вопрос. Большое спасибо. - person Jonathan; 18.05.2020
comment
Вы можете использовать dict или карту для размещения соединений. Например, создайте clientMap = {}, затем в событии connectionMade добавьте соединение на карту self.factory.clientMap[self.transport.client] = self. Вы можете отправлять данные конкретному клиенту, например factory.clientMap[(ip, port)].transport.write(data). - person notorious.no; 19.05.2020
comment
Это именно то, что мне было нужно. Большое большое спасибо, берегите себя. - person Jonathan; 19.05.2020
comment
@ Джонатан, я тоже застрял в этом месте, не могли бы вы поделиться, как вы решили эту проблему с помощью приведенного выше примера кода, заранее спасибо. - person mubasher chaudhary; 27.01.2021
comment
@mubasherchaudhary Этот код заставил меня понять, что каждое соединение является самостоятельным экземпляром. Таким образом, не существует такой концепции наличия одного большого цикла, который по очереди подключается ко всем устройствам... Я создал многопроцессорную очередь, которую использую для хранения сообщений, которые необходимо отправить на определенное устройство. Все экземпляры просматривают эту очередь, и если для них предназначен самый верхний элемент (идентифицируемый по IP-адресу в начале сообщения), они отправляют его и удаляют элемент из очереди. Выглядит неудобно, но работает. Надеюсь, это поможет, берегите себя. - person Jonathan; 28.01.2021