我编写了一个语音聊天应用程序代码,但是当我们运行此代码并加入语音频道时,我收到照片中的错误
这是我的代码; 客户端代码:
import tkinter as tk
from tkinter import messagebox
import pyaudio
import socket
import threading
import time
HOST = '127.0.0.1'
PORT = 65432
class VoiceChatApp:
def __init__(self, root):
self.root = root
self.root.title("Voice Chat Application")
self.username = ""
self.channel = None
self.is_mic_on = True
self.setup_gui()
self.p = pyaudio.PyAudio()
self.stream = None
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect_to_server()
threading.Thread(target=self.receive_data, daemon=True).start()
def setup_gui(self):
self.root.geometry("300x400")
self.root.configure(bg="#2e2e2e")
# Title label
tk.Label(self.root, text="Voice Chat", font=("Helvetica", 16, "bold"), fg="#ffffff", bg="#2e2e2e").pack(pady=10)
# Username entry
self.username_entry = tk.Entry(self.root, font=("Helvetica", 12), bg="#ffffff", fg="#000000")
self.username_entry.insert(0, "Enter username")
self.username_entry.pack(pady=5, padx=10, fill=tk.X)
# Set Username button
tk.Button(self.root, text="Set Username", command=self.set_username, font=("Helvetica", 12), bg="#4CAF50",
fg="#ffffff", relief=tk.RAISED).pack(pady=5, padx=10, fill=tk.X)
# Channel selection
self.channel_var = tk.StringVar(value="Select Channel")
self.channel_menu = tk.OptionMenu(self.root, self.channel_var, "Channel1", "Channel2")
self.channel_menu.config(font=("Helvetica", 12), bg="#ffffff", fg="#000000")
self.channel_menu.pack(pady=5, padx=10, fill=tk.X)
# Join Channel button
tk.Button(self.root, text="Join Channel", command=self.join_channel, font=("Helvetica", 12), bg="#2196F3",
fg="#ffffff", relief=tk.RAISED).pack(pady=5, padx=10, fill=tk.X)
# Leave Channel button
tk.Button(self.root, text="Leave Channel", command=self.leave_channel, font=("Helvetica", 12), bg="#FFC107",
fg="#ffffff", relief=tk.RAISED).pack(pady=5, padx=10, fill=tk.X)
# Current Channel label
self.current_channel_label = tk.Label(self.root, text="Current Channel: None", font=("Helvetica", 12),
fg="#ffffff", bg="#2e2e2e")
self.current_channel_label.pack(pady=5)
# Mute button
self.mute_button = tk.Button(self.root, text="Mute Microphone", command=self.toggle_mic, font=("Helvetica", 12),
bg="#F44336", fg="#ffffff", relief=tk.RAISED)
self.mute_button.pack(pady=5, padx=10, fill=tk.X)
# User list
self.user_list_label = tk.Label(self.root, text="Users in Channel:", font=("Helvetica", 12), fg="#ffffff",
bg="#2e2e2e")
self.user_list_label.pack(pady=5)
self.user_listbox = tk.Listbox(self.root, font=("Helvetica", 12), bg="#ffffff", fg="#000000")
self.user_listbox.pack(pady=5, padx=10, fill=tk.BOTH, expand=True)
# Exit button
tk.Button(self.root, text="Exit Application", command=self.exit_application, font=("Helvetica", 12),
bg="#F44336", fg="#ffffff", relief=tk.RAISED).pack(pady=5, padx=10, fill=tk.X)
def connect_to_server(self):
while True:
try:
self.socket.connect((HOST, PORT))
break
except Exception as e:
print(f"Connection attempt failed: {e}")
time.sleep(5)
def set_username(self):
self.username = self.username_entry.get()
if not self.username:
messagebox.showwarning("Warning", "Username cannot be empty")
else:
messagebox.showinfo("Info", f"Username set to {self.username}")
def join_channel(self):
self.channel = self.channel_var.get()
if self.channel in ["Channel1", "Channel2"]:
message = f"JOIN|{self.channel}|{self.username}"
try:
self.socket.send(message.encode())
self.start_audio_stream()
self.current_channel_label.config(text=f"Current Channel: {self.channel}")
except Exception as e:
messagebox.showerror("Connection Error", f"Unable to send message to server: {e}")
else:
messagebox.showwarning("Warning", "Please select a valid channel")
def leave_channel(self):
if self.channel:
message = f"LEAVE|{self.channel}|{self.username}"
try:
self.socket.send(message.encode())
self.stop_audio_stream()
self.current_channel_label.config(text="Current Channel: None")
self.channel = None
except Exception as e:
messagebox.showerror("Connection Error", f"Unable to send message to server: {e}")
else:
messagebox.showwarning("Warning", "You are not in a channel")
def start_audio_stream(self):
try:
device_index = None
for i in range(self.p.get_device_count()):
info = self.p.get_device_info_by_index(i)
if info['maxInputChannels'] > 0:
device_index = i
break
if device_index is None:
raise ValueError("No input device found")
self.stream = self.p.open(format=pyaudio.paInt16,
channels=1,
rate=44100,
input=True,
input_device_index=device_index,
frames_per_buffer=1024,
stream_callback=self.audio_callback)
except Exception as e:
messagebox.showerror("Audio Error", f"Unable to start audio stream: {e}")
def audio_callback(self, in_data, frame_count, time_info, status):
if self.is_mic_on:
try:
self.socket.sendall(in_data)
except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError) as e:
print(f"Audio send error: {e}")
self.stop_audio_stream()
messagebox.showerror("Connection Error", "Connection lost while sending audio data")
return (None, pyaudio.paContinue)
def stop_audio_stream(self):
if self.stream:
try:
self.stream.stop_stream()
self.stream.close()
except IOError as e:
print(f"Stream stop error: {e}")
self.stream = None
def toggle_mic(self):
self.is_mic_on = not self.is_mic_on
status = "Muted" if not self.is_mic_on else "Unmuted"
self.mute_button.config(text=f"Microphone {status}")
def receive_data(self):
while True:
try:
message = self.socket.recv(1024).decode()
if message.startswith("UPDATE"):
_, channel, users = message.split('|', 2)
self.user_listbox.delete(0, tk.END)
for user in users.split(', '):
self.user_listbox.insert(tk.END, user)
except (ConnectionResetError, ConnectionAbortedError) as e:
print(f"Receive data error: {e}")
self.connect_to_server()
except Exception as e:
print(f"Unexpected error: {e}")
def exit_application(self):
if self.stream:
self.stop_audio_stream()
self.socket.close()
self.root.destroy()
if __name__ == "__main__":
root = tk.Tk()
app = VoiceChatApp(root)
root.mainloop()
服务器代码:
import socket
import threading
HOST = '127.0.0.1'
PORT = 65432
channels = {"Channel1": [], "Channel2": []}
connections = []
def handle_client(conn, addr):
print(f"Yeni bağlantı: {addr}")
try:
while True:
message = conn.recv(1024).decode()
if not message:
break
command = message.split('|')
if command[0] == "JOIN":
channel = command[1]
username = command[2]
if channel in channels:
channels[channel].append(username)
update_users_in_channel(channel)
elif command[0] == "LEAVE":
channel = command[1]
username = command[2]
if channel in channels and username in channels[channel]:
channels[channel].remove(username)
update_users_in_channel(channel)
except (ConnectionResetError, ConnectionAbortedError) as e:
print(f"Connection error: {e}")
finally:
conn.close()
connections.remove(conn)
print(f"Bağlantı kapandı: {addr}")
def update_users_in_channel(channel):
user_list = ', '.join(channels[channel])
message = f"UPDATE|{channel}|{user_list}"
for conn in connections:
try:
conn.send(message.encode())
except (BrokenPipeError, ConnectionResetError) as e:
print(f"Broadcast error: {e}")
connections.remove(conn)
def start_server():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((HOST, PORT))
server_socket.listen()
print("Sunucu başlatıldı ve dinleniyor...")
while True:
conn, addr = server_socket.accept()
connections.append(conn)
threading.Thread(target=handle_client, args=(conn, addr), daemon=True).start()
if __name__ == "__main__":
start_server()
我不明白出了什么问题
如果您能帮助我,我将非常感激。
我正在尝试通过制作一个不和谐的副本来提高自己简单的界面。我在编写代码时也得到了 chatgpt 的帮助。但他找不到我遇到的这个错误的解决方案。
代码中有几个问题可能会导致遇到的错误:
客户端问题:
-
音频回调中的套接字错误处理: 在
audio_callback
函数中,正在捕获BrokenPipeError
、ConnectionResetError
和ConnectionAbortedError
,但没有清除通道信息或通知服务器已断开连接。这可能会导致服务器尝试将数据发送到一个已关闭的套接字,从而产生“管道损坏”错误。 -
缺少退出处理: 当关闭客户端窗口时,没有正确地关闭套接字连接或停止音频流。这可能会导致服务器端出现错误,因为它不知道客户端已断开连接。
服务器端问题:
-
广播中的套接字错误处理:
在
update_users_in_channel
函数中,正在迭代所有连接并发送更新。但是,如果其中一个连接断开,将收到错误,并且不会向列表中的其余连接发送更新。
解决方案:
客户端:
-
在
audio_callback
中处理断开连接:python def audio_callback(self, in_data, frame_count, time_info, status): if self.is_mic_on: try: self.socket.sendall(in_data) except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError) as e: print(f"Audio send error: {e}") self.stop_audio_stream() self.channel = None # 清除通道信息 self.current_channel_label.config(text="Current Channel: None") messagebox.showerror("Connection Error", "Connection lost while sending audio data") return (None, pyaudio.paContinue)
-
在
exit_application
中发送离开消息:python def exit_application(self): if self.channel: self.leave_channel() # 发送离开消息到服务器 if self.stream: self.stop_audio_stream() self.socket.close() self.root.destroy()
服务器端:
-
在
update_users_in_channel
中处理断开连接:python def update_users_in_channel(channel): user_list = ', '.join(channels[channel]) message = f"UPDATE|{channel}|{user_list}" for conn in connections[:]: # 迭代连接副本 try: conn.send(message.encode()) except (BrokenPipeError, ConnectionResetError) as e: print(f"Broadcast error: {e}") connections.remove(conn)
通过进行这些更改,将处理套接字错误并防止应用程序在客户端或服务器意外断开连接时崩溃。
其他建议:
-
考虑使用更强大的网络库,如
asyncio
或Twisted
,以更好地处理异步套接字通信。 - 在的代码中添加更多错误检查和日志记录,以帮助调试问题。
希望这些解决方案能帮助解决语音聊天应用程序中的错误。如果有任何其他问题,请告诉我!
标签:python,server,python-multithreading,pyaudio,python-sockets From: 78849986