Skip to main content

A user-friendly asynchronous socket wrapper with a simple communication protocol, currently available for Python and C++.

Project description

easy-asocket

A user-friendly asynchronous socket wrapper with a simple communication protocol, currently available for Python and C++.

Features

  • Asynchronous I/O: Built on Python's asyncio for high-performance network communication
  • Simple Protocol: Lightweight application-layer protocol with CRC16 checksum validation
  • Request-Response Pattern: Built-in support for request-response communication with automatic message ID management
  • Multi-Client Server: Server can handle multiple concurrent client connections
  • Cross-Language Support: Available for both Python and C++

Installation

pip install easy-asocket

Quick Start

Server Example

import asyncio
from easy_asocket import SocketServer, SimpleProtocol

def on_request(pkg: SimpleProtocol, client_id: int):
    """Callback function to handle received requests"""
    print(f'Received data from client {client_id}:')
    print(f'  Message ID: {pkg.get_msgid()}')
    print(f'  CMD: {pkg.get_cmd()}')
    print(f'  Data: {pkg.get_data()}')
    
    # Create and send response
    resp = SimpleProtocol()
    resp.set_msgid(pkg.get_msgid())  # Use same message ID
    resp.set_cmd(pkg.get_cmd())      # Use same command
    resp.set_data(b'Response data')  # Set response data
    
    # Send response asynchronously
    asyncio.create_task(server.response(resp, client_id))

async def main():
    server = SocketServer(22334)
    server.set_request_callback(on_request)
    await server.start()

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

Client Example

import asyncio
import easy_asocket as eas

def on_request(pkg: eas.SimpleProtocol):
    """Callback function to handle received requests from server"""
    print(f"Received request:")
    print(f"  Message ID: {pkg.get_msgid()}")
    print(f"  CMD: {pkg.get_cmd()}")
    print(f"  Data: {pkg.get_data()}")

async def main():
    client = eas.SocketClient("127.0.0.1", 22334)
    
    # Set callback to handle received requests
    client.set_request_callback(on_request)
    
    # Connect to server
    await client.connect()
    
    # Send a request and wait for response
    pkg = eas.SimpleProtocol()
    pkg.set_cmd(0x0001)
    pkg.set_data(b'Hello, Server!')
    
    success, response = await client.request(pkg, expect_response=True, timeout=5.0)
    if success:
        print(f"Response received: {response.get_data()}")
    
    # Keep connection alive
    await asyncio.sleep(3600)  # Keep running for 1 hour

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

API Reference

SocketServer

Server class for accepting multiple client connections.

Methods

  • __init__(port: int): Initialize server on specified port
  • set_request_callback(callback: Callable[[SimpleProtocol, int], None]): Set callback function to handle incoming requests. The callback receives the protocol package and client ID.
  • async start(): Start the server and begin accepting connections
  • async stop(): Stop the server and close all connections
  • get_clients() -> dict[int, ClientSession]: Get a snapshot of all connected clients
  • async response(pkg: SimpleProtocol, client_id: int): Send a response to a specific client
  • async request(pkg: SimpleProtocol, client_id: int, expect_response: bool = False, timeout: float = 5.0): Send a request to a specific client

SocketClient

Client class for connecting to a server.

Methods

  • __init__(host: str, port: int): Initialize client with server address
  • set_request_callback(callback: Callable[[SimpleProtocol], None]): Set callback function to handle incoming requests from server
  • async connect(): Connect to the server
  • async disconnect(): Disconnect from the server
  • async request(pkg: SimpleProtocol, expect_response: bool = False, timeout: float = 5.0) -> Tuple[bool, SimpleProtocol]: Send a request. Returns (success, response) tuple.
  • async response(pkg: SimpleProtocol): Send a response

SimpleProtocol

Protocol class for encoding and decoding messages.

Methods

  • set_msgid(msg_id: int): Set message ID
  • get_msgid() -> int: Get message ID
  • set_cmd(cmd: int): Set command code
  • get_cmd() -> int: Get command code
  • set_direction(direction: int): Set direction (DIRECTION_REQUEST or DIRECTION_RESPONSE)
  • get_direction() -> int: Get direction
  • set_data(data: bytes): Set data payload
  • get_data() -> bytes: Get data payload (reference)
  • get_data_copy() -> bytes: Get data payload (copy)
  • encode() -> bytes: Encode protocol data into byte array
  • decode(data: bytes) -> bool: Decode byte array into protocol data. Returns True if successful.

