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

使用Python asyncio创建IPv6数据报端点时遇WinError 10022错误

问题:异步IPv6 UDP套接字发送报错[WinError 10022]

执行修改后的Python异步UDP客户端脚本时,出现如下错误:

Send: Hello World!
Error received: [WinError 10022] An invalid argument was supplied
Error received: [WinError 10022] An invalid argument was supplied
Connection closed

脚本代码:

import asyncio
import socket

class EchoClientProtocol:
    def __init__(self, message, on_con_lost):
        self.message = message
        self.on_con_lost = on_con_lost
        self.transport = None

    def connection_made(self, transport):
        self.transport = transport
        print('Send:', self.message)
        self.transport.sendto(self.message.encode(), ("::1", 60000))

    def datagram_received(self, data, addr):
        print("Received:", data.decode())

        print("Close the socket")
        self.transport.close()

    def error_received(self, exc):
        print('Error received:', exc)

    def connection_lost(self, exc):
        print("Connection closed")
        self.on_con_lost.set_result(True)


async def main():
    loop = asyncio.get_running_loop()

    on_con_lost = loop.create_future()
    message = "Hello World!"

    sock = socket.socket(family=socket.AF_INET6, type=socket.SOCK_DGRAM)
    transport, protocol = await loop.create_datagram_endpoint(
        lambda: EchoClientProtocol(message, on_con_lost),
        sock = sock)

    try:
        await on_con_lost
    finally:
        transport.close()

asyncio.run(main())

修改说明与环境

对官方示例做了两处修改:

  • 预先创建IPv6套接字,通过create_datagram_endpoint的sock参数传入(需设置特定套接字选项);
  • 在transport.sendto方法中指定目标地址("::1", 60000)。

环境:Python 3.11.2 + Windows 10 21H2。IPv4版本可正常运行;非异步框架下的IPv6套接字sendto操作也能正常执行。


问题原因与修复方案

原因

Windows平台下,当预先创建的IPv6套接字未绑定任何地址/端口时,asyncio的UDP传输层调用sendto会触发[WinError 10022]参数错误。而IPv4套接字在未绑定状态下,Windows会自动隐式绑定到合适的地址,因此不会报错。

修复方案

方案一:预先绑定IPv6套接字

在创建套接字后,显式绑定到任意IPv6接口(::)和随机可用端口,这样asyncio的传输层就能正常处理sendto请求:

修改main函数中创建套接字的部分:

sock = socket.socket(family=socket.AF_INET6, type=socket.SOCK_DGRAM)
# 新增:绑定到所有IPv6接口,端口自动分配
sock.bind(("::", 0))

方案二:通过remote_addr指定目标地址

直接在create_datagram_endpoint中传入remote_addr参数指定目标,无需在connection_made中调用sendto时传地址。这种方式下,asyncio会自动处理套接字的绑定逻辑:

修改后的main函数和connection_made方法:

# 修改后的main函数
async def main():
    loop = asyncio.get_running_loop()

    on_con_lost = loop.create_future()
    message = "Hello World!"

    sock = socket.socket(family=socket.AF_INET6, type=socket.SOCK_DGRAM)
    transport, protocol = await loop.create_datagram_endpoint(
        lambda: EchoClientProtocol(message, on_con_lost),
        sock = sock,
        remote_addr=("::1", 60000))  # 新增remote_addr参数

    try:
        await on_con_lost
    finally:
        transport.close()

# 修改后的connection_made方法
def connection_made(self, transport):
    self.transport = transport
    print('Send:', self.message)
    # 无需再传地址,直接发送
    self.transport.sendto(self.message.encode())

完整修复后的示例代码(方案一)

import asyncio
import socket

class EchoClientProtocol:
    def __init__(self, message, on_con_lost):
        self.message = message
        self.on_con_lost = on_con_lost
        self.transport = None

    def connection_made(self, transport):
        self.transport = transport
        print('Send:', self.message)
        self.transport.sendto(self.message.encode(), ("::1", 60000))

    def datagram_received(self, data, addr):
        print("Received:", data.decode())

        print("Close the socket")
        self.transport.close()

    def error_received(self, exc):
        print('Error received:', exc)

    def connection_lost(self, exc):
        print("Connection closed")
        self.on_con_lost.set_result(True)


async def main():
    loop = asyncio.get_running_loop()

    on_con_lost = loop.create_future()
    message = "Hello World!"

    sock = socket.socket(family=socket.AF_INET6, type=socket.SOCK_DGRAM)
    # 显式绑定IPv6套接字
    sock.bind(("::", 0))
    transport, protocol = await loop.create_datagram_endpoint(
        lambda: EchoClientProtocol(message, on_con_lost),
        sock = sock)

    try:
        await on_con_lost
    finally:
        transport.close()

asyncio.run(main())

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 14:47:27