Python 3 中使用 socket.send() 时发生的 Broken Pipe 错误

147 阅读1分钟

在编写一个客户端-服务器即时消息程序时,经常在 Python 3 中遇到 “[Errno 32] Broken Pipe”错误。该错误通常发生在服务器将消息发送给另一个客户端时,导致程序退出。经过调查发现,此错误通常出现在客户端断开连接时。

v2-61dc0f1534ff20ea13b469141e2bb6ae_720w.jpg

2. 解决方案

经过分析,需要解决两个问题:

  1. 将 select 语句放在循环内,而不是循环外,以便在循环中不断检查是否有数据可读。
  2. 在服务器端的 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” 错误。