如何在套接字上不断接收用户的消息



我使用Python Socket创建了一个信使,当我使用两个客户端时,例如,当一个用户离开聊天室时,另一个用户可以再发送1-2条消息,然后服务器停止接收来自其他用户的消息,即存在已知错误BrokenpipeError。我理解错误的术语,也许错误在于我的服务器上的While True循环(一个包括用户彼此执行的所有操作的循环(,因为有以下形式的精彩代码:

if not data:
print(f'User {name1} leave')
break

如何使服务器在任何情况下都能工作,并不断接收来自离开、登录、停留等用户的消息。?如何使流同步工作而不出现故障?以下代码:

服务器:

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()

客户端(用于服务器访问(:

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('rr' + 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()

您将每个客户端的处理代码放入一个单独的线程中。如果客户端断开连接,该线程会注意到这一点(如果我理解正确的话,会出现异常(。然后您要做的就是简单地停止为客户端提供服务,即终止线程。您所需要做的就是捕获异常并返回。请注意,发送到其他客户端时的异常必须被忽略,只有在与特定服务的客户端通信时,您才需要对错误采取行动!

注:

  • 这种为客户提供服务的风格很有效,但扩展性不好。使用基于select的方法要好得多
  • 使用线程会带来另一个问题:竞争条件。简而言之,从多个线程访问同一资源是有问题的,因为一个线程可能会修改另一个线程正在修改的内容,从而导致不一致
  • 我再也不会使用普通套接字了,除非我真的需要它们来实现现有的协议。相反,我会使用ZeroMQ或Protobuf之类的工具

最新更新