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!
-
You can also resort to more advanced ones like
socketserver
or something else. They may be convenient, but also sometimes redundant. ↩︎