debug
debug

Reputation: 1079

How to chat with multiple clients from a chat server

The issues:


Chat with the server

Start a chat server, and make chat connections from clients.

$ nc -v 127.0.0.1 4444 

Double clients as follow:

 ====================     |---->  [client1: ('127.0.0.1', 37748)]
 | Server (Console) |<----|
 ====================     |---->  [client2: ('127.0.0.1', 37750)]


root@lab:/tmp# python ChatManager.py
start a chat server: ('127.0.0.1', 4444)
[+] client: ('127.0.0.1', 37748)
[+] client: ('127.0.0.1', 37750)
Hello World

When multiple clients are available, the console of chat server sends "Hello World", and it shows

error: uncaptured python exception, closing channel <__main__.ConsoleHandler connected at 0xb73e96ec> 
(<type 'exceptions.OSError'>:  
[Errno 11] Resource temporarily unavailable  
[/usr/lib/python2.7/asyncore.py|read|83]  
[/usr/lib/python2.7/asyncore.py|handle_read_event|449]  
[ChatManager.py|handle_read|17] [/usr/lib/python2.7/asyncore.py|recv|387]  
[/usr/lib/python2.7/asyncore.py|recv|619])

ChatServer with multi clients


Chat Server Code

#!/usr/bin/env python
# -*- coding: utf8 -*-

import asyncore
import sys


class ConsoleHandler(asyncore.file_dispatcher):
    """Enable console interactive for socket read/write.
    """
    def __init__(self, sender, file):
        asyncore.file_dispatcher.__init__(self, file)
        self.current_chat = sender
        self.BUFSIZE = 1024

    def handle_read(self):
        self.current_chat.out_buffer += self.recv(self.BUFSIZE)


class ChatManager(asyncore.dispatcher):
    """Handle tcp in-connections, ex: send commands to targets.
    """
    def __init__(self, _sock=None, _map=None):
        asyncore.dispatcher.__init__(self, _sock, _map)
        self.out_buffer = ''
        self.BUFSIZE = 1024

    def handle_read(self):
        data = self.recv(self.BUFSIZE)
        print(data.strip())
        # self.send(data)

    def handle_write(self):
        if self.out_buffer != "":
            sent = self.send(self.out_buffer)
            self.out_buffer = self.out_buffer[sent:]

    def handle_error(self):
        pass

    def handle_close(self):
        """Called when the socket is closed.
        """
        self.close()


class Listener(asyncore.dispatcher):
    """Start a tcp listener (default: 127.0.0.1:4444), and wait for connections.
       If a new connection, `ChatManager' will try to handle it.
    """
    def __init__(self, addr=('127.0.0.1', 4444), max_connections=4):
        asyncore.dispatcher.__init__(self)
        self.connections = []
        self.create_socket(asyncore.socket.AF_INET, asyncore.socket.SOCK_STREAM)

        self.set_reuse_addr()
        self.bind(addr)
        self.listen(max_connections)
        print('start a chat server: {}'.format(addr))

    def handle_accept(self):
        client, caddr = self.accept()
        print('[+] client: {}'.format(caddr))
        ConsoleHandler(ChatManager(client), sys.stdin)


if __name__ == "__main__":
    Listener()
    asyncore.loop()

Upvotes: 0

Views: 665

Answers (1)

thomas
thomas

Reputation: 327

This self.set_reuse_addr() is generally a bad idea, but fine for development. Try to properly close your connections instead.

I think the problem is you end up calling handle_read() from ConsoleHandler twice (perhaps put a print("CH read")in there to check the logic of your code). To send data, add a call to handle_write.

 def handle_read(self):
    self.current_chat.out_buffer += self.recv(self.BUFSIZE)
    self.current_chat.handle_write()

I changed sent = self.send(self.out_buffer) to sent = self.send(bytes(self.out_buffer, encoding="utf-8"))

Upvotes: 0

Related Questions