凌峰创科服务平台

Python socket服务器端如何实现并发连接处理?

Socket 通信流程

想象一下你正在打电话:

Python socket服务器端如何实现并发连接处理?-图1
(图片来源网络,侵删)
  1. 买电话(创建 Socket):你需要一部电话(一个 socket 对象)。
  2. 插上电话线并绑定号码(Bind & Listen):你把电话插上墙(绑定到一个 IP 地址和端口),并设置电话为响铃状态(监听来自客户端的连接)。
  3. 等待来电(Accept):电话响了,你接起电话(接受一个连接),这时,你和来电者之间建立了一条独立的通话线路(一个新的 socket)。
  4. 通话(Send/Recv):你和来电者通过这条线路开始对话(发送和接收数据)。
  5. 挂断(Close):通话结束,双方都挂断电话(关闭 socket)。

Python 的 socket 编程完全遵循这个逻辑。


最简单的服务器端示例(TCP)

这是一个最基础的 TCP 服务器,它只接受一个客户端的连接,接收一条消息,然后关闭。

# server_simple.py
import socket
# 1. 创建一个 socket 对象
# socket.AF_INET 表示使用 IPv4 地址
# socket.SOCK_STREAM 表示使用 TCP 协议
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 绑定 IP 地址和端口号
# '0.0.0.0' 表示监听本机所有可用的网络接口
# 8888 是端口号,可以自定义,但要注意不要与其他程序冲突
server_address = ('0.0.0.0', 8888)
server_socket.bind(server_address)
# 3. 开始监听 incoming connections
# 5 是挂起队列的最大连接数
server_socket.listen(5)
print(f"服务器启动,正在监听 {server_address[0]}:{server_address[1]}...")
# 4. 等待并接受客户端连接
# accept() 是一个阻塞函数,程序会在这里等待,直到有客户端连接
# 它会返回一个 (conn, addr) 元组
# conn: 一个新的 socket 对象,用于与这个特定的客户端通信
# addr: 客户端的地址 (IP 地址, 端口号)
conn, client_addr = server_socket.accept()
# 5. 与客户端通信
print(f"已接受来自 {client_addr} 的连接!")
# 接收客户端发送的数据
# recv(1024) 表示每次最多接收 1024 字节的数据
# recv() 也是一个阻塞函数
data = conn.recv(1024)
print(f"收到来自客户端的消息: {data.decode('utf-8')}")
# 向客户端发送响应
response = "你好,客户端!你的消息已收到。"
conn.sendall(response.encode('utf-8'))
# 6. 关闭连接
# 关闭与客户端的通信 socket
conn.close()
# 关闭服务器监听 socket
server_socket.close()
print("连接已关闭,服务器退出。")

如何运行这个例子?

  1. 保存代码:将上面的代码保存为 server_simple.py
  2. 运行服务器:在终端中运行 python server_simple.py,你会看到 "服务器启动..." 的提示,程序会停在那里等待连接。
  3. 测试客户端:打开另一个终端,使用 netcat (nc) 或 Python 客户端来连接。
    • 使用 netcat (推荐,因为它简单): nc localhost 8888,然后输入一些文字并按回车。
    • 或者,你可以运行下面这个简单的客户端脚本。

一个简单的客户端示例(用于测试)

# client_simple.py
import socket
# 1. 创建一个 socket 对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 连接服务器
server_address = ('127.0.0.1', 8888) # 连接到本地的 8888 端口
client_socket.connect(server_address)
print(f"已连接到服务器 {server_address}")
# 3. 发送数据
message = "你好,服务器!"
client_socket.sendall(message.encode('utf-8'))
# 4. 接收服务器的响应
data = client_socket.recv(1024)
print(f"收到服务器的响应: {data.decode('utf-8')}")
# 5. 关闭连接
client_socket.close()

测试步骤:

  1. 在一个终端运行 python server_simple.py
  2. 在另一个终端运行 python client_simple.py
  3. 观察服务器终端的输出,它会显示客户端的连接和消息。

进阶:能够处理多个客户端的服务器

上面的服务器一次只能服务一个客户端,因为它在 accept() 之后会一直等待数据,无法再接受新的连接,要处理多个客户端,最简单的方法是使用多线程

Python socket服务器端如何实现并发连接处理?-图2
(图片来源网络,侵删)

工作原理: 主线程负责监听和接受新的连接,每当有一个新的客户端连接时,就创建一个新线程来专门处理这个客户端的通信,而主线程则继续回去监听下一个连接。

# server_multi_thread.py
import socket
import threading
# 定义一个处理单个客户端连接的函数
def handle_client_connection(conn, client_addr):
    """与单个客户端进行通信"""
    print(f"[新连接] {client_addr} 已连接。")
    try:
        while True:
            # 接收数据
            data = conn.recv(1024)
            if not data:
                # recv() 返回空数据,说明客户端已关闭连接
                print(f"[客户端断开] {client_addr} 断开了连接。")
                break
            print(f"[来自 {client_addr}] {data.decode('utf-8')}")
            # 发送响应
            response = f"服务器已收到你的消息: {data.decode('utf-8')}"
            conn.sendall(response.encode('utf-8'))
    except ConnectionResetError:
        print(f"[连接错误] {client_addr} 强制断开了连接。")
    finally:
        # 确保连接被关闭
        conn.close()
        print(f"[连接关闭] 与 {client_addr} 的连接已关闭。")
def main():
    # 1. 创建并绑定服务器 socket
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = ('0.0.0.0', 8888)
    server_socket.bind(server_address)
    server_socket.listen(5)
    print(f"服务器启动,正在监听 {server_address}...")
    try:
        while True:
            # 2. 循环接受新的连接
            conn, client_addr = server_socket.accept()
            # 3. 为每个新连接创建一个新线程
            # target 指定线程要执行的函数
            # args 指定传递给函数的参数
            client_thread = threading.Thread(target=handle_client_connection, args=(conn, client_addr))
            # 将线程设置为守护线程,这样当主线程退出时,子线程也会强制退出
            client_thread.daemon = True
            # 启动线程
            client_thread.start()
            print(f"[活动连接数] {threading.active_count() - 1}") # 减去主线程本身
    except KeyboardInterrupt:
        print("\n服务器正在关闭...")
    finally:
        server_socket.close()
        print("服务器已关闭。")
if __name__ == '__main__':
    main()

如何测试多线程服务器?

  1. 运行 python server_multi_thread.py
  2. 打开两个或更多终端,每个终端都运行 python client_simple.py (或者用 nc 连接)。
  3. 你会发现服务器能够同时响应所有连接的客户端,而不会阻塞其他连接。

关键点解析

代码/概念 解释
socket.socket(family, type) 创建一个 socket 对象。family 通常是 AF_INET (IPv4) 或 AF_INET6 (IPv6)。type 通常是 SOCK_STREAM (TCP) 或 SOCK_DGRAM (UDP)。
socket.bind(address) 将 socket 绑定到一个特定的地址和端口,地址是 ('host', port) 元组。'0.0.0.0' 表示监听所有可用的网络接口。
socket.listen(backlog) 开始监听传入的连接。backlog 是挂起队列的最大连接数。
socket.accept() 接受一个连接,它返回一个新的 socket 对象 (conn) 和客户端的地址 (addr),这是一个阻塞调用。
socket.recv(bufsize) 接收数据。bufsize 是一次接收的最大字节数,这也是一个阻塞调用,如果没有数据,它会等待,当客户端关闭连接时,它会返回空数据 (b'')。
socket.sendall(data)
Python socket服务器端如何实现并发连接处理?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