You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python多线程Socket服务端匹配客户端处理函数触发KeyError异常

异常触发原因

你遇到的KeyError本质是字典初始化时机和逻辑设计错误导致的:

  1. function_dictionary在服务启动阶段就完成了初始化,此时还没有任何客户端接入,clients是空列表,和functions压缩后生成的是空字典,内部没有任何socket对象作为键。
  2. 后续新客户端接入时,你仅把新的socket对象追加到了clients列表中,从未同步更新function_dictionary的键值对,所以访问function_dictionary[client]时必然找不到对应键,触发KeyError。
  3. 额外逻辑缺陷:你原本的设计是按客户端接入顺序绑定处理函数,完全没有识别客户端类型的步骤,就算字典能正常工作,也会出现把Iconet客户端分配给Robot处理函数的错配问题,不符合需求。
修正实现方案

核心调整逻辑:

  • 不需要动态维护「socket对象->处理函数」的映射,只需要维护固定的「客户端类型字符串->处理函数」映射即可
  • 客户端建立连接后,先读取客户端首包上报的身份类型,校验类型合法后,直接从固定映射中取对应的处理函数启动工作线程
  • 增加异常兜底逻辑,对上报非法类型、连接异常断开的客户端做资源清理,避免socket泄漏

修正后的可运行代码如下:

import threading 
import socket 

PORT = 1026
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER,PORT)
FORMAT = "utf-8"
HEADER = 1024

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
clients = []

def broadcast(values_iconet):
    for client in clients:
        try:
            client.send(values_iconet)
        except:
            # 发送失败说明客户端已断开,清理无效连接
            clients.remove(client)
        
def handle_robot(client,addr):
    print(f"[ROBOT CONNECTION] {addr} connected.")
    client.send('hello robot'.encode(FORMAT))
    connected = True
    while connected:
        try:
            msg = client.recv(HEADER)
            if not msg:
                break
            # 在此处编写Robot客户端专属业务处理逻辑
            print(f"[ROBOT {addr}] {msg.decode(FORMAT)}")
        except:
            break
    # 连接断开后清理资源
    client.close()
    if client in clients:
        clients.remove(client)
    print(f"[ROBOT DISCONNECT] {addr} disconnected.")

def handle_iconet(client,addr):
    print(f"[ICONET CONNECTION] {addr} connected.")
    client.send('hello iconet'.encode(FORMAT))
    connected = True
    while connected:
        try:
            msg = client.recv(HEADER)
            if not msg:
                break
            # 在此处编写Iconet客户端专属业务处理逻辑
            print(f"[ICONET {addr}] {msg.decode(FORMAT)}")
        except:
            break
    # 连接断开后清理资源
    client.close()
    if client in clients:
        clients.remove(client)
    print(f"[ICONET DISCONNECT] {addr} disconnected.")

# 固定维护 客户端类型->处理函数 的映射,不需要动态生成
handler_map = {
    "Robot": handle_robot,
    "Iconet": handle_iconet
}

def start():
    server.listen()
    print(f"[LISTENING] Server is listening on {SERVER}")
    while True:
        client, addr = server.accept()
        clients.append(client)
        print(f"[NEW CONNECTION] {addr} connected.")
        client.send('Welcome, please send your client type'.encode(FORMAT))
        try:
            # 读取客户端首包上报的类型,解码后去掉首尾空白字符
            client_type = client.recv(HEADER).decode(FORMAT).strip()
            # 校验客户端类型合法性
            if client_type not in handler_map:
                print(f"[INVALID CLIENT] {addr} sent unknown type {client_type}, disconnect.")
                client.send("Invalid client type".encode(FORMAT))
                client.close()
                clients.remove(client)
                continue
            # 从固定映射取对应处理函数启动工作线程,设置为守护线程随主进程退出
            thread = threading.Thread(target=handler_map[client_type], args=(client,addr), daemon=True)
            thread.start()
            print(f"[ACTIVE CONNECTIONS] {threading.active_count() -1}")
        except Exception as e:
            print(f"[CONNECTION ERROR] {addr} error: {str(e)}")
            client.close()
            if client in clients:
                clients.remove(client)
       
print ('[STARTING] server is starting')
start()

实现说明

  • 客户端需要在连接成功、收到服务端欢迎消息后,第一时间发送自身类型字符串(Robot或Iconet),服务端识别类型后才会进入对应业务处理流程
  • 工作线程设置为daemon守护线程,服务端退出时会自动回收所有工作线程,避免进程僵死
  • 所有消息收发位置增加了异常捕获,客户端异常断开时会自动从clients列表清理无效socket,避免广播操作时报错

内容的提问来源于stack exchange,提问作者Sho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 07:39:19