凌峰创科服务平台

Python socket服务器如何实现高并发?

第 1 部分:最简单的 TCP 服务器

这个例子将实现最基本的功能:启动一个服务器,监听一个端口,接收一个客户端的连接,接收一条消息,打印出来,然后关闭连接。

Python socket服务器如何实现高并发?-图1
(图片来源网络,侵删)

代码示例

# 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. 开始监听,等待客户端连接
# 5 是连接队列的长度,表示最多有多少个等待连接的客户端
server_socket.listen(5)
print(f"服务器启动,正在监听 {server_address[0]}:{server_address[1]}...")
# 4. 接受客户端连接
# accept() 是一个阻塞函数,程序会在这里等待,直到有客户端连接
# 它返回一个 (client_socket, client_address) 元组
# client_socket 是一个新的 socket 对象,用于与这个特定的客户端通信
# client_address 是客户端的 IP 地址和端口号
client_socket, client_address = server_socket.accept()
print(f"已接受来自 {client_address} 的连接!")
# 5. 接收客户端发送的数据
# recv(1024) 表示最多接收 1024 字节的数据
# 它也是一个阻塞函数,会等待客户端发送数据
data = client_socket.recv(1024).decode('utf-8')
print(f"收到来自客户端的消息: {data}")
# 6. 向客户端发送响应
response = "你好,客户端!你的消息已收到。"
client_socket.send(response.encode('utf-8'))
# 7. 关闭连接
client_socket.close()
server_socket.close()
print("连接已关闭,服务器退出。")

如何运行

  1. 保存代码:将上面的代码保存为 server_simple.py

  2. 运行服务器:在终端中执行 python server_simple.py

    $ python server_simple.py
    服务器启动,正在监听 0.0.0.0:8888...

    服务器会阻塞在 server_socket.accept() 等待客户端连接。

  3. 使用客户端测试:打开另一个终端,使用 telnetnc (netcat) 作为客户端来连接服务器。

    Python socket服务器如何实现高并发?-图2
    (图片来源网络,侵删)
    # 使用 telnet
    $ telnet 127.0.0.1 8888
    # 或者使用 nc (netcat)
    $ nc 127.0.0.1 8888

    连接成功后,在客户端终端输入一些消息,hello server,然后按回车。

  4. 观察服务器输出:回到服务器的终端,你会看到:

    服务器启动,正在监听 0.0.0.0:8888...
    已接受来自 ('127.0.0.1', 54321) 的连接!  # 端口号可能不同
    收到来自客户端的消息: hello server
    连接已关闭,服务器退出。

    客户端终端也会收到服务器的响应:

    hello server
    你好,客户端!你的消息已收到。

第 2 部分:改进服务器 - 处理多个客户端(多线程)

上面的服务器只能处理一个客户端连接,一旦连接关闭,服务器就退出了,在实际应用中,服务器需要能够同时处理多个客户端的请求,最简单的方法是使用多线程。

Python socket服务器如何实现高并发?-图3
(图片来源网络,侵删)

当一个客户端连接进来时,我们创建一个新的线程来处理这个客户端的所有通信,主线程则继续回到 accept() 状态,等待下一个客户端。

代码示例

# server_threaded.py
import socket
import threading
# 定义一个函数,用于处理与单个客户端的通信
def handle_client(client_socket, client_address):
    print(f"[新连接] {client_address} 已连接。")
    try:
        while True:
            # 接收数据
            data = client_socket.recv(1024).decode('utf-8')
            if not data:
                # recv() 返回空数据,表示客户端已关闭连接
                break
            print(f"[来自 {client_address}] {data}")
            # 发送响应
            response = f"服务器已收到你的消息: {data}"
            client_socket.send(response.encode('utf-8'))
    except ConnectionResetError:
        print(f"[连接断开] {client_address} 异常断开。")
    finally:
        # 确保连接被关闭
        client_socket.close()
        print(f"[连接关闭] {client_address} 的连接已关闭。")
# --- 主服务器逻辑 ---
if __name__ == "__main__":
    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[0]}:{server_address[1]}...")
    try:
        while True:
            # 接受新的客户端连接
            client_socket, client_address = server_socket.accept()
            # 为每个客户端连接创建一个新的线程
            client_thread = threading.Thread(
                target=handle_client, 
                args=(client_socket, client_address)
            )
            # 设置为守护线程,这样当主线程退出时,子线程也会随之退出
            client_thread.daemon = True
            client_thread.start()
            print(f"[活动连接] 当前有 {threading.active_count() - 1} 个客户端连接。")
    except KeyboardInterrupt:
        print("\n服务器正在关闭...")
    finally:
        server_socket.close()
        print("服务器已关闭。")

如何运行和测试

  1. 保存代码:保存为 server_threaded.py
  2. 运行服务器
    $ python server_threaded.py
    服务器启动,正在监听 0.0.0.0:8888...
  3. 测试
    • 打开两个或三个终端,分别运行 telnet 127.0.0.1 8888
    • 在每个 telnet 窗口中输入不同的消息,"client 1", "client 2"。
    • 你会看到服务器终端能够同时打印出来自不同客户端的消息,并且活动连接数在增加。
    • 关闭一个 telnet 窗口,服务器终端会打印出对应的连接关闭信息。

第 3 部分:关键概念和最佳实践

socket.socket() - 创建套接字

  • socket.AF_INET: 用于 IPv4 网络。
  • socket.AF_INET6: 用于 IPv6 网络。
  • socket.SOCK_STREAM: 面向连接的 TCP 协议。
  • socket.SOCK_DGRAM: 无连接的 UDP 协议。

socket.bind() - 绑定地址

  • 服务器必须绑定一个特定的 IP 地址和端口号。
  • IP 地址 '0.0.0.0' 是一个特殊地址,表示“所有可用的网络接口”,这意味着服务器可以从本机的任何 IP 地址(如 0.0.1 或局域网 IP)接收连接。
  • 端口号注意:端口号范围是 0-65535,0-1023 是系统保留端口(如 HTTP 80, HTTPS 443),普通用户程序应使用 1024 以上的端口。

socket.listen() - 开始监听

  • listen(backlog): backlog 参数指定了连接队列的长度,当服务器繁忙时,新的连接请求会被放入这个队列中等待,如果队列满了,新的连接请求可能会被拒绝。

socket.accept() - 接受连接

  • 这是服务器端的关键阻塞点,它等待客户端发起连接。
  • 成功后,它会返回一个新的 客户端套接字 (client_socket) 和 客户端地址 (client_address)。
  • 重要server_socket 只负责接受连接,之后与客户端的所有数据收发(send, recv)都应该使用 client_socket

socket.recv()socket.send() - 数据收发

  • recv(buffer_size): 从套接字中接收数据。buffer_size 指定了最多接收多少字节,它是阻塞的,如果没有数据到达,它会等待。
  • 返回值:如果连接正常关闭,recv() 会返回一个空字节对象 `
分享:
扫描分享到社交APP
上一篇
下一篇