在编写一个客户端-服务器即时消息程序时,经常在 Python 3 中遇到 “[Errno 32] Broken Pipe”错误。该错误通常发生在服务器将消息发送给另一个客户端时,导致程序退出。经过调查发现,此错误通常出现在客户端断开连接时。
2. 解决方案
经过分析,需要解决两个问题:
- 将 select 语句放在循环内,而不是循环外,以便在循环中不断检查是否有数据可读。
- 在服务器端的 broadcast 函数中,需要使用 socket.send 而不是 s.send,因为 s 是监听套接字,而 socket 是连接的客户端套接字。
以下是修改后的代码:
# 服务器代码
import socket, select, sys
def broadcast(sock, messaged):
for socket in connection_list:
if socket != s and socket != sock:
try:
socket.send(messaged.encode("utf-8"))
except BrokenPipeError as e:
print(e)
sys.exit()
connection_list = []
host = ''
port = 5558
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
s.listen(5)
connection_list.append(s)
while True:
read_sockets, write_sockets, error_sockets = select.select(connection_list, [], [])
for sock in read_sockets:
if sock == s:
conn, addr = s.accept()
connection_list.append(conn)
client = "Client (%s,%s) connected" % addr
print(client)
broadcast(sock, client)
else:
try:
data = sock.recv(2048)
if not data:
offline = "Client " + addr + " is offline"
broadcast(sock, offline)
print(offline)
connection_list.remove(sock)
sock.close()
continue
decodeddata = data.decode("utf-8")
broadcast(sock, decodeddata)
except:
offline = "Client " + addr + " is offline"
broadcast(sock, offline)
print(offline)
connection_list.remove(sock)
sock.close()
continue
# 客户端代码
import socket, select, string, sys, time
def prompt(data):
print("<You> " + data)
def Person(data):
print("<Receiver> " + data)
if __name__ == "__main__":
host = "localhost"
port = 5558
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(2)
try:
s.connect((host, port))
except:
print('Unable to connect')
sys.exit()
print('Connected.')
socket_list = [s]
read_sockets, write_sockets, error_sockets = select.select(socket_list, [], [])
while 1:
# 把 select 语句放在循环内
read_sockets, write_sockets, error_sockets = select.select(socket_list, [], [])
for sock in read_sockets:
if sock == s:
try:
time.sleep(1)
data = sock.recv(1024)
Person(data.decode("utf-8"))
except:
msg = input("Send a message: ")
try:
s.send(str.encode(msg))
except:
print("Server is offline")
sys.exit()
else:
print("Server is offline")
sys.exit()
通过以上修改,程序应该能够正常运行,不会再出现 “Broken Pipe” 错误。