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
asynciofor 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 portset_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 connectionsasync stop(): Stop the server and close all connectionsget_clients() -> dict[int, ClientSession]: Get a snapshot of all connected clientsasync response(pkg: SimpleProtocol, client_id: int): Send a response to a specific clientasync 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 addressset_request_callback(callback: Callable[[SimpleProtocol], None]): Set callback function to handle incoming requests from serverasync connect(): Connect to the serverasync disconnect(): Disconnect from the serverasync 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 IDget_msgid() -> int: Get message IDset_cmd(cmd: int): Set command codeget_cmd() -> int: Get command codeset_direction(direction: int): Set direction (DIRECTION_REQUEST or DIRECTION_RESPONSE)get_direction() -> int: Get directionset_data(data: bytes): Set data payloadget_data() -> bytes: Get data payload (reference)get_data_copy() -> bytes: Get data payload (copy)encode() -> bytes: Encode protocol data into byte arraydecode(data: bytes) -> bool: Decode byte array into protocol data. ReturnsTrueif successful.
Constants
DIRECTION_REQUEST = 0x00: Request directionDIRECTION_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) or0x01(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 portvoid start(): Start the server and begin accepting connectionsvoid stop(): Stop the server and close all connectionsvoid set_request_callback(std::function<void(SimpleProtocol &p, size_t client_id)> callback): Set callback function to handle incoming requestsstd::unordered_map<size_t, std::shared_ptr<ClientSession>> get_clients() const: Get a snapshot of all connected clientsvoid response(SimpleProtocol &p, size_t client_id): Send a response to a specific clientstd::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 serverstd::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 responsebool is_connected() const: Check if the client is connected
SimpleProtocol
void set_msgid(uint16_t msg_id): Set message IDuint16_t get_msgid(): Get message IDvoid set_cmd(uint16_t cmd): Set command codeuint16_t get_cmd(): Get command codevoid set_direction(uint8_t direction): Set direction (DIRECTION_REQUEST or DIRECTION_RESPONSE)uint8_t get_direction(): Get directionvoid set_data(std::vector<uint8_t> data): Set data payloadvoid set_data(char *data, uint32_t length): Set data payload from C-style arraystd::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 arraystatic 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_contextvoid run_ctx_backend(): Run the io_context in a background threadvoid stop_ctx(): Stop the io_context backend threadvoid set_terminal_verbose(bool enabled): Enable/disable verbose terminal outputbool get_terminal_verbose(): Get verbose terminal output status
License
MIT License
Author
Red Elephant (redelephant@foxmail.com)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb20e66aed0d76e40b495eeedb1a4791052e450d950727dc88ed4f4add58d480
|
|
| MD5 |
b71d96929864999b764771d98e222781
|
|
| BLAKE2b-256 |
1c80c316b19c2733d76ab06d64a4feb9f882923e15b3edccfa443b7c09971d34
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b656f39cf0a4d35525ce7d4e23e30b8c04fc7df239d239e68a5f5e2df06653cf
|
|
| MD5 |
35675d36023e3a317d71e1bf8aa127fe
|
|
| BLAKE2b-256 |
5bd7d56474e6dec18c8e1e904eff172906da31605546c6d51c0b511d7dbe8077
|