You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Python Socket跨两台机器无法连接及WinError 10038错误排查求助

问题排查与解决方案

我来帮你拆解这两个常见的Socket开发问题,咱们逐个解决:

一、WinError 10038 错误的根源与修复

这个错误的本质是你在已经关闭的套接字对象上执行了操作。看你的客户端代码就能发现问题:

客户端的run函数里写了一个无限循环,一直调用_socket.recv()接收服务器命令,但当客户端执行完服务器发送的_socket.close()后,_socket已经被关闭,可循环还在继续跑,下一次再调用recv()时,就会触发「尝试对非套接字操作」的错误(WinError 10038)。

具体修复步骤:

  1. 客户端修改:让客户端在执行关闭命令后跳出循环,避免无效操作。比如给关闭命令加个判断,执行后终止循环:
import socket

def run(mode='client'):
    _socket = socket.socket()
    _socket.connect(('192.168.0.101', 1234))
    keep_running = True
    while keep_running:
        command = _socket.recv(1024).decode('utf-8')
        # 检测到关闭命令时,执行后终止循环
        if command == "_socket.close()":
            exec(command)
            keep_running = False
        else:
            exec(command)

while True:
    try:
        run()
        # 正常退出后稍作休息再重连,避免频繁重试
        import time
        time.sleep(1)
    except OSError as e:
        print(f"连接出错啦: {e}")
        import time
        time.sleep(1)
  1. 服务器端补充:处理完每个客户端请求后,主动关闭客户端套接字c,避免资源泄漏(虽然不是直接导致10038的原因,但能让服务器更稳定):
    在服务器代码的这两个位置添加c.close()
# 处理无效主机名的分支
except:
    c.send(bytes('''print("The hostname is either invalid or wasn't found")''','utf-8'))
    c.send(bytes('_socket.close()','utf-8'))
    c.close()  # 新增:关闭客户端连接
    continue

# 处理有效主机名的分支
c.send(bytes("print('"+ip+"')",'utf-8'))
c.send(bytes('_socket.close()','utf-8'))
c.close()  # 新增:关闭客户端连接

二、跨机器无法连接的排查思路与解决

跨机器连接失败通常是网络或配置问题,咱们按优先级排查:

1. 先确认网络连通性

  • 同局域网检查:让朋友先ping你的服务器IP(192.168.0.101),如果ping不通,说明两台机器不在同一个局域网(比如不在同一WiFi/子网),或者网络路由有问题。
  • 端口开放检查:如果ping通,让朋友用telnet 192.168.0.101 1234测试端口是否开放:
    • 若telnet连接失败,大概率是防火墙问题(即使你说没拦截,再仔细确认一遍):
      • 你的Windows防火墙:进入「Windows Defender防火墙」→「高级设置」→「入站规则」,确保允许Python程序(或直接放行1234端口)接收来自局域网的连接。
      • 朋友的电脑防火墙:检查是否允许出站连接到1234端口。

2. 确认服务器IP与绑定设置

  • IP正确性:在服务器上运行ipconfig查看IPv4地址,确认是不是192.168.0.101——很多局域网IP是动态分配的,可能已经变了,导致客户端连错地址。
  • 绑定设置:你的服务器绑定('', port)是对的,等价于绑定0.0.0.0,允许所有网卡访问,这部分没问题。如果朋友在外网(不在你的局域网),192.168.x.x是内网IP,无法直接访问,需要在路由器上做端口映射,或者使用公网IP/动态域名。

3. 代码逻辑的隐患(重要!)

你现在的实现方式是服务器发送可执行代码给客户端,让客户端exec执行,这种方式不仅极度不安全(客户端会执行服务器发送的任意代码),还可能因为编码、命令拆分的问题导致客户端无法正确执行,间接表现为连接异常。

建议改成数据交互模式,只传递数据而非执行代码,这样更稳定安全:

优化后的服务器端:

import socket
from time import sleep

def run_server(port=1234):
    print('Booting server...')
    print('|-|-|-', end='')
    sleep(0.05)
    server = socket.socket()
    server.bind(('', port))
    print('|-|-|-', end='')
    sleep(0.05)
    server.listen(5)
    print('|-|-|', end='')
    sleep(0.05)
    print('\nServer is running and can be accessed now\n===============================================')
    while True:
        c, addr = server.accept()
        print('Received connection from: ', addr)
        # 发送提示消息,让客户端输入主机名
        c.send(bytes("Welcome. Enter hostname to extract ip from: ", 'utf-8'))
        # 接收客户端发送的主机名
        hostname = c.recv(1024).decode('utf-8').strip()
        try:
            ip = socket.gethostbyname(hostname)
            c.send(bytes(f"IP Address: {ip}", 'utf-8'))
        except:
            c.send(bytes("The hostname is either invalid or wasn't found", 'utf-8'))
        c.close()  # 关闭客户端连接

run_server()

优化后的客户端:

import socket

def run():
    _socket = socket.socket()
    try:
        _socket.connect(('192.168.0.101', 1234))
        # 接收服务器的提示消息
        welcome_msg = _socket.recv(1024).decode('utf-8')
        print(welcome_msg)
        # 输入主机名并发送给服务器
        hostname = input().strip()
        _socket.send(bytes(hostname, 'utf-8'))
        # 接收服务器返回的IP结果
        result = _socket.recv(1024).decode('utf-8')
        print(result)
    except OSError as e:
        print(f"连接错误: {e}")
    finally:
        _socket.close()

while True:
    run()
    print("\n按Enter键重新连接...")
    input()

这种模式彻底避免了exec带来的各种问题,连接稳定性也会大幅提升。


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

火山引擎 最新活动