views:

190

answers:

2

I have the following code (almost an exact copy of the Chat server example listed here:

import twisted.scripts.twistd from twisted.protocols import basic from twisted.internet import protocol, reactor from twisted.application import service, internet

class MyChat(basic.LineReceiver):
    def connectionMade(self):
        print "Got new client!"
        self.factory.clients.append(self)

    def connectionLost(self, reason):
        print "Lost a client!"
        self.factory.clients.remove(self)

    def lineReceived(self, line):
        print "received", repr(line)
        for c in self.factory.clients:
            c.message(line)

    def message(self, message):
        self.transport.write(message + '\n')

factory = protocol.ServerFactory()
factory.protocol = MyChat
factory.clients = []

if __name__ == "__main__":
    print "Building reactor...."
    reactor.listenTCP(50000, factory)
    print "Running ractor...."
    reactor.run()
else:
    application = service.Application("chatserver")
    internet.TCPServer(50000, factory).setServiceParent(application)

The server runs without error, and if I connect to it via Telnet, I can send data and the server prints to the console and relays it to all clients (as is expected). However, if I connect to it via a different tool (a MUD client), it never gets the data.

I have ensured that the client is sending the data (Traced the packets with Wireshark, and they're going across the wire), but the server either never receives it, or is choosing to ignore it for some reason.

I have tried this with two MUD clients, gmud, and JMC. If it is important, I am running Windows 7 x64.

Does anyone have any idea why this could be happening?

Thanks,

Mike

EDIT:

Thanks to the hints provided by Maiku Mori, I tried adding another method that was specified in the Twisted API Docs, dataReceived. Once this was added, the MUD clients worked perfectly, but Telnet is now sending every character as it's own set of data, instead of waiting for the user to press Enter.

Here's a snipped of the new code:

    def dataReceived(self, data):
        print "Dreceived", repr(data)
        for c in self.factory.clients:
            c.message(data)

#    def lineReceived(self, line):
#        print "received", repr(line)
#        for c in self.factory.clients:
#            c.message(line)

Has anyone experiences this before, and if so, how do you get around it? Ideally, I would like Telnet and MUD clients to work with this application.

Thanks again.

+1  A: 

Are you sure that the MUD clients send line ending chars after each line? The lineReceived will only be called after line ending char has been sent.

EDIT:

Here I found API docs for LineReceiver. You could play around with dataReceived method to see if you are actually getting any kind of data. If I recall you can use it just like lineReceived.

Maiku Mori
I'm not sure if they are or aren't. Is there a way I can manually add that to the end of the next to test it?
Mike Trpcic
Well you can test it using Wireshark. There are also other Receiver base classes which you could use. It's been a while since I read Twisted API docs, but I think there's also another method which gets called as soon as data chunks come in live, you could use that to see if there's any data coming in at all. But it really has been almost a year since I read the docs, so I might be wrong.
Maiku Mori
Your reply helped, and I got the MUD clients to work, but the method I added broke Telnet support. I've outlined it in the edits to my initial post.
Mike Trpcic
+2  A: 

In case anyone stumbles across this question with similar problems, I'm leaving my findings as the accepted answer so that people don't have to hunt the way I did.

I fixed the issue by changing the delimiter value from in my Twisted protocol from "\r\n" (default), to just "\n" (which is what my MUD clients send. This means that in Telnet, when you enter the string:

Hello, World

Your application will receive it as:

Hello, World\r

You may need to do data sanitation on the server side to keep things in order. My final code was as follows:

import twisted.scripts.twistd from twisted.protocols import basic from twisted.internet import protocol, reactor from twisted.application import service, internet

class MyChat(basic.LineReceiver):
    def __init__(self):
        self.delimiter = "\n"

    def connectionMade(self):
        print "Got new client!"
        self.factory.clients.append(self)

    def connectionLost(self, reason):
        print "Lost a client!"
        self.factory.clients.remove(self)

    def lineReceived(self, line):
        print "received", repr(line)
        for c in self.factory.clients:
            c.message(line)

    def message(self, message):
        self.transport.write(message + '\n')

factory = protocol.ServerFactory()
factory.protocol = MyChat
factory.clients = []

if __name__ == "__main__":
    print "Building reactor...."
    reactor.listenTCP(50000, factory)
    print "Running ractor...."
    reactor.run()
else:
    application = service.Application("chatserver")
    internet.TCPServer(50000, factory).setServiceParent(application)

Thanks for all the help.

Mike Trpcic
Ah I thought you checked with wireshark and that MUD clients use the pause or something else as delimiter. Well this is a clean solution :D
Maiku Mori
Basically you can't make any assumptions about whether the client will send \n, \r, \n\r, or \r\n. Personally I wouldn't try bending LineReceiver to handle Telnet (and especially not MUD traffic) because it's not fundamentally a line-based protocol, especially when you consider IAC sequences, ANSI colour, etc.
Kylotan
What Twisted components would you suggest using then?
Mike Trpcic