Hacktoberfest 2026:維護者為十月標記出來的 issue,仍然開放、適合新手。 瀏覽 Hacktoberfest issue

Missing asyncio feature regarding support of SCTP sockets

未關閉
#467 0 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

還沒有人認領這個 Issue。

評估

難度
4/5
預估耗時
3-5 天
新手友好度
35/100
Issue 類型
功能
描述清晰度
基本清楚
活躍度
停滯
技術堆疊
python
領域
networking

研究方向

在安裝了 libsctp 的 Linux 上執行提供的 SCTP 用戶端和伺服器重現,並比較原生 asyncio 與 uvloop。接著檢查 uvloop/handles/tcp.pyx 以及 uvloop/loop.pyx 中的 create_connection 路徑。完成的標準是 SCTP 用戶端和伺服器連線能夠在 uvloop 下運作,且不會出現回報的協定錯誤或連線中斷。

由索引模型根據 Issue 內容生成。

描述

  • uvloop version: v0.16.0

  • Python version: 3.10.4

  • Platform: Linux
    Please note that SCTP works only if you have libsctp(-dev) installed on your Linux system. MacOS doesn't support SCTP without further effort and Windows I cannot say, since I don't use WIndows.

  • Can you reproduce the bug with PYTHONASYNCIODEBUG in env?: Please clarify if you expect further output from uvloop in this case. The error is an OS error as a result of uvloop OS socket configuration, so there is no additional output I can see other than the output below. I will edit my post accordingly if I am missing information or should try something else. Thanks!

  • Does uvloop behave differently from vanilla asyncio? How?: Yes, in vanilla asyncio, SCTP sockets can be used out of the box, behaving in the same way TCP stream sockets do.

I am using SCTP as the transport stream protocol, which works fine with Python vanilla asyncio. Asyncio shows the same behaviour as for TCP sockets, which is expected since both transport protocols are stream protocols. However, uvloop seems to only allow TCP sockets.

Server side SCTP socket issue observation
When using an SCTP listening server socket, starting the server works fine. As soon as a SCTP client tries to connect, no connection notification or client data ever reaches the Python application code, but also no errors or other messages are observed on application layer. Wireshark shows that the connection from the client is dropped by the server side right after the SCTP cookie ack handshake.

Client side SCTP socket issue observation
Using an SCTP socket for a client raises an OS Error. From the stack trace it looks like uvloop is trying to use TCP under the hood instead of the SCTP socket.

