I am writing a TCP server to listen for TCP packets containing status information from remote machines. The remote machines keep the TCP connection alive once established. Here are the salient parts of my code:
#!/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()
The machines can connect and send their data, and I can send acknowledgements back in the send_data
block.
So far, so good.
I cannot understand how to asynchronously send data to one of the devices from outside the Protocol code. Clearly, I need to somehow access an instance of the Factory class for the specific connection I wish to use but I cannot see how.
Keep safe and many thanks.
EDIT After @notorious.no provided a very helpful example, I changed my code to capture IP addresses and ports, also connection objects of connected devices:
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()
When I run this and then connect two test devices I get:
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>]
So now I have lists of connected device IPs and ports, and presumably I can use the connections objects to asynchronously send one a message when needed. Please could you advise how I can do this? Keep safe...
Not really sure what you mean by "devices from outside the Protocol code"
but I assume you mean accessing other clients that have connected to the same server (please comment if that's not the case). One thing you can do is have list of connected protocols in the factory object. The Factory.buildProtocol
(by default, unless you overload it) will set the factory
param in the 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()