Constants

  • DIRECTION_REQUEST = 0x00: Request direction
  • DIRECTION_RESPONSE = 0x01: Response direction

Protocol Format

The SimpleProtocol uses the following structure:

[Header(2)][Direction(1)][MessageID(2)][Length(4)][CMD(2)][Data(N)][CRC16(2)][Footer(2)]
  • Header: 0xAA 0x55
  • Direction: 0x00 (request) or 0x01 (response)
  • MessageID: 16-bit unsigned integer
  • Length: 32-bit unsigned integer (data length)
  • CMD: 16-bit unsigned integer (command code)
  • Data: Variable length byte array
  • CRC16: 16-bit CRC checksum
  • Footer: 0x55 0xAA

C++ Support

The library also provides C++ headers and implementation files with the same API as Python. To get the C++ include directory path:

easy-asocket-cppdir

This will print the path to the C++ headers directory, which you can use in your CMake configuration.

C++ Server Example

#include "simple_protocol.hpp"
#include "simple_protocol_impl.hpp"
#include "easy_asocket.hpp"
#include "easy_asocket_impl.hpp"
#include <boost/asio.hpp>
#include <iostream>
#include <thread>

int main() {
    auto io_ctx = EasyASocket::get_global_io_ctx();
    EasyASocket::SocketServer server(22334, io_ctx);
    
    // Set callback to handle incoming requests
    server.set_request_callback([](EasyASocket::SimpleProtocol &pkg, size_t client_id) {
        std::cout << "Received request from client " << client_id << std::endl;
        std::cout << "  Message ID: " << pkg.get_msgid() << std::endl;
        std::cout << "  CMD: " << pkg.get_cmd() << std::endl;
        
        // Create and send response
        EasyASocket::SimpleProtocol resp;
        resp.set_msgid(pkg.get_msgid());
        resp.set_cmd(pkg.get_cmd());
        std::vector<uint8_t> resp_data{'R', 'e', 's', 'p', 'o', 'n', 's', 'e'};
        resp.set_data(resp_data);
        
        server.response(resp, client_id);
    });
    
    server.start();
    EasyASocket::run_ctx_backend();
    
    // Keep running
    std::this_thread::sleep_for(std::chrono::hours(1));
    return 0;
}

C++ Client Example

#include "simple_protocol.hpp"
#include "simple_protocol_impl.hpp"
#include "easy_asocket.hpp"
#include "easy_asocket_impl.hpp"
#include <boost/asio.hpp>
#include <iostream>
#include <thread>

int main() {
    auto io_ctx = EasyASocket::get_global_io_ctx();
    EasyASocket::SocketClient client("127.0.0.1", 22334, io_ctx);
    
    // Set callback to handle incoming requests
    client.set_request_callback([](EasyASocket::SimpleProtocol &pkg) {
        std::cout << "Received request from server" << std::endl;
        std::cout << "  Message ID: " << pkg.get_msgid() << std::endl;
        std::cout << "  CMD: " << pkg.get_cmd() << std::endl;
    });
    
    // Wait for connection
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    
    // Send a request and wait for response
    EasyASocket::SimpleProtocol pkg;
    pkg.set_cmd(0x0001);
    std::vector<uint8_t> data{'H', 'e', 'l', 'l', 'o'};
    pkg.set_data(data);
    
    auto [success, response] = client.request(pkg, true, 5000);
    if (success) {
        std::cout << "Response received" << std::endl;
    }
    
    // Keep running
    std::this_thread::sleep_for(std::chrono::hours(1));
    return 0;
}

Dependencies

The C++ implementation requires Boost.Asio library. Make sure you have Boost installed on your system before using the C++ headers.

Installing Boost

Ubuntu/Debian:

sudo apt-get install libboost-all-dev

macOS (using Homebrew):

brew install boost

Windows (using vcpkg):

vcpkg install boost-asio

CMake Configuration

When using CMake, you can find Boost and link against it:

