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?
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.