pythonsocketsnetwork-programming

Two threads using same socket object - Problem?


I found this example of a simple Python chat implementation, in order to understand how sockets work:

#server    
import socket, threading                                                #Libraries import
        
    host = '127.0.0.1'                                                      #LocalHost
    port = 7976                                                             #Choosing unreserved port
        
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)              #socket initialization
    server.bind((host, port))                                               #binding host and port to socket
    server.listen()
        
    clients = []
    nicknames = []
        
    def broadcast(message):                                                 #broadcast function declaration
        for client in clients:
            client.send(message)
        
    def handle(client):                                         
        while True:
            try:                                                            #recieving valid messages from client
                message = client.recv(1024)
                broadcast(message)
            except:                                                         #removing clients
                index = clients.index(client)
                clients.remove(client)
                client.close()
                nickname = nicknames[index]
                broadcast('{} left!'.format(nickname).encode('ascii'))
                nicknames.remove(nickname)
                break
        
    def receive():                                                          #accepting multiple clients
        while True:
            client, address = server.accept()
            print("Connected with {}".format(str(address)))       
            client.send('NICKNAME'.encode('ascii'))
            nickname = client.recv(1024).decode('ascii')
            nicknames.append(nickname)
            clients.append(client)
            print("Nickname is {}".format(nickname))
            broadcast("{} joined!".format(nickname).encode('ascii'))
            client.send('Connected to server!'.encode('ascii'))
            thread = threading.Thread(target=handle, args=(client,))
            thread.start()
        
    receive()
#client
import socket, threading


nickname = input("Choose your nickname: ")

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)      #socket initialization
client.connect(('127.0.0.1', 7976))                             #connecting client to server

def receive():
    while True:                                                 #making valid connection
        try:
            message = client.recv(1024).decode('ascii')
            if message == 'NICKNAME':
                client.send(nickname.encode('ascii'))
            else:
                print(message)
        except:                                                 #case on wrong ip/port details
            print("An error occured!")
            client.close()
            break
def write():
    while True:                                                 #message layout
        message = '{}: {}'.format(nickname, input(''))
        client.send(message.encode('ascii'))

receive_thread = threading.Thread(target=receive)               #receiving multiple messages
receive_thread.start()
write_thread = threading.Thread(target=write)                   #sending messages 
write_thread.start()

The client script uses two threads for writing and receiving data. Both threads use the same client socket. Is that a problem?

My understanding by now is that client.recv() will block the socket (and its own thread) and make the writing thread fail - or vice versa. Is that right?

I saw this: can two threads use the same socket at the same time, and are there possible problems with this? It just states that it is possible to use two threads for receiving and writing as long as you coordinate well.

My question is: If client.recv() is using the socket to listen all the time, how can the other thread send data?


Solution

  • A socket is fully bidirectional. It has separate buffers for sending and receiving. One thread can be reading from a socket while another thread is writing to the same socket, that is perfectly OK and does not require any coordination between the threads. Reading does not block writing, and vice versa.

    What is not OK, though, is having 2+ threads reading from the same socket at the same time, or 2+ threads writing to the same socket at the same time, without coordination.

    And in fact, your client is doing exactly that. Both of its threads are send()'ing to the client socket without coordinating the sends, so the messages will end up overlapping each other on the wire and corrupt your communication.