SHSP Implementations
Complete implementation of Single HandShake Protocol (SHSP) - provides UDP sockets, handshake logic, and utilities for peer-to-peer networking.
Features
- 🚀 Complete SHSP implementation ready to use
- 📡 UDP-based networking with peer-to-peer support
- 🤝 Multiple handshake mechanisms (IP, ownership, time-based)
- 🔄 Callback-driven architecture for asynchronous communication
- 🌐 IPv4 and IPv6 support with STUN integration
- 🔒 Cryptographic utilities for secure communications
- 🛠️ Comprehensive utilities for address formatting and data handling
- ⚡ High performance UDP socket operations
Installation
Add this to your package's pubspec.yaml:
dependencies:
shsp_implementations: ^1.2.0
This will automatically include the required dependencies:
shsp_types^1.0.7shsp_interfaces^1.2.0
Quick Start
import 'package:shsp_implementations/shsp_implementations.dart';
void main() async {
// Create a new SHSP socket
final socket = ShspSocket();
// Bind to a local address
await socket.bind(InternetAddress.anyIPv4, 0);
// Set up message handling
socket.onMessage = (data, remoteInfo) {
print('Received: ${String.fromCharCodes(data)} from $remoteInfo');
};
// Send a message to a peer
final peer = RemoteInfo(InternetAddress.loopbackIPv4, 8080);
await socket.send('Hello SHSP!'.codeUnits, peer);
}
Core Components
Socket Implementation
ShspSocket: Complete UDP socket with callback managementRawShspSocket: Low-level socket operations
Protocol Management
Shsp: Core protocol implementationShspInstance: Protocol instance managementShspPeer: Peer connection management
Handshake Mechanisms
HandshakeIp: IP-based handshakesHandshakeOwnership: Ownership verificationHandshakeTime: Time-based handshakes
Utilities
CallbackMap: Multiple callback managementMessageCallbackMap: Message-specific callbacksAddressUtility: Address formattingConcatUtility: Data concatenation
Advanced Usage
import 'package:shsp_implementations/shsp_implementations.dart';
void advancedExample() async {
final shsp = Shsp();
// Configure with custom handshake
final handshake = HandshakeIp();
await shsp.configure(handshake: handshake);
// Set up peer discovery
shsp.onPeerDiscovered = (peer) {
print('New peer discovered: $peer');
};
// Start the protocol
await shsp.start();
// Use callback map for organized message handling
final callbackMap = MessageCallbackMap<String>();
callbackMap.addCallback('greeting', (data, peer) {
print('Greeting received from $peer: $data');
});
}
Creating SHSP Objects
This package provides factory methods for creating each main SHSP component:
1. Creating a ShspSocket
The socket is the foundation for all network communication. Use the bind() factory to create and bind a socket:
import 'dart:io';
import 'package:shsp_implementations/shsp_implementations.dart';
void main() async {
// Create and bind a new socket to listen on all IPv4 interfaces, port 8000
final socket = await ShspSocket.bind(InternetAddress.anyIPv4, 8000);
print('Socket listening on port 8000');
// Set up callbacks for socket events
socket.setCloseCallback(() => print('Socket closed'));
socket.setErrorCallback((err) => print('Socket error: $err'));
// Clean up when done
socket.close();
}
2. Creating a ShspPeer
A peer represents a remote connection. Use the create() factory to create a peer:
import 'dart:io';
import 'package:shsp_implementations/shsp_implementations.dart';
import 'package:shsp_types/shsp_types.dart';
void main() async {
final socket = await ShspSocket.bind(InternetAddress.anyIPv4, 8000);
// Define the remote peer's information
final remotePeer = PeerInfo(
address: InternetAddress('192.168.1.100'),
port: 9000,
);
// Create a peer for communication with that remote address
final peer = ShspPeer.create(
remotePeer: remotePeer,
socket: socket,
);
// Send a message to the peer
peer.sendMessage([1, 2, 3, 4]);
peer.close();
socket.close();
}
3. Creating a ShspInstance
ShspInstance extends ShspPeer with protocol-aware message handling (handshakes, keep-alive, etc.):
import 'dart:io';
import 'package:shsp_implementations/shsp_implementations.dart';
import 'package:shsp_types/shsp_types.dart';
void main() async {
final socket = await ShspSocket.bind(InternetAddress.anyIPv4, 8000);
final remotePeer = PeerInfo(
address: InternetAddress('192.168.1.100'),
port: 9000,
);
// Create a protocol instance with 20-second keep-alive
final instance = ShspInstance.create(
remotePeer: remotePeer,
socket: socket,
keepAliveSeconds: 20,
);
// Set up message callback
instance.setMessageCallback((msg) {
print('Received message: $msg');
});
// Start sending keep-alive messages
instance.startKeepAlive();
// Send handshake
instance.sendHandshake();
instance.stopKeepAlive();
instance.close();
socket.close();
}
4. Converting a Peer to an Instance
You can upgrade an existing ShspPeer to a ShspInstance using fromPeer():
import 'dart:io';
import 'package:shsp_implementations/shsp_implementations.dart';
import 'package:shsp_types/shsp_types.dart';
void main() async {
final socket = await ShspSocket.bind(InternetAddress.anyIPv4, 8000);
final remotePeer = PeerInfo(
address: InternetAddress('192.168.1.100'),
port: 9000,
);
// Start with a basic peer
final peer = ShspPeer.create(
remotePeer: remotePeer,
socket: socket,
);
// Later, upgrade to protocol instance with keep-alive
final instance = ShspInstance.fromPeer(
peer,
keepAliveSeconds: 25,
);
instance.startKeepAlive();
instance.close();
socket.close();
}
5. Creating a Low-Level Shsp Object
For direct socket management, use the Shsp.create() factory:
import 'dart:io';
import 'package:shsp_implementations/shsp_implementations.dart';
void main() async {
// Create a raw UDP socket first
final rawSocket = await RawDatagramSocket.bind(
InternetAddress.anyIPv4,
8000,
);
// Wrap it in a Shsp object with remote peer info and optional signal
final shsp = Shsp.create(
socket: rawSocket,
remoteIp: '192.168.1.100',
remotePort: 9000,
signal: 'CLIENT_HELLO',
);
print('SHSP signal: ${shsp.getSignal()}');
shsp.close();
}
Complete Example: Creating All Objects Together
import 'dart:io';
import 'package:shsp_implementations/shsp_implementations.dart';
import 'package:shsp_types/shsp_types.dart';
void main() async {
// Step 1: Create a socket
final socket = await ShspSocket.bind(InternetAddress.anyIPv4, 8000);
print('✓ Socket created on port 8000');
// Step 2: Create a peer
final remotePeer = PeerInfo(
address: InternetAddress('192.168.1.100'),
port: 9000,
);
final peer = ShspPeer.create(
remotePeer: remotePeer,
socket: socket,
);
print('✓ Peer created for 192.168.1.100:9000');
// Step 3: Upgrade to protocol instance
final instance = ShspInstance.fromPeer(peer, keepAliveSeconds: 20);
print('✓ Instance created with 20s keep-alive');
// Step 4: Set up and start
instance.setMessageCallback((msg) {
print('Message received: $msg');
});
instance.startKeepAlive();
print('✓ Keep-alive started');
// Step 5: Clean up
instance.stopKeepAlive();
instance.close();
socket.close();
print('✓ All objects closed');
}
Working with Singletons
The package provides singleton implementations for managing global SHSP resources. Singletons ensure you have only one instance of critical components throughout your application.
1. ShspSocketInfoSingleton
Manages socket configuration (address and port) globally. Loads configuration from shsp_socket_config.json file or uses defaults (127.0.0.1:6969).
import 'package:shsp_implementations/shsp_implementations.dart';
void main() {
// Get singleton instance (loads from shsp_socket_config.json if exists)
final info = ShspSocketInfoSingleton();
print('Address: ${info.address}, Port: ${info.port}');
// Subsequent calls return the same instance
final sameInfo = ShspSocketInfoSingleton();
assert(identical(info, sameInfo));
// Clean up when needed
ShspSocketInfoSingleton.destroy();
}
Configuration File (shsp_socket_config.json):
The singleton automatically looks for a file named shsp_socket_config.json in the current directory. If the file exists, it loads the configuration from there. If not, it uses default values.
{
"address": "192.168.1.100",
"port": 8080
}
Default values (if file not found or fields missing):
- Address:
127.0.0.1 - Port:
6969
2. ShspSocketSingleton
Global socket instance with thread-safe initialization. Prevents multiple socket bindings.
import 'package:shsp_implementations/shsp_implementations.dart';
void main() async {
// Create singleton socket (thread-safe)
final socket = await ShspSocketSingleton.bind();
// Subsequent calls return the same instance
final socket2 = await ShspSocketSingleton.bind();
assert(identical(socket, socket2));
// Access existing instance without async
final existing = ShspSocketSingleton.instance;
// Set up message handling
socket.onMessage = (data, remoteInfo) {
print('Received: $data from $remoteInfo');
};
// Clean up
ShspSocketSingleton.destroy();
}
3. MessageCallbackMapSingleton
Global callback registry for message handling across your application.
import 'package:shsp_implementations/shsp_implementations.dart';
void main() {
// Get singleton instance
final callbacks = MessageCallbackMapSingleton();
// Register callbacks for different message types
callbacks.addCallback((data, remoteInfo) {
print('Handler 1: $data');
});
callbacks.addCallback((data, remoteInfo) {
print('Handler 2: $data');
});
// All parts of your app use the same callback map
final sameCallbacks = MessageCallbackMapSingleton();
assert(identical(callbacks, sameCallbacks));
// Clean up
MessageCallbackMapSingleton.destroy();
}
4. ShspInstanceHandlerSingleton
Global handler for managing SHSP protocol instances.
import 'package:shsp_implementations/shsp_implementations.dart';
void main() {
// Get singleton handler
final handler = ShspInstanceHandlerSingleton();
// Use it to manage instances across your application
// (manages peer connections and protocol instances)
// Subsequent calls return the same instance
final sameHandler = ShspInstanceHandlerSingleton();
assert(identical(handler, sameHandler));
// Clean up
ShspInstanceHandlerSingleton.destroy();
}
Complete Singleton Usage Example
import 'package:shsp_implementations/shsp_implementations.dart';
void main() async {
// NOTE: All singleton constructors have NO parameters
// Configuration is loaded from shsp_socket_config.json if it exists
// Step 1: Get socket info singleton (no parameters)
final info = ShspSocketInfoSingleton();
print('✓ Socket info: ${info.address}:${info.port}');
// Step 2: Set up global callbacks (no parameters)
final callbacks = MessageCallbackMapSingleton();
callbacks.addCallback((data, remoteInfo) {
print('Global handler received: $data');
});
print('✓ Global callbacks registered');
// Step 3: Create singleton socket (no parameters)
final socket = await ShspSocketSingleton.bind();
print('✓ Singleton socket created');
// Step 4: Get instance handler (no parameters)
final handler = ShspInstanceHandlerSingleton();
print('✓ Instance handler ready');
// Now all parts of your application share these instances
// Calling the constructors again returns the same instances
// Clean up all singletons when done
ShspSocketSingleton.destroy();
ShspSocketInfoSingleton.destroy();
MessageCallbackMapSingleton.destroy();
ShspInstanceHandlerSingleton.destroy();
print('✓ All singletons cleaned up');
}
When to Use Singletons
Use singletons when you need:
- Global socket configuration across multiple modules
- Single socket binding to prevent port conflicts
- Shared callback registry for centralized message handling
- Unified instance management across your application
Important Notes
-
Empty constructors: All singleton constructors have no parameters - this ensures proper singleton behavior
ShspSocketInfoSingleton()- no parameters, loads fromshsp_socket_config.jsonor uses defaultsMessageCallbackMapSingleton()- no parametersShspInstanceHandlerSingleton()- no parametersShspSocketSingleton.bind()- no parameters, automatically uses the other singletons
-
Configuration: To customize socket address/port, create a
shsp_socket_config.jsonfile before calling any singleton -
Thread-safe: All singletons are thread-safe, especially
ShspSocketSingleton.bind()which uses a Completer to prevent race conditions -
Testing: Call
.destroy()on each singleton to reset state during testing or reconfiguration -
Use case: Singletons are ideal for applications with a single SHSP connection. For multiple connections or dynamic configurations, use the non-singleton classes instead
Related Packages
shsp_types- Type definitionsshsp_interfaces- Abstract interfaces
Performance
The implementation is optimized for:
- Low-latency UDP communication
- Efficient memory usage
- Minimal allocations in hot paths
- Asynchronous operations throughout
Documentation
For complete API documentation, visit pub.dev/packages/shsp_implementations.
Contributing
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our GitHub repository.
License
This project is licensed under the LGPL-3.0 License.
Libraries
- connection/connection
- connection/handshake_ip
- connection/handshake_ownership
- connection/handshake_time
- factory/factory_inputs
- factory/shsp_factories
- shsp_base/compression/compression_codecs
- Compression codecs for SHSP protocol
- shsp_base/compression/gzip_codec
- shsp_base/compression/lz4_codec
- shsp_base/compression/zstd_codec
- shsp_base/shsp_peer
- shsp_base/shsp_socket
- shsp_base/shsp_socket_singleton
- shsp_implementations
- shsp_instance/shsp_handshake_handler
- shsp_instance/shsp_instance
- shsp_instance/shsp_instance_handler
- shsp_instance/shsp_instance_handler_singleton
- single_hand_shake_protocol_monorepo
- utility/address_utility
- utility/callback_map
- utility/concat_utility
- utility/keep_alive_timer
- utility/message_callback_map
- utility/message_callback_map_singleton
- utility/raw_shsp_socket
- utility/shsp_socket_info_singleton
- utility/utility_factories