Question

I have this code from here:

#!/usr/bin/python

from asyncore import dispatcher
from asynchat import async_chat
import socket, asyncore

PORT = 55555
NAME = 'ChatLine'

class ChatSession(async_chat):
    def __init__(self,server,sock):
        async_chat.__init__(self, sock)
        self.server = server
        self.set_terminator("\r\n")
        self.data = []

    def collect_incoming_data(self, data):
        self.data.append(data)

    def found_terminator(self):
        line = "".join(self.data)
        self.data = []
        self.server.broadcast(line)

    def handle_close(self):
        async_chat.handle_close(self)
        self.server.disconnect(self)

class ChatServer(dispatcher):
    def __init__(self, port, name):
        dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind(("",port))
        self.listen(5)
        self.name = name
        self.sessions = []

    def disconnect(self, sessions):
        self.sessions.remove(session)

    def broadcast(self, line):
        for session in self.sessions:
            session.push('>>' + line + '\r\n')

    def handle_accept(self):
        conn, addr = self.accept()
        print "Connected from " + str(addr)
        self.sessions.append(ChatSession(self, conn))

if __name__ == '__main__':
    s = ChatServer(PORT, NAME)
    try: asyncore.loop()
    except KeyboardInterrupt: print

If I connect it using putty/telnet or any other software I'm getting this output:

test
>>test

First test is what I'm sending and second >>>test is what server broadcasts back.

How can I remove the broadcast of the same message that I send, so I won't get second >>>test? Maybe I can register the session ID or any other data and when server broadcast the message I can send data to all sessions except the session that was recorded previously?

Or maybe I can add usernames and use that to remove the repeated message?

I didn't find any tutorial or documentation about asyncore or asychat, so any help will be appreciated.

Était-ce utile?

La solution

Your sessions list on ChatServer contains all of the ChatSession instances representing connections to your server.

This includes the ChatSession that sent a particular message to the server. If you want to send that message out to all of the other connections, then you just need to skip one of the ChatSession instances in the loop inside broadcast. To know which one to skip, you might add an argument to broadcast:

def broadcast(self, line, skip):

And then use it to not call push one time through the loop:

    for session in self.sessions:
        if session is skip:
            session.push('Alternate pedagogical output\r\n')
        else:
            session.push('>>' + line + '\r\n')

Here I haven't actually skipped the session, just gave it different treatment so you can easily tell that something different is happening to it (instead of seeing no output, which could also be caused by some other bug...)

Now you just need to change the caller to pass this argument. Fortunately, the caller is a method of ChatSession where the correct ChatSession to skip is easily available - as self!

So just change the found_terminate method of ChatSession to look like...

def found_terminator(self):
    line = "".join(self.data)
    self.data = []
    self.server.broadcast(line, skip=self)

As you can see, this isn't really taking advantage of any asyncore or asynchat features. It's just some more logic in your application code.

Incidentally, Twisted is a lot more featureful than asynchat - so I recommend you spend your time learning that, instead. For a taste, here's your chat server written with Twisted:

from twisted.internet import reactor
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineOnlyReceiver

PORT = 55555

class ChatSession(LineOnlyReceiver):
    def connectionMade(self):
        print "Connection from", self.transport.getPeer()
        self.factory.sessions.append(self)

    def connectionLost(self, reason):
        self.factory.sessions.remove(self)

    def lineReceived(self, line):
        self.factory.broadcast(line, skip=self)

    def send(self, line):
        self.sendLine('>>' + line)

class ChatServer(Factory):
    protocol = ChatSession

    def __init__(self):
        self.sessions = []

    def broadcast(self, line, skip):
        for session in self.sessions:
            if session is not skip:
                session.send(line)

reactor.listenTCP(PORT, ChatServer())
reactor.run()
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top