aioice

raw JSON →
0.10.2 verified Sat Apr 25 auth: no python

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.

pip install aioice
error ModuleNotFoundError: No module named 'aioice'
cause The 'aioice' library is not installed in the Python environment.
fix
Install the 'aioice' library using pip: 'pip install aioice'.
error AttributeError: module 'aioice' has no attribute 'Connection'
cause The 'Connection' class is not found in the 'aioice' module, possibly due to an incorrect import or a version mismatch.
fix
Ensure you are using the correct import statement: 'from aioice import Connection'.
error ImportError: cannot import name 'Connection' from 'aioice'
cause The 'Connection' class cannot be imported from 'aioice', possibly due to an incorrect import statement or a version mismatch.
fix
Use the correct import statement: 'from aioice import Connection'.
error TypeError: __init__() got an unexpected keyword argument 'ice_controlling'
cause The 'ice_controlling' argument is not recognized in the 'Connection' class constructor, possibly due to a version mismatch.
fix
Check the 'aioice' documentation for the correct usage of the 'Connection' class and its parameters.
error RuntimeError: Event loop is closed
cause An attempt was made to run an asyncio event loop that has already been closed.
fix
Ensure that the event loop is open and running before executing asyncio tasks.
breaking 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.
fix Upgrade Python to 3.9 or a later supported version.
gotcha 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.
fix Replace `time.sleep()` with `await asyncio.sleep()` in all asynchronous contexts.
gotcha 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.
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)`.
gotcha 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.
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.
runtime status import time mem disk
3.10-alpine 0.31s 8.6MB 20.6M
3.10-slim 0.20s 8.5MB 21M
3.11-alpine 0.49s 9.8MB 23.1M
3.11-slim 0.36s 9.9MB 24M
3.12-alpine 0.66s 9.9MB 14.9M
3.12-slim 0.59s 9.9MB 15M
3.13-alpine 0.56s 9.7MB 14.5M
3.13-slim 0.56s 9.7MB 15M
3.9-alpine 0.26s 7.7MB 20.0M
3.9-slim 0.21s 7.7MB 20M

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.

import asyncio
import aioice
import os

async def connect_using_ice():
    # In a real application, STUN/TURN servers should be provided.
    # For testing, you might use public STUN servers or set up your own.
    stun_server = os.environ.get('AIOICE_STUN_SERVER', 'stun.l.google.com:19302')
    
    connection = aioice.Connection(ice_controlling=True, stun_server=(stun_server.split(':')[0], int(stun_server.split(':')[1])))
    
    # Gather local candidates
    await connection.gather_candidates()
    
    # In a real application, 'send_local_info' and 'get_remote_info'
    # would be replaced by your signaling mechanism (e.g., WebSockets).
    # For this example, we'll simulate an exchange.
    
    # These would be exchanged with the remote peer via signaling
    local_candidates_sdp = [c.to_sdp() for c in connection.local_candidates]
    local_username = connection.local_username
    local_password = connection.local_password
    
    print(f"Local candidates: {local_candidates_sdp}")
    print(f"Local username: {local_username}")
    print(f"Local password: {local_password}")
    
    # --- Simulate remote information reception (replace with actual signaling) ---
    # For a full example, you'd receive this from another peer.
    # Let's assume remote_candidates, remote_username, remote_password are obtained.
    remote_candidates = [] # Populate with remote Candidate objects or SDP strings
    remote_username = "remote_user" # Example
    remote_password = "remote_pass" # Example
    # --- End simulation ---

    # Add remote candidates
    for candidate_sdp in remote_candidates:
        await connection.add_remote_candidate(aioice.Candidate.from_sdp(candidate_sdp))
    await connection.add_remote_candidate(None) # Signal end-of-candidates

    connection.remote_username = remote_username
    connection.remote_password = remote_password
    
    print("Performing ICE handshake...")
    try:
        await connection.connect()
        print(f"ICE connection established: {connection.state}")

        # Send and receive data on component 1
        await connection.sendto(b'Hello from aioice!', 1)
        print("Sent 'Hello from aioice!'")
        
        # In a real app, you would continuously listen for data
        data, component = await connection.recvfrom()
        print(f"Received '{data.decode()}' on component {component}")

    except Exception as e:
        print(f"ICE connection failed: {e}")
    finally:
        await connection.close()
        print("Connection closed.")

# To run this, you'd typically have two peers exchanging information via a signaling channel.
# For a local test, one would act as controlling, the other as controlled, and manually
# exchange the candidate, username, and password strings.
# asyncio.run(connect_using_ice())
print("Quickstart example demonstrates aioice usage. For a full connection, actual signaling is required.")