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.2a4.tar.gz (213.8 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.2a4-cp312-cp312-macosx_10_9_universal2.whl (549.7 kB view details)

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

File details

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

File metadata

  • Download URL: bisocket-0.0.2a4.tar.gz
  • Upload date:
  • Size: 213.8 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.2a4.tar.gz
Algorithm Hash digest
SHA256 97fe2c23e3f4360748ecee92204753101186508f9bb8e0bebd5a5a4598d73c11
MD5 b30367bce3cdde9796c36335cbc6d364
BLAKE2b-256 b64ba7115602c28d6f6aa6bfdeb9acf70c91129b1c0a1c96a7c649c9cfe3d727

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for bisocket-0.0.2a4-cp312-cp312-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 c15019dd67799a932c6c3bd8bd3ff4b938e0dc128465c5adfddee231dbfbe840
MD5 5ef6c585318d8fac57c7407b183a732c
BLAKE2b-256 06f5cd4f0f303bb877046e40f16b65d76e730070260449d64dd3543b70b34f2f

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