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

Python Socket聊天室运行报WinError 10038套接字错误求助

问题诊断

OSError: [WinError 10038] 错误的本质是:代码试图对已经关闭、失效的socket对象执行send/recv等网络操作。结合你贴出的服务端、客户端代码,共有5处逻辑缺陷会稳定触发该错误:

  • 服务端broadcast函数无有效性校验:当管理员执行KICK/BAN操作、客户端异常断开时,对应socket会被提前关闭并从连接列表移除,若此时其他线程刚好执行broadcast遍历连接列表,就会拿到已关闭的socket执行send操作,直接触发错误。
  • 客户端退出逻辑缺失线程终止标记:客户端输入quit()时仅调用client.close()关闭连接,未设置stop_thread = True,receive和write两个工作线程不会立刻退出循环,后续仍会尝试对已关闭的socket执行IO操作。
  • 服务端踢人逻辑存在竞态条件:kickUser函数关闭客户端socket后,对应该连接的handle线程会触发异常进入断开清理流程,两个线程同时修改clients、nicknames列表时,极易出现列表索引错位、重复操作已关闭socket的问题。
  • 多线程操作socket无同步保护:客户端两个工作线程同时操作同一个全局socket对象,极端场景下一个线程关闭socket时,另一个线程刚好在执行send/recv,也会触发该错误。
  • 服务端BAN命令逻辑不完整:处理BAN指令时仅调用kickUser踢人,未做异常捕获,若踢人过程中socket已失效,会直接抛错打断后续ban列表写入流程,残留无效连接记录。
修复方案

1. 改造服务端broadcast函数,增加失效连接自动清理

倒序遍历连接列表,发送失败时自动清理失效socket,避免列表移除元素导致的索引错位:

def broadcast(message):
    # 倒序遍历避免删除元素时索引错乱
    for i in range(len(clients)-1, -1, -1):
        client = clients[i]
        try:
            client.send(message)
        except:
            client.close()
            if i < len(nicknames):
                del nicknames[i]
            del clients[i]

2. 补全客户端quit退出逻辑

关闭socket前设置线程终止标记,关闭后直接跳出循环,避免线程后续操作失效连接:

if message[len(nickname) + 2:] == 'quit()':
    print(f'{nickname} left!')
    global stop_thread
    stop_thread = True
    client.close()
    break

3. 修复服务端kickUser函数的竞态问题

先清理列表记录再执行广播,避免广播遍历到已关闭的socket,发送踢人通知时增加异常捕获:

def kickUser(name):
    if name in nicknames:
        name_index = nicknames.index(name)
        clientToKick = clients[name_index]
        try:
            clientToKick.send("You were kicked by an admin".encode('ascii'))
        except:
            pass
        clientToKick.close()
        # 先从列表移除失效连接,再执行广播
        del clients[name_index]
        del nicknames[name_index]
        broadcast(f'{name} was kicked by admin'.encode('ascii'))

4. 加固服务端handle线程的异常清理逻辑

清理失效连接前先做存在性判断,避免重复删除列表元素触发二次错误:

except:
    if client in clients:
        index = clients.index(client)
        clients.remove(client)
        client.close()
        nickname = nicknames[index]
        nicknames.remove(nickname)
        # 清理完成后再广播离开通知
        broadcast(f'{nickname} left the chat'.encode('ascii'))
    break

5. 补全服务端BAN指令逻辑

踢人操作完成后再写入ban列表,避免踢人失败时写入无效的封禁记录,同时可以补充你注释里提到的管理员权限校验逻辑,防止普通用户发送KICK/BAN指令。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 22:51:19