Skip to main content

bisocket is a high-level Python library for simple, secure, and truly bidirectional socket communication, using a dual-socket architecture to enable non-blocking, full-duplex I/O. It provides automatic AES-GCM encryption and supports both synchronous (threading) and asynchronous (asyncio) client-server applications

Project description

bisocket: Simple, Secure, Bidirectional Python Sockets

bisocket is a high-level Python library that simplifies bidirectional (two-way) communication over sockets. It provides a robust framework for building client-server applications that require sending and receiving data simultaneously without blocking.

It comes with built-in AES-GCM end-to-end encryption and bz2 compression, ensuring your data is secure and transmitted efficiently. The library offers both synchronous (threading-based) and asynchronous (asyncio) APIs, making it versatile for various application architectures.


✨ Features

  • True Bidirectional Communication: Uses separate sockets for sending and receiving, enabling non-blocking, full-duplex communication.
  • End-to-End Encryption: Automatic AES-GCM encryption for all messages ensures data privacy and integrity.
  • Data Compression: Automatic bz2 compression reduces bandwidth usage for large payloads.
  • Sync & Async Support: Provides both a standard threading API and a modern asyncio API.
  • Simple Handler-Based API: Use a clean handler function on the server and an on_receive callback on the client to process messages.
  • Unique Client Identification: Manages clients using unique UUIDs, making it easy to track connections.

⚙️ Installation

Install bisocket directly from PyPI:

pip install bisocket

The only dependency is the cryptography library for encryption.


🚀 Quick Start

Here’s a simple echo client and server to get you started.

1. Set the Encryption Key

For security, bisocket requires an encryption key. Set it as an environment variable. If it's not set, the library will use a default, insecure key suitable only for testing.

export CRYPTO_KEY='your-super-secret-and-long-encryption-key'

2. Synchronous Example

Server (server.py)

from bisocket import Server, ServerRequest

# Define a handler to process incoming requests.
def handler(request: ServerRequest):
    print(f"Received method '{request.method}' with data: {request.data.decode()}")

    if request.method == 'echo':
        # Send the received data back to the client.
        request.send_data(request.data)
    elif request.method == 'ping':
        request.send_data(b'pong')

# Create and start the server.
if __name__ == "__main__":
    server = Server(host='127.0.0.1', port=65432, handler=handler)
    print("Starting synchronous server on port 65432...")
    server.start()

Client (client.py)

import time
from bisocket import Client, Message

# Define a callback to handle messages from the server.
def on_receive(msg: Message):
    print(f"Received response for request ID {msg.request_id}: {msg.data.decode()}")

# Use the Client as a context manager for clean connection handling.
with Client(host='127.0.0.1', port=65432, on_receive=on_receive) as client:
    print("Client connected.")
    
    # Send an 'echo' request.
    request_id_1 = client.send('echo', b'Hello, World!')
    print(f"Sent 'echo' request with ID: {request_id_1}")
    
    time.sleep(1) # Wait for the response.
    
    # Send a 'ping' request.
    request_id_2 = client.send('ping', b'')
    print(f"Sent 'ping' request with ID: {request_id_2}")
    
    time.sleep(2) # Give time for messages to be processed before exiting.

print("Client disconnected.")

3. Asynchronous Example

Async Server (async_server.py)

import asyncio
from bisocket import Server, ServerRequest

# Define an async handler for non-blocking operations.
async def ahandler(request: ServerRequest):
    print(f"Received method '{request.method}' with data: {request.data.decode()}")
    
    if request.method == 'echo':
        await asyncio.sleep(0.5) # Simulate I/O-bound work.
        request.send_data(request.data)

# Create and run the async server.
async def main():
    server = Server(host='127.0.0.1', port=65432, handler=ahandler)
    print("Starting asynchronous server on port 65432...")
    await server.astart()

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("Server shutting down.")

Async Client (async_client.py)

import asyncio
from bisocket import Client, Message

# Define an async callback to process server messages.
async def aon_receive(msg: Message):
    print(f"Received response for request ID {msg.request_id}: {msg.data.decode()}")

async def main():
    # Use the async context manager for the client.
    async with Client(host='127.0.0.1', port=65432, on_receive=aon_receive) as client:
        print("Async client connected.")
        
        # Send multiple requests concurrently.
        tasks = [
            client.asend('echo', b'First async message'),
            client.asend('echo', b'Second async message')
        ]
        request_ids = await asyncio.gather(*tasks)
        print(f"Sent requests with IDs: {request_ids}")
        
        await asyncio.sleep(2) # Keep client running to receive responses.

if __name__ == "__main__":
    asyncio.run(main())

🧠 How It Works

Traditional socket programming can be tricky when you need to send and receive data at the same time, often leading to blocking calls or complex multiplexing.

bisocket simplifies this by establishing two separate socket connections for each client:

  1. Send Socket: The client uses this connection exclusively to send data to the server.
  2. Receive Socket: The client uses this connection exclusively to receive data from the server.

This architecture allows the client and server to communicate in full-duplex mode without one operation blocking the other. The library manages these connections, message framing, encryption, and compression internally, so you can focus on your application logic.

  • On the Client: The Client runs a background thread (or asyncio task) to listen for incoming messages on the receive socket. These messages are passed to your on_receive callback.
  • On the Server: The Server manages a pool of client connections. It receives a request from a client's "send" socket, processes it in your handler, and then queues the response to be sent back via that same client's "receive" socket.

🔐 Security

All data transmitted by bisocket is encrypted using AES-256-GCM, an authenticated encryption scheme that provides confidentiality and integrity. The 256-bit encryption key is derived from the string you provide via the CRYPTO_KEY environment variable using SHA-256.

⚠️ It is crucial to set a strong, unique secret key for your application.

You can generate a cryptographically secure key using OpenSSL:

# This command generates a 32-byte (256-bit) random key in hex format.
export CRYPTO_KEY=$(openssl rand -hex 32)

If CRYPTO_KEY is not set, a default, insecure key ('secret-lol') is used, and a warning is printed. This is intended only for local testing and development.


📄 License

This project is licensed under the MIT License. See the LICENSE file for details.


🙏 Contributing

Contributions are welcome! Please feel free to submit a pull request or open an issue to discuss new features or bugs.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bisocket-0.0.2a5.tar.gz (213.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bisocket-0.0.2a5-cp312-cp312-macosx_10_9_universal2.whl (549.8 kB view details)

Uploaded CPython 3.12macOS 10.9+ universal2 (ARM64, x86-64)

File details

Details for the file bisocket-0.0.2a5.tar.gz.

File metadata

  • Download URL: bisocket-0.0.2a5.tar.gz
  • Upload date:
  • Size: 213.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.5

File hashes

Hashes for bisocket-0.0.2a5.tar.gz
Algorithm Hash digest
SHA256 60b6b07a033056ea89a89357719ce2ea396821c19236fedc1f560941e58165ab
MD5 bd84917b1511aa5751884b07d12bac25
BLAKE2b-256 df9aa3327996b96504a923660c2bba58d936947de9ef5729c68dea57bc43d4ab

See more details on using hashes here.

File details

Details for the file bisocket-0.0.2a5-cp312-cp312-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for bisocket-0.0.2a5-cp312-cp312-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 d7cccebe1c30cba334a42c7ec8b6260509d17cb26679b5460724a5b4fcbdc55f
MD5 8831ed99f17fb478ed7765ef0778b60a
BLAKE2b-256 d7e34f020f02c39f5394b9fc52ec101a2a2bbca33c7d7988f2f38f3d11388a07

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page