Traceback (most recent call last):
  File "/home/lukas/uvloop-bug-report/client.py", line 69, in <module>
    asyncio.run(start_client(use_sctp=True))
  File "/usr/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "uvloop/loop.pyx", line 1501, in uvloop.loop.Loop.run_until_complete
  File "/home/lukas/uvloop-bug-report/client.py", line 55, in start_client
    transport, _ = await asyncio.get_event_loop().create_connection(
  File "uvloop/loop.pyx", line 2057, in create_connection
  File "uvloop/loop.pyx", line 2045, in uvloop.loop.Loop.create_connection
  File "uvloop/handles/tcp.pyx", line 177, in uvloop.loop.TCPTransport._open
  File "uvloop/handles/tcp.pyx", line 34, in uvloop.loop.__tcp_open
OSError: [Errno 92] Protocol not available

To reproduce the issues, the following client/server code can be used on a Linux machine or wrapped in a Linux based Docker container if you don't have a Linux OS (I am happy to help with this if needed).

Server code

"""
The server side of the asyncio/uvloop SCTP socket demo.

Author: Lukas Stermann <lukas@ng-voice.com>
"""
import asyncio
import socket
from typing import Optional
import uvloop


class ServerProtocol(asyncio.Protocol):
    """
    This is just a dummy server echo protocol for demo purposes.
    """
    def __init__(self):
        self.transport: Optional[asyncio.Transport] = None

    def connection_made(self, transport: asyncio.Transport) -> None:
        self.transport = transport
        print('New connection from client')

    def data_received(self, raw_data: bytes) -> None:
        print(f'Data received from client: <{raw_data.decode()}>')
        self.transport.write(b'pong')

    def connection_lost(self, exc: Optional[Exception]) -> None:
        print('Connection lost')
        self.transport.close()


async def start_server(use_sctp: bool = True) -> None:
    """
    This server starts a listening TCP/SCTP socket.

    :param use_sctp: Whether the server shall use SCTP.
                     If set to False it uses TCP.
    """
    socket_: socket.socket

    if use_sctp:
        socket_ = socket.socket(socket.AF_INET6,
                                socket.SOCK_STREAM,
                                socket.IPPROTO_SCTP)
    else:
        socket_ = socket.socket(socket.AF_INET6,
                                socket.SOCK_STREAM)

    socket_.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    if socket.has_dualstack_ipv6():
        socket_.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
    else:
        print('This OS does not support IPv4/IPv6 dual stack. '
              'Falling back to IPv6 only.')

    socket_.setblocking(False)
    socket_.bind(('', 10002))
    socket_.listen()

    connection_settings: dict = {
        'protocol_factory': lambda: ServerProtocol(),
        'sock': socket_}

    server = await asyncio.get_event_loop().create_server(**connection_settings)
    await server.serve_forever()


# Comment in the following line to see the issue with using an SCTP socket in uvloop.
# None of the print statements in the protocol are executed and the socket is closed
# right after the SCTP cookie handshake (use Wireshark to see this behaviour or see attached pcap).
uvloop.install()

# Switch between TCP and SCTP to see that for uvloop TCP works well, while SCTP doesn't
asyncio.run(start_server(use_sctp=True))

Client code

"""
The client side of the asyncio/uvloop SCTP socket demo.

Author: Lukas Stermann <lukas@ng-voice.com>
"""
import asyncio
import socket
from typing import Optional
import uvloop


class ClientProtocol(asyncio.Protocol):
    """
    This is just a dummy client echo protocol for demo purposes.
    """
    def __init__(self):
        self.transport: Optional[asyncio.Transport] = None

    def connection_made(self, transport: asyncio.Transport) -> None:
        self.transport = transport
        print('Connection established to server')
        self.transport.write(b'ping')

    def data_received(self, raw_data: bytes) -> None:
        print(f'Answer received from server: <{raw_data.decode()}>')

    def connection_lost(self, exc: Optional[Exception]) -> None:
        print('Connection lost')
        self.transport.close()


async def start_client(use_sctp: bool = True) -> None:
    """
    This client sends a message over TCP/SCTP socket.

    :param use_sctp: Whether the server shall use SCTP.
                     If set to False it uses TCP.
    """
    connection_parameters: dict = {
        'protocol_factory': lambda: ClientProtocol()
    }

    if use_sctp:
        socket_ = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
                                socket.IPPROTO_SCTP)
    else:
        socket_ = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    socket_.setblocking(False)
    connection_parameters['sock'] = socket_

    await asyncio.get_event_loop().sock_connect(
                socket_, ('127.0.0.1', 10002))

    transport, _ = await asyncio.get_event_loop().create_connection(
        **connection_parameters)

    await asyncio.sleep(2)
    transport.close()

# Comment in the following line to see the issue with using an SCTP socket in uvloop.
# The client will crash with OSError saying the protocol is not supported
uvloop.install()

# Switch between TCP and SCTP to see that for uvloop TCP works well, while SCTP doesn't
asyncio.run(start_client(use_sctp=True))

Please also find attached a zipped PCAP for Wireshark. The first part of the trace there is the vanilla asyncio version with the uvloop.install() command commented out in both client and server code. The second trace shows the behavior when the server is using uvloop but the client is using vanilla asyncio.

Would be great if you would comment on this, cheers for your work, really appreciate that you are bringing performance to Python :)

uvloop_sctp_issue.zip

主要語言
Cython
星號
11.9k
分支
615
PR 合併指標
30 天內沒有已合併 PR

貢獻指南

這個儲存庫沒有索引到貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

MagicStack/uvloop 的其他 Issue

查看 MagicStack/uvloop 的全部 Issue

相似的 Issue

更多 Networking Issue

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。