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.
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()