0
votes

Recently I've taken my first stab at Twisted/Python building an app that echoes incoming UDP strings out the TCP port. I assumed this would be very simple, but I haven't been able to get it to work. The code below is the example TCP & UDP Server modified to run together. I'm just trying to pass some data between the two. Any help would be appreciated.

from twisted.internet.protocol import Protocol, Factory, DatagramProtocol
from twisted.internet import reactor

class TCPServer(Protocol):

    def dataReceived(self, data):
        self.transport.write(data)


class UDPServer(DatagramProtocol):

    def datagramReceived(self, datagram, address):
        #This is where I would like the TCPServer's dataReceived method run passing "datagram".  I've tried: 
        TCPServer.dataReceived(datagram)
        #But of course that is not the correct call because UDPServer doesn't recognize "dataReceived"


def main():
    f = Factory()
    f.protocol = TCPServer
    reactor.listenTCP(8000, f)
    reactor.listenUDP(8000, UDPServer())
    reactor.run()

if __name__ == '__main__':
    main()
1

1 Answers

2
votes

This is essentially the frequently asked How do I make input on one connection result in output on another?

The UDP<->TCP specifics in this question don't break the general answer given in the FAQ entry. Just notice that a DatagramProtocol is easier to work with than a Protocol because you already have the DatagramProtocol instance without having to get the cooperation of a factory as you do in the Protocol case.

Put another way:

from twisted.internet.protocol import Protocol, Factory, DatagramProtocol
from twisted.internet import reactor

class TCPServer(Protocol):
    def connectionMade(self):
        self.port = reactor.listenUDP(8000, UDPServer(self))

    def connectionLost(self, reason):
        self.port.stopListening()


class UDPServer(DatagramProtocol):
    def __init__(self, stream):
        self.stream = stream

    def datagramReceived(self, datagram, address):
        self.stream.transport.write(datagram)


def main():
    f = Factory()
    f.protocol = TCPServer
    reactor.listenTCP(8000, f)
    reactor.run()

if __name__ == '__main__':
    main()

Notice the essential change: UDPServer needs to call a method on an instance of TCPServer so it needs a reference to that instance. This is achieved by making the TCPServer instance pass itself to the UDPServer initializer and making the UDPServer initializer save that reference as an attribute of the UDPServer instance.