find_package(Boost REQUIRED COMPONENTS system)
target_link_libraries(your_target PRIVATE Boost::system)

C++ API Reference

The C++ API is designed to match the Python API as closely as possible. All classes and methods are in the EasyASocket namespace.

SocketServer

  • SocketServer(uint16_t port, std::shared_ptr<asio::io_context> io_ctx = nullptr): Initialize server on specified port
  • void start(): Start the server and begin accepting connections
  • void stop(): Stop the server and close all connections
  • void set_request_callback(std::function<void(SimpleProtocol &p, size_t client_id)> callback): Set callback function to handle incoming requests
  • std::unordered_map<size_t, std::shared_ptr<ClientSession>> get_clients() const: Get a snapshot of all connected clients
  • void response(SimpleProtocol &p, size_t client_id): Send a response to a specific client
  • std::tuple<bool, SimpleProtocol> request(SimpleProtocol &p, size_t client_id, bool expect_response = false, uint32_t timeout = 5000): Send a request to a specific client

SocketClient

  • SocketClient(std::string host, uint16_t port, std::shared_ptr<asio::io_context> io_ctx = nullptr): Initialize client with server address (automatically connects)
  • void set_request_callback(std::function<void(SimpleProtocol &p)> callback): Set callback function to handle incoming requests from server
  • std::tuple<bool, SimpleProtocol> request(SimpleProtocol &p, bool expect_response = false, uint32_t timeout = 5000): Send a request. Returns (success, response) tuple.
  • void response(SimpleProtocol &p): Send a response
  • bool is_connected() const: Check if the client is connected

SimpleProtocol

  • void set_msgid(uint16_t msg_id): Set message ID
  • uint16_t get_msgid(): Get message ID
  • void set_cmd(uint16_t cmd): Set command code
  • uint16_t get_cmd(): Get command code
  • void set_direction(uint8_t direction): Set direction (DIRECTION_REQUEST or DIRECTION_RESPONSE)
  • uint8_t get_direction(): Get direction
  • void set_data(std::vector<uint8_t> data): Set data payload
  • void set_data(char *data, uint32_t length): Set data payload from C-style array
  • std::vector<uint8_t> &get_data(): Get data payload (reference)
  • std::vector<uint8_t> get_data_copy(): Get data payload (copy)
  • std::vector<uint8_t> encode(): Encode protocol data into byte array
  • static std::tuple<bool, SimpleProtocol> decode(const std::vector<uint8_t> &bytearray): Decode byte array into protocol data

Global Functions

  • std::shared_ptr<asio::io_context> get_global_io_ctx(): Get or create global io_context
  • void run_ctx_backend(): Run the io_context in a background thread
  • void stop_ctx(): Stop the io_context backend thread
  • void set_terminal_verbose(bool enabled): Enable/disable verbose terminal output
  • bool get_terminal_verbose(): Get verbose terminal output status

License

MIT License

Author

Red Elephant (redelephant@foxmail.com)

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

easy_asocket-0.1.1.tar.gz (26.3 kB view details)

Uploaded Source

Built Distribution

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

easy_asocket-0.1.1-py3-none-any.whl (26.4 kB view details)

Uploaded Python 3

File details

Details for the file easy_asocket-0.1.1.tar.gz.

File metadata

  • Download URL: easy_asocket-0.1.1.tar.gz
  • Upload date:
  • Size: 26.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for easy_asocket-0.1.1.tar.gz
Algorithm Hash digest
SHA256 cfc48186f84855fefb8e27bf8e1dae35d47303ef0bfe4d6d983d9aec7be87c56
MD5 51a4c8fc4dd7fa56cd1b80240abf172c
BLAKE2b-256 c615dedb41911e818ef5a59353af5b103736ae6912e94be63ce987e73ab4242d

See more details on using hashes here.

File details

Details for the file easy_asocket-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: easy_asocket-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 26.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for easy_asocket-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5a1cd7fc8f94be8e995f71a4b30edfd3ca088988c3c029f65ffff720ed3c9d7c
MD5 d7df1372267660edbbc78f58f06cc885
BLAKE2b-256 a6fbeea60f92fa27b663a0ed33568f22485855104335af5da3e409a4a1d5667d

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