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.