I'm writing a simple chat program to learn how to use sockets in python. Everything works perfectly, but when a client disconnects, the server stops working: it closes the connections as soon as they send a message, it still accepts connection but when a new connection sends a message it closes it.
Server:
from pickle import TRUE
import socket
import threaded
from threading import Thread
import _thread
import os
import time
#Crating global table for broadcast
global clients
clients = []
#Main
def recive(client):
global clients
name = client.recv(2024)
name = name.decode("utf-8")
closed = False
while True:
#Normal operations
try:
string = client.recv(2024)
string = string.decode("utf-8")
string = name ": " string
if string == "!exit":
#client.close()
print("Connection interrupted")
closed = True
break
else:
for c in clients:
c.send(bytes(string, "utf-8"))
print(string)
#Errors or client disconnection handling
except:
#client.close()
print("Connection interrupted")
break
if __name__ == "__main__":
#Variable assignment
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
ip = str(local_ip)
port = 55555
#Server start
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((ip, port))
server.listen()
print(f"Server listening on {ip} on port {port}")
#server listening
while True:
client, address = server.accept()
print(f"Connectione estabished - {address[0]}:{address[1]}")
clients.append(client)
_thread.start_new_thread(recive ,(client,))
CodePudding user response:
I can't test it but when client disconnects then you have to remove it from list clients.
You don't remove it and when next client connects then server tries to send message to previous client and it may raise error and disconnect new client.
Something like this:
(I uses \n to detect end of message because socket may send many messages as single text)
import socket
from threading import Thread
clients = []
def read(client, buf):
# every message should ends with '\n`
while True:
buf = client.recv(2024)
pos = buf.find(b'\n')
if pos > -1:
message = buf[:pos].decode("utf-8")
buf = buf[pos 1:]
break
return buf, message
def send(client, message):
# every message sends with '\n` at the end
data = (message '\n').encode('utf-8')
client.send(data)
def receive(client, address):
global clients
buf = b""
buf, name = read(client, buf)
print('name:', name)
while True:
try:
buf, message = read(client, buf)
if message == "!exit":
print("Connection interrupted")
break
else:
text = f"{name}: {message}"
print(text)
for c in clients:
send(c, text)
except Exception as ex:
print("Exception", ex)
print("Connection interrupted")
break
# --- after looo ---
#client.close()
print('remove')
if client in clients:
clients.remove(client)
if __name__ == "__main__":
IP = '0.0.0.0'
PORT = 55555
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((IP, PORT))
server.listen()
print(f"Server listening on {IP} on port {PORT}")
while True:
client, address = server.accept()
print(f"Connectione estabished - {address[0]}:{address[1]}")
clients.append(client)
Thread(target=receive, args=(client, address)).start()
Client for tests:
import socket
def send(client, message):
# every message sends with '\n` at the end
data = (message '\n').encode('utf-8')
client.send(data)
client = socket.socket()
client.connect(("0.0.0.0", 55555))
send(client, 'James Bond')
send(client, 'Hello World!')
send(client, 'Bye!')
send(client, '!exit')
client.close()
Maybe it would be better to keep clients in dictionary as {address: connection, ...} because it will be simpler to remove disconected client when there will be more clients.
