Python console chat with socket

Question:

I’m writing a simple console chat with server and client. When receiving a message from the first client server should send it to the second client and vice versa. But when first client sends a message to the server it returns back and doesn’t reach the second client. Maybe there is a problem in receiving() function.

Here is my client.py:

import socket
from _thread import *


def recieving(clientSocket):
    while True:
        encodedMsg = clientSocket.recv(1024)
        decodedMsg = encodedMsg.decode('utf-8')

        print(decodedMsg)


def chat(clientSocket, name):
    msg = input()
    encoded_msg = f'[{name}] {msg}'.encode('utf-8')

    clientSocket.send(encoded_msg)


def main():
    serverAddress = (socket.gethostname(), 4444)

    clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    clientSocket.connect(serverAddress)

    name = input('Enter your name: ')
    start_new_thread(recieving, (clientSocket,))
    while True:
        chat(clientSocket, name)
    

if __name__ == "__main__":
    main()

And server.py:

import time
import socket
from _thread import *


def listen(clientSocket, addr):
    while True:
        encodedMsg = clientSocket.recv(1024)
        decodedMsg = encodedMsg.decode('utf-8')
        currTime = time.strftime("%Y-%m-%d-%H.%M.%S", time.localtime())
    
        for client in clients:
            if addr != client:
                clientSocket.sendto(encodedMsg, client)

    print(f'[{currTime}] {decodedMsg}')


def main():
    serverAddress = (socket.gethostname(), 4444)
    global clients
    clients = []

    serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serverSocket.bind(serverAddress)
    serverSocket.listen(2)

    while True:
        clientSocket, addr = serverSocket.accept()

        if addr not in clients:
            clients.append(addr)
            print(f'{addr} joined chat')

        start_new_thread(listen, (clientSocket, addr))


if __name__ == '__main__':
    main()
Asked By: lian

||

Answers:

sendto doesn’t work as expected if its socket is connected. It just sends to the connected socket, not the specified address.

Therefore, listen needs to be able to access the open socket of each client in order to write to it.

Currently clients is a list of addresses, but you could change it to a dict of address to socket mappings:

def main():
    global clients
    clients = {}

Then when you get a new client connection, save address and socket:

        clientSocket, addr = serverSocket.accept()

        if addr not in clients:
            clients[addr] = clientSocket
            print(f'{addr} joined chat')

        start_new_thread(listen, (clientSocket, addr))

Finally, in listen, write to each other client’s socket, not the connected clientSocket for that listen thread:

        for client in clients:
            if addr != client:
                print(f"sending message from {addr} to {client}")
                clients[client].send(encodedMsg)

There’s a number of other problems with your code.

Sockets are not thread safe. So there is a race condition if 2 clients happen to write the same thing at the same time; the writes could be interpolated and the messages munged up.

If a client disconnects, the server doesn’t handle the disconnection well. If the server disconnects, the clients go into an infinite loop as well.

Answered By: Daniel Farrell
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.