{"id":3391,"library":"aioice","title":"aioice","description":"aioice is a Python library implementing Interactive Connectivity Establishment (ICE, RFC 5245), built on top of `asyncio`. It facilitates NAT traversal for peer-to-peer UDP data streams, crucial for applications like SIP and WebRTC. The library is actively maintained, with version 0.10.2 being the latest, and typically sees several releases per year.","status":"active","version":"0.10.2","language":"en","source_language":"en","source_url":"https://github.com/aiortc/aioice","tags":["asyncio","networking","WebRTC","ICE","NAT traversal","UDP"],"install":[{"cmd":"pip install aioice","lang":"bash","label":"Install latest version"}],"dependencies":[],"imports":[{"symbol":"Connection","correct":"from aioice import Connection"},{"symbol":"Candidate","correct":"from aioice import Candidate"}],"quickstart":{"code":"import asyncio\nimport aioice\nimport os\n\nasync def connect_using_ice():\n    # In a real application, STUN/TURN servers should be provided.\n    # For testing, you might use public STUN servers or set up your own.\n    stun_server = os.environ.get('AIOICE_STUN_SERVER', 'stun.l.google.com:19302')\n    \n    connection = aioice.Connection(ice_controlling=True, stun_server=(stun_server.split(':')[0], int(stun_server.split(':')[1])))\n    \n    # Gather local candidates\n    await connection.gather_candidates()\n    \n    # In a real application, 'send_local_info' and 'get_remote_info'\n    # would be replaced by your signaling mechanism (e.g., WebSockets).\n    # For this example, we'll simulate an exchange.\n    \n    # These would be exchanged with the remote peer via signaling\n    local_candidates_sdp = [c.to_sdp() for c in connection.local_candidates]\n    local_username = connection.local_username\n    local_password = connection.local_password\n    \n    print(f\"Local candidates: {local_candidates_sdp}\")\n    print(f\"Local username: {local_username}\")\n    print(f\"Local password: {local_password}\")\n    \n    # --- Simulate remote information reception (replace with actual signaling) ---\n    # For a full example, you'd receive this from another peer.\n    # Let's assume remote_candidates, remote_username, remote_password are obtained.\n    remote_candidates = [] # Populate with remote Candidate objects or SDP strings\n    remote_username = \"remote_user\" # Example\n    remote_password = \"remote_pass\" # Example\n    # --- End simulation ---\n\n    # Add remote candidates\n    for candidate_sdp in remote_candidates:\n        await connection.add_remote_candidate(aioice.Candidate.from_sdp(candidate_sdp))\n    await connection.add_remote_candidate(None) # Signal end-of-candidates\n\n    connection.remote_username = remote_username\n    connection.remote_password = remote_password\n    \n    print(\"Performing ICE handshake...\")\n    try:\n        await connection.connect()\n        print(f\"ICE connection established: {connection.state}\")\n\n        # Send and receive data on component 1\n        await connection.sendto(b'Hello from aioice!', 1)\n        print(\"Sent 'Hello from aioice!'\")\n        \n        # In a real app, you would continuously listen for data\n        data, component = await connection.recvfrom()\n        print(f\"Received '{data.decode()}' on component {component}\")\n\n    except Exception as e:\n        print(f\"ICE connection failed: {e}\")\n    finally:\n        await connection.close()\n        print(\"Connection closed.\")\n\n# To run this, you'd typically have two peers exchanging information via a signaling channel.\n# For a local test, one would act as controlling, the other as controlled, and manually\n# exchange the candidate, username, and password strings.\n# asyncio.run(connect_using_ice())\nprint(\"Quickstart example demonstrates aioice usage. For a full connection, actual signaling is required.\")","lang":"python","description":"This quickstart demonstrates the basic steps to establish an ICE connection: creating a `Connection` object, gathering local candidates, exchanging candidate information (and username/password) with a remote peer via a signaling channel, adding remote candidates, performing the ICE handshake, and finally sending/receiving data. Note that the signaling part is a placeholder for your specific application's communication method."},"warnings":[{"fix":"Upgrade Python to 3.9 or a later supported version.","message":"aioice versions 0.9.0 and higher require Python 3.9 or newer. Users on older Python versions must upgrade their Python environment or use an older, unsupported version of aioice.","severity":"breaking","affected_versions":">=0.9.0"},{"fix":"Replace `time.sleep()` with `await asyncio.sleep()` in all asynchronous contexts.","message":"When working with `asyncio`-based libraries like aioice, ensure you use `await asyncio.sleep(duration)` instead of `time.sleep(duration)`. Using `time.sleep` will block the entire asyncio event loop, causing your application to freeze and potentially leading to connection timeouts or failures.","severity":"gotcha","affected_versions":"All versions (general asyncio pattern)"},{"fix":"Review ICE trickle specifications (RFC 8838) and ensure your signaling mechanism and peer implementations are compatible with the expected candidate exchange patterns. Explicitly signal end-of-candidates with `connection.add_remote_candidate(None)`.","message":"While aioice supports 'half-trickle ICE' (adding candidates iteratively), a full understanding of ICE trickle mechanisms is important. Ensure your signaling protocol properly handles the exchange of candidates over time, and that both peers support the desired trickle behavior to avoid delays or connection failures, especially with older client implementations.","severity":"gotcha","affected_versions":"<0.7.0 (limited trickle support), All versions (trickle implementation details)"},{"fix":"Implement robust state management for ICE connections and channel binding. Ensure proper synchronization and unique channel identifier allocation for each peer and component to prevent conflicts.","message":"The error 'STUN transaction failed (400 - You cannot use the same channel number with different peer)' can occur due to race conditions during channel binding or incorrect handling of channel numbers, particularly when rapidly creating or re-establishing connections. This can lead to intermittent connection issues.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-11T00:00:00.000Z","next_check":"2026-07-10T00:00:00.000Z"}