allenfrostline

How to Write a Chatroom on a Socket Server


2018-12-05

I’m trying to write a chatroom in this post, using the socket package1 in Python. The general structure of this problem can be devided into three parts. In the simplest case we have two clients, namely client0 and client1, and a server. Except for that the server provides the interface, everything else will remain the same among these three classes: they inherit from the class socket.socket and have two methods sending and recving. The two methods are built to loop infinitely just so that all requests are accepted unattended. In the meantime, in order to avoid interruption between these two functions, we have to run them simultaneously using the threading package. The two clients are reporting to different ports of the same host and the server listens to both, also in an infinite loop.

The code for server.py:

import socket
import threading


HOST, PORT = 'localhost', [9998, 9999]
names = ['a', 'b']
unsent_msg = {name: [] for name in names}


class Server(socket.socket):
    def __init__(self, family, type, id):
        super().__init__(family, type)
        self.bind((HOST, PORT[id]))
        self.listen(1)
        self.conn = None
        self.sender = names[id]
        self.receiver = names[1 - id]

    def recving(self):
        conn, addr = self.accept()
        self.conn = conn
        while True:
            msg = str(self.conn.recv(1024), 'utf-8')
            print('[{}]: (unsent) {}'.format(self.sender, msg))
            if msg == 'log-in succeeded':
                continue
            unsent_msg[self.sender].append(msg)

    def sending(self):
        while True:
            while unsent_msg[self.receiver]:
                msg = unsent_msg[self.receiver].pop()
                self.conn.sendall(bytes(msg, 'utf-8'))
                print('[{}]: ( sent ) {}'.format(self.receiver, msg))

    def start(self):
        threading.Thread(target=self.recving).start()
        threading.Thread(target=self.sending).start()


if __name__ == '__main__':
    sa = Server(socket.AF_INET, socket.SOCK_STREAM, 0)
    sb = Server(socket.AF_INET, socket.SOCK_STREAM, 1)
    sa.start()
    sb.start()

The code for client0.py:

import socket
import threading
from server import HOST, PORT


class Client(socket.socket):
    def __init__(self, id):
        super().__init__(socket.AF_INET, socket.SOCK_STREAM)
        self.connect((HOST, PORT[id]))
        self.sendall(bytes('log-in succeeded', 'utf-8'))

    def sending(self):
        while True:
            msg = input('[  me ]: ')
            self.sendall(bytes(msg, 'utf-8'))

    def recving(self):
        while True:
            msg = str(self.recv(1024), 'utf-8')
            print('\r[guest]: {}\n[  me ]: '.format(msg), end='')

    def start(self):
        threading.Thread(target=self.recving).start()
        threading.Thread(target=self.sending).start()


if __name__ == '__main__':
    c = Client(0)
    c.start()

The code for client1.py:

import socket
import threading
from server import HOST, PORT


class Client(socket.socket):
    def __init__(self, id):
        super().__init__(socket.AF_INET, socket.SOCK_STREAM)
        self.connect((HOST, PORT[id]))
        self.sendall(bytes('log-in succeeded', 'utf-8'))

    def sending(self):
        while True:
            msg = input('[  me ]: ')
            self.sendall(bytes(msg, 'utf-8'))

    def recving(self):
        while True:
            msg = str(self.recv(1024), 'utf-8')
            print('\r[guest]: {}\n[  me ]: '.format(msg), end='')

    def start(self):
        threading.Thread(target=self.recving).start()
        threading.Thread(target=self.sending).start()


if __name__ == '__main__':
    c = Client(1)
    c.start()

Start server.py first and then the two clients. The terminal screenshot is as below.

Again, this is just a very simple, toy-like chatroom and there’re a lot to be implemented if you want to, like quiting schemes, front-end delivery and broadcasting in multi-client cases. However, I’m sure taking this as the starting point won’t hurt. Enjoy coding!


  1. You can also resort to more advanced ones like socketserver or something else. They may be convenient, but also sometimes redundant. ↩︎