I created a messenger using Python Socket, when I use two clients, for example, when one user leaves the chat, the other user can send 1-2 more messages and then the server stops receiving messages from other users, ie there is a known error BrokenpipeError. I understand the terminology of the error, perhaps the error lies on my server in the While True loop (a loop that includes all the actions that users perform with each other) because there is fabulous code in the form of:
if not data:
print(f'User {name1} leave')
break
How can I make the server work under any circumstances and can constantly receive messages from users who leave, log in, stay, etc.? How to make a stream work synchronously without failures? Code below:
server:
import socket
import threading
import time
HOST = '127.0.0.1'
PORT = 8888
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((HOST, PORT))
server.listen(15)
print(f'Server {HOST}:{PORT} start.')
users = []
sort = []
def crypto(text, key):
encrypt = ''
for i in text:
encrypt += chr(ord(i) + key)
return encrypt
def listen_decode(user, addr):
print(f'User IP-address {addr[0]} login..')
sort.append(user)
user.send('Encode'.encode('utf-8'))
user.send('Name'.encode('utf-8'))
name1 = user.recv(1024).decode('utf-8')
users.append(name1)
while True:
data = user.recv(1024).decode('utf-8')
b1 = time.ctime()
atribute = ' | '
data_crypto = crypto(data, 4)
print(f'{name1} sent message: {data_crypto} ' + atribute + '' + b1 + ' ')
for i in sort:
if(i != server and i != user):
i.sendall(f'{name1} > {data}'.encode('utf-8'))
if not data:
print(f'User {name1} leave')
break
def start_server():
while True:
user_socket, addr = server.accept()
potok_info = threading.Thread(target=listen_decode, args=(user_socket, addr))
potok_info.start()
if __name__ == '__main__':
start_server()
Client (this is for server access):
from tkinter import messagebox
from tkinter import *
import _tkinter
import socket
import threading
import os
window = Tk()
window.title('Login')
window.geometry('320x200')
window.resizable(True, True)
HOST = '127.0.0.1'
PORT = 8888
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((HOST, PORT))
name = StringVar()
password = StringVar()
def encrypt(text, key):
encrypt1 = ''
for i in text:
encrypt1 += chr(ord(i) - key)
return encrypt1
def send_message():
while True:
data = client.recv(1024)
print('\r\r' + data.decode('utf-8') + '\n' + f'you: ', end='')
def chat():
string_name = name.get()
if('Name' in client.recv(1024).decode('utf-8')):
name1 = string_name
client.send(name1.encode('utf-8'))
potok = threading.Thread(target=send_message)
potok.start()
while True:
msg = input('you: ')
client.send(msg.encode('utf-8'))
def crypt():
string_name = name.get()
string_password = password.get()
try:
user_encryption_selection = (encryption_listbox.get(encryption_listbox.curselection()))
except _tkinter.TclError:
messagebox.showerror('Error', 'Enter type message')
if string_name == 'John':
if string_password == '5555':
if user_encryption_selection == 'Use Encrypted':
window.after(1000, lambda: window.destroy())
menu = Tk()
menu.title('Menu Chat')
menu.geometry('500x350')
menu.resizable(False, False)
menu_button = Button(menu, text='Global chat', command=chat, height=1, width=18)
menu_button.grid(padx=150)
menu.mainloop()
else:
messagebox.showerror('Error', 'Error password')
else:
messagebox.showerror('Error', 'Error name')
entry = Entry(window, textvariable=name, width=10)
entry.grid(column=1, pady=7, padx=4)
label = Label(window, text='Enter name: ')
label.grid(row=0, padx=1)
entry1 = Entry(window, textvariable=password, width=10)
entry1.grid(column=1, pady=7, padx=2)
label1 = Label(window, text='Enter password: ')
label1.grid(row=1, padx=1)
listbox = Listbox(window, selectmode=SINGLE, width=12, height=2)
listbox.grid(column=1, row=2, pady=7, padx=2)
encryption_options = ['Use Encrypted']
encryption_listbox = Listbox(window, selectmode=SINGLE, width=10, height=1)
encryption_listbox.grid(column=1, row=2, pady=7, padx=2)
for i in encryption_options:
encryption_listbox.insert(END, i)
label_crypto = Label(window, text='Type message: ', bg='black', fg='red')
label_crypto.grid(row=2)
button = Button(window, text='Enter', command=crypt)
button.grid(pady=30)
window.mainloop()
You're putting each client's handling code into a separate thread. If a client disconnects, that thread will notice this (gets an exception, if I understand you right). What you'd do then is to simply stop servicing the client, i.e. terminate the thread. All you need to do is to catch the exception and return. Note that an exception while sending to the other clients must be ignored, only when communicating with the specifically served clients you need to act upon errors!
Notes:
select-based approach is much better.