使用Python asyncio.loop.create_datagram_endpoint和IPv6时,“[WinError 10022]提供了无效参数”

cnh2zyt3  于 2023-03-28  发布在  Python
关注(0)|答案(1)|浏览(170)

当我执行下面的脚本时,我得到

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():
    # Get a reference to the event loop as we plan to use
    # low-level APIs.
    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())

这个例子是从这里开始的。我所做的唯一修改是:

  • 通过使用create_datagram_endpoint的sock参数使用已创建的IPv6套接字。
  • 我必须这样做,因为我必须在套接字上指定一些我在示例中省略的选项。
  • 在transport.sendto函数中提供地址(“::1”,60000)。

谁能解释一下我做错了什么,以及如何解决这个问题?
我在Windows 10 21H2上使用Python 3.11.2。
我已经尝试过IPv4(将AF_INET6改为AF_INET,将::1改为127.0.0.1)。然后这个例子就可以正常工作了。使用没有异步框架的套接字也可以工作,即使是IPv6:

sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
sock.sendto(b"11", ("::1", 60000))
gcxthw6b

gcxthw6b1#

解决方案是在sendto函数中提供流信息和作用域ID:

self.transport.sendto(self.message.encode(), ("::1", 60000, 0, 0))

此外,可能还有第二个问题:
connection_made回调函数中没有调用sendto函数时,asyncio框架接下来会调用ov.WSARecvFrom,这会导致相同的错误([WinError 10022] An invalid argument was supplied),因为套接字处于错误的状态(接收但没有提供地址)。解决方案是在调用create_datagram_endpoint之前绑定套接字:sock.bind(("", 0))
一些相关的bug供进一步阅读:

相关问题