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++.

Version

Current Version: 0.1.2

Changelog

[0.1.2] - Current

  • Add support for Boost 1.87 and later.

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.2.tar.gz (26.4 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.2-py3-none-any.whl (26.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: easy_asocket-0.1.2.tar.gz
  • Upload date:
  • Size: 26.4 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.2.tar.gz
Algorithm Hash digest
SHA256 fb20e66aed0d76e40b495eeedb1a4791052e450d950727dc88ed4f4add58d480
MD5 b71d96929864999b764771d98e222781
BLAKE2b-256 1c80c316b19c2733d76ab06d64a4feb9f882923e15b3edccfa443b7c09971d34

See more details on using hashes here.

File details

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

File metadata

  • Download URL: easy_asocket-0.1.2-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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 b656f39cf0a4d35525ce7d4e23e30b8c04fc7df239d239e68a5f5e2df06653cf
MD5 35675d36023e3a317d71e1bf8aa127fe
BLAKE2b-256 5bd7d56474e6dec18c8e1e904eff172906da31605546c6d51c0b511d7dbe8077

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