High-Performance Socket Library for Java 25
FastSocket is a lightweight networking library built on Java 25's Panama FFM API. It provides a simple, fluent API for building scalable client-server applications with built-in support for unicast, multicast, and broadcast messaging.
- Installation
- Quick Start
- Architecture
- Server API
- Connection API
- Messaging Patterns
- Groups API
- Client API
- SSL/TLS Encryption
- Binary Protocol
- Buffer Pool
- Complete Examples
- API Reference
- Java 25 or later with preview features enabled
- Maven 3.8+
<dependency>
<groupId>io.github.sk8erboi17</groupId>
<artifactId>fast-socket-api</artifactId>
<version>2.0.0</version>
</dependency>java --enable-preview -jar your-application.jarimport io.github.sk8erboi17.api.FastSocket;
public class EchoServer {
public static void main(String[] args) {
FastSocket.create()
.onMessage((conn, msg) -> conn.send("Echo: " + msg))
.start(8080);
}
}import io.github.sk8erboi17.api.FastSocketClient;
public class EchoClient {
public static void main(String[] args) throws Exception {
var client = FastSocketClient.create()
.onMessage(msg -> System.out.println("Received: " + msg))
.connect("localhost", 8080);
client.send("Hello!");
Thread.sleep(1000);
client.close();
}
}FastSocket uses a multi-reactor pattern for high concurrency:
┌─────────────────┐
│ AcceptorLoop │ ← Single thread accepts connections
│ (ServerSocket) │
└────────┬────────┘
│ round-robin distribution
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ EventLoop-0 │ │ EventLoop-1 │ │ EventLoop-N │ ← Worker threads
│ (Selector) │ │ (Selector) │ │ (Selector) │ handle I/O
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────┐
│ NativeBufferPool (Panama FFM) │ ← Off-heap memory
│ Thread-local caches + lock-free queues │
└──────────────────────────────────────────────────┘
Key components:
| Component | Description |
|---|---|
| AcceptorLoop | Single thread that accepts incoming connections and distributes them to workers |
| EventLoop | Worker thread with its own Selector for non-blocking I/O |
| NativeBufferPool | Off-heap memory pool using Panama FFM's MemorySegment |
| ConnectionRegistry | Tracks all connections for unicast/multicast/broadcast |
| ConnectionGroup | Named group of connections for multicast messaging |
There are two ways to create a server:
FastSocket server = FastSocket.create()
.onMessage((conn, msg) -> conn.send(msg))
.start(8080);FastSocket server = FastSocket.create(config -> config
.workerThreads(8)
.bufferPoolSize(256)
.maxFrameSize(1024 * 1024)
.tcpNoDelay(true)
.keepAlive(true)
)
.onMessage((conn, msg) -> conn.send(msg))
.start(8080);// Listen on all interfaces
server.start(8080);
// Listen on specific interface
server.start("192.168.1.100", 8080);
// Using InetSocketAddress
server.start(new InetSocketAddress("localhost", 8080));FastSocket provides four event handlers that you can register:
Called when a new client connects. Use this to initialize connection state.
server.onConnect(conn -> {
System.out.println("New connection: " + conn.id());
System.out.println("Remote address: " + conn.remoteAddress());
// Initialize connection attributes
conn.attr("connectedAt", System.currentTimeMillis());
conn.attr("messageCount", 0);
// Add to default group
server.group("lobby").add(conn);
// Send welcome message
conn.send("Welcome! Your ID is " + conn.id());
});Called when a message is received from a client. The message is automatically deserialized based on its type marker.
server.onMessage((conn, msg) -> {
// msg can be: String, Integer, Long, Float, Double, Boolean, or byte[]
if (msg instanceof String text) {
System.out.println("String message: " + text);
conn.send("Got your text!");
} else if (msg instanceof Integer number) {
System.out.println("Integer message: " + number);
conn.send(number * 2);
} else if (msg instanceof byte[] data) {
System.out.println("Binary data: " + data.length + " bytes");
conn.sendBytes(data); // Echo back
}
});You can register handlers for specific types:
// Handle only String messages
server.onMessage(String.class, (conn, text) -> {
System.out.println("Text: " + text);
});
// Handle only binary messages
server.onBinaryMessage((conn, bytes) -> {
System.out.println("Binary: " + bytes.length + " bytes");
});Called when a client disconnects (either gracefully or due to error).
server.onDisconnect(conn -> {
System.out.println("Disconnected: " + conn.id());
// Connection is automatically removed from all groups
// and unregistered from the registry
// Clean up any external resources
String username = conn.attr("username");
if (username != null) {
userDatabase.setOffline(username);
}
});Called when an error occurs during message processing.
server.onError((conn, error) -> {
System.err.println("Error on connection " + conn.id() + ": " + error.getMessage());
error.printStackTrace();
// Optionally close the connection
// conn.close();
});| Parameter | Type | Default | Description |
|---|---|---|---|
workerThreads |
int | Available CPUs | Number of EventLoop worker threads |
bufferPoolSize |
int | 128 | Number of buffers per size tier |
maxFrameSize |
int | 65536 (64KB) | Maximum allowed frame size in bytes |
tcpNoDelay |
boolean | true | Disable Nagle's algorithm for lower latency |
keepAlive |
boolean | true | Enable TCP keep-alive probes |
FastSocket.create(config -> config
.workerThreads(16) // More workers for high concurrency
.bufferPoolSize(512) // More buffers for many connections
.maxFrameSize(10 * 1024 * 1024) // 10MB max frame
.tcpNoDelay(true) // Low latency
.keepAlive(true) // Detect dead connections
);FastSocket server = FastSocket.create()
.onMessage((conn, msg) -> conn.send(msg))
.start(8080);
// Check if running
if (server.isRunning()) {
System.out.println("Server is running");
}
// Get statistics
System.out.println("Connected clients: " + server.connectionCount());
System.out.println("Buffer stats: " + server.bufferStats());
// Block until shutdown
server.awaitTermination();
// Or stop explicitly
server.stop();
// Can also use try-with-resources
try (FastSocket s = FastSocket.create().onMessage((c, m) -> {}).start(8080)) {
// Server runs here
} // Automatically stoppedThe Connection object represents a client connection and provides methods for sending data and storing state.
Automatically detects the type and sends with appropriate encoding:
conn.send("Hello"); // Sends as String
conn.send(42); // Sends as Integer
conn.send(123456789L); // Sends as Long
conn.send(3.14f); // Sends as Float
conn.send(Math.PI); // Sends as Double
conn.send(true); // Sends as Boolean
conn.send(new byte[]{1,2,3}); // Sends as byte[]Sends raw binary data:
byte[] imageData = Files.readAllBytes(Path.of("image.png"));
conn.sendBytes(imageData);Sends a keep-alive ping:
conn.sendHeartbeat();All send methods return the Connection for chaining:
conn.send("Hello")
.send("World")
.send(42)
.sendHeartbeat();Store per-connection state without using external maps. There are two ways to use attributes:
Use integer indices 0-7. This is O(1) with no hashing overhead:
// Define constants for clarity
static final int ATTR_USER_ID = 0;
static final int ATTR_SESSION = 1;
static final int ATTR_ROLE = 2;
// Set attributes
conn.attr(ATTR_USER_ID, "user-123");
conn.attr(ATTR_SESSION, sessionToken);
conn.attr(ATTR_ROLE, "admin");
// Get attributes (type is inferred)
String userId = conn.attr(ATTR_USER_ID);
String session = conn.attr(ATTR_SESSION);
String role = conn.attr(ATTR_ROLE);Use string keys for readability. Slightly slower due to linear scan:
// Set attributes
conn.attr("userId", "user-123");
conn.attr("username", "john_doe");
conn.attr("role", "admin");
// Get attributes
String userId = conn.attr("userId");
String username = conn.attr("username");
// Check if attribute exists
if (conn.hasAttr("role")) {
String role = conn.attr("role");
}Note: String-keyed attributes use pairs of slots internally (key, value), so you can store up to 4 string-keyed attributes.
// Get connection ID (unique across server lifetime)
long id = conn.id();
// Get remote address
InetSocketAddress remote = conn.remoteAddress();
String ip = remote.getAddress().getHostAddress();
int port = remote.getPort();
// Check if connection is still open
if (conn.isOpen()) {
conn.send("Still connected!");
}
// Close the connection
conn.close();FastSocket supports three messaging patterns out of the box.
Send a message to a specific connection by its ID.
// From server to specific connection
server.send(connectionId, "Private message");
server.sendBytes(connectionId, binaryData);
// Check if send was successful (connection exists and is open)
boolean sent = server.send(connectionId, "Hello");
if (!sent) {
System.out.println("Connection not found or closed");
}
// Get connection by ID and send directly
Connection conn = server.connection(connectionId);
if (conn != null && conn.isOpen()) {
conn.send("Direct message");
}- Private messages between users
- Targeted notifications
- Request-response patterns
- Game state updates for specific player
Send a message to a named group of connections.
// Create/get a group and add connections
server.group("lobby").add(conn);
// Send to all group members
server.group("lobby").broadcast("Welcome to the lobby!");
// Or use the shorthand
server.multicast("lobby", "Hello everyone!");
// Send to group except one connection
server.group("lobby").broadcastExcept("User typing...", senderConn);
server.multicastExcept("lobby", "User joined", joiningConn);
// Conditional multicast
server.group("admins").broadcastIf(
"Admin alert!",
c -> c.attr("level") != null && (int) c.attr("level") >= 5
);- Chat rooms
- Game lobbies
- Topic subscriptions (pub/sub)
- Team notifications
Send a message to ALL connected clients.
// Broadcast to everyone
server.broadcast("Server maintenance in 5 minutes");
// Broadcast binary data
server.broadcastBytes(systemUpdatePayload);
// Broadcast except one connection
server.broadcastExcept("New user joined!", joiningConnection);
server.broadcastExcept("User left", leavingConnectionId);
// Conditional broadcast
server.broadcastIf(
"EU server update",
conn -> "EU".equals(conn.attr("region"))
);- System announcements
- Server status updates
- Global events
- Heartbeat/ping all clients
Groups are named collections of connections for multicast messaging.
// Get or create a group (lazy creation)
ConnectionGroup lobby = server.group("lobby");
// Add connection to group
lobby.add(conn);
// Remove connection from group
lobby.remove(conn);
// Check membership
if (lobby.contains(conn)) {
System.out.println("Connection is in lobby");
}
// Get group size
int memberCount = lobby.size();
// Check if group is empty
if (lobby.isEmpty()) {
System.out.println("Lobby is empty");
}
// Get all members
Set<Connection> members = lobby.members();
for (Connection member : members) {
System.out.println("Member: " + member.id());
}A connection can belong to multiple groups:
server.onConnect(conn -> {
// Add to multiple groups
server.group("all-users").add(conn);
server.group("lobby").add(conn);
});
server.onMessage((conn, msg) -> {
String text = (String) msg;
if (text.startsWith("/join ")) {
String room = text.substring(6);
server.group("lobby").remove(conn);
server.group(room).add(conn);
conn.attr("currentRoom", room);
conn.send("Joined " + room);
}
});Store custom data on groups:
ConnectionGroup room = server.group("game-123");
// Set metadata
room.metadata(new GameState(/* ... */));
// Get metadata
GameState state = room.metadata();ConnectionGroup group = server.group("chat");
// Broadcast to all members
group.broadcast("Hello everyone!");
// Broadcast except one
group.broadcastExcept("User is typing...", typingUser);
// Conditional broadcast
group.broadcastIf(
"Moderator message",
conn -> "mod".equals(conn.attr("role"))
);
// Binary broadcast
group.broadcastBytes(binaryPayload);// Remove closed connections from a group
int removed = server.group("lobby").prune();
System.out.println("Pruned " + removed + " closed connections");
// Close all connections in a group
server.group("old-session").closeAll();
// Remove empty groups
int emptyGroups = server.registry().pruneEmptyGroups();
// Check if group exists
if (server.registry().hasGroup("lobby")) {
// ...
}
// Remove a group
server.registry().removeGroup("temporary-room");
// Get all group names
Set<String> groupNames = server.registry().groupNames();When a connection disconnects, it is automatically removed from all groups:
server.onDisconnect(conn -> {
// No need to manually remove from groups!
// FastSocket handles this automatically
// Just handle your business logic
System.out.println("User left: " + conn.attr("username"));
});FastSocketClient client = FastSocketClient.create()
.onConnect(() -> System.out.println("Connected!"))
.onMessage(msg -> System.out.println("Received: " + msg))
.onDisconnect(() -> System.out.println("Disconnected"))
.onError(err -> System.err.println("Error: " + err.getMessage()))
.connect("localhost", 8080);FastSocketClient client = FastSocketClient.create(config -> config
.bufferPoolSize(64)
.maxFrameSize(1024 * 1024)
.connectTimeout(5000)
.tcpNoDelay(true)
);// Send various types
client.send("Hello Server!");
client.send(42);
client.send(3.14);
client.send(true);
client.send(new byte[]{1, 2, 3, 4, 5});
// Check connection state
if (client.isConnected()) {
client.send("Still here!");
}// Connect
client.connect("localhost", 8080);
// Check connection
boolean connected = client.isConnected();
// Disconnect
client.close();
// Or use try-with-resources
try (var c = FastSocketClient.create().onMessage(m -> {}).connect("localhost", 8080)) {
c.send("Hello!");
}FastSocket supports encrypted connections using SSL/TLS. The implementation uses Java's SSLEngine for non-blocking encryption with a dedicated buffer pool optimized for SSL's memory access patterns.
import io.github.sk8erboi17.api.FastSocket;
import io.github.sk8erboi17.ssl.SslConfig;
import java.io.FileInputStream;
public class SecureServer {
public static void main(String[] args) throws Exception {
// Load keystore containing server certificate and private key
var keystoreStream = new FileInputStream("server-keystore.p12");
SslConfig sslConfig = SslConfig.forServer(keystoreStream, "keystorePassword")
.build();
FastSocket server = FastSocket.create(config -> config
.workerThreads(4)
.ssl(sslConfig))
.onConnect(conn -> {
System.out.println("Secure connection from: " + conn.remoteAddress());
System.out.println("Protocol: " + conn.sslProtocol()); // e.g., "TLSv1.3"
System.out.println("Cipher: " + conn.sslCipherSuite()); // e.g., "TLS_AES_256_GCM_SHA384"
})
.onMessage((conn, msg) -> {
conn.send("Encrypted echo: " + msg);
})
.start(8443);
System.out.println("Secure server started on port 8443");
}
}SslConfig sslConfig = SslConfig.forServer(keystoreStream, "password")
.protocols("TLSv1.3", "TLSv1.2") // Allowed protocols
.ciphers( // Allowed cipher suites
"TLS_AES_256_GCM_SHA384",
"TLS_AES_128_GCM_SHA256",
"TLS_CHACHA20_POLY1305_SHA256"
)
.clientAuth(true) // Require client certificates
.trustStore(truststoreStream, "trustpass") // For client cert validation
.build();import io.github.sk8erboi17.api.FastSocketClient;
import io.github.sk8erboi17.ssl.SslConfig;
import java.io.FileInputStream;
public class SecureClient {
public static void main(String[] args) throws Exception {
// For production: use system trust store or custom truststore
// For testing with self-signed certs: trust the server's certificate
var truststoreStream = new FileInputStream("truststore.p12");
SslConfig sslConfig = SslConfig.forClient()
.trustStore(truststoreStream, "truststorePassword")
.build();
FastSocketClient client = FastSocketClient.create()
.ssl(sslConfig)
.onConnect(() -> System.out.println("Secure connection established"))
.onMessage(msg -> System.out.println("Received: " + msg))
.connect("localhost", 8443);
// Check SSL info
System.out.println("SSL enabled: " + client.isSsl());
System.out.println("Protocol: " + client.sslProtocol());
client.send("Hello over TLS!");
Thread.sleep(1000);
client.close();
}
}// WARNING: Never use in production! Disables certificate validation.
SslConfig sslConfig = SslConfig.forClient()
.trustAll() // Accepts any certificate - INSECURE
.build();// Client with its own certificate (mutual TLS / mTLS)
var clientKeystore = new FileInputStream("client-keystore.p12");
var truststore = new FileInputStream("truststore.p12");
SslConfig sslConfig = SslConfig.forClient()
.keyStore(clientKeystore, "clientpass") // Client's certificate
.trustStore(truststore, "trustpass") // Server's CA
.build();SSL connections use a dedicated buffer pool (SslBufferPool) optimized for TLS operations. Each SSL connection requires 3 buffers (20KB each) for network input, network output, and application data.
SSL buffers have different characteristics than regular I/O buffers:
- Larger size: 16-17KB per buffer (TLS packet size) vs 1-32KB for regular I/O
- Persistent: Buffers persist for connection lifetime, not short-lived
- Fixed count: Each connection needs exactly 3 buffers
The pool is automatically sized for 1000 concurrent SSL connections (3000 buffers = ~60MB). For different loads:
// Memory calculation:
// 100 connections × 3 buffers × 20KB = 6MB
// 1,000 connections × 3 buffers × 20KB = 60MB
// 10,000 connections × 3 buffers × 20KB = 600MBThe pool includes built-in leak detection and statistics:
// Access via SslBufferPool.stats() if you have a reference
// Stats include: poolSize, available, acquired, hitRate, missCount# Generate a self-signed certificate in PKCS12 format
keytool -genkeypair \
-alias server \
-keyalg RSA \
-keysize 2048 \
-validity 365 \
-keystore server-keystore.p12 \
-storetype PKCS12 \
-storepass changeit \
-keypass changeit \
-dname "CN=localhost,OU=Dev,O=MyCompany,L=City,ST=State,C=US"# Export the certificate (no private key)
keytool -exportcert \
-alias server \
-keystore server-keystore.p12 \
-file server.crt \
-storepass changeit
# Import into a truststore for clients
keytool -importcert \
-alias server \
-file server.crt \
-keystore client-truststore.p12 \
-storetype PKCS12 \
-storepass changeit \
-nopromptFor testing with self-signed certificates, you can use the server's keystore as the client's truststore:
// Server
SslConfig serverSsl = SslConfig.forServer(
new FileInputStream("test-keystore.p12"), "testpass"
).build();
// Client (trusts the same keystore)
SslConfig clientSsl = SslConfig.forClient()
.trustStore(new FileInputStream("test-keystore.p12"), "testpass")
.build();Client Server
│ │
│──────── TCP Connect ─────────────►│
│ │
│◄─────── TCP Accept ──────────────│
│ │
│◄═══════ SSL Handshake ═══════════►│ ← Negotiates protocol, cipher,
│ (TLS 1.3: 1-RTT) │ exchanges certificates
│ │
│ onConnect() called │ ← After handshake completes
│ │
│◄══════ Encrypted Messages ═══════►│ ← All data encrypted
│ │
│──────── SSL close_notify ────────►│ ← Graceful shutdown
│◄─────── SSL close_notify ─────────│
│ │
│ onDisconnect() called │
│ │
-
Handshake cost: TLS 1.3 requires 1 round-trip; TLS 1.2 requires 2. Use connection pooling for short-lived connections.
-
Encryption overhead: ~2-5% CPU overhead for modern ciphers (AES-GCM with hardware acceleration).
-
Buffer memory: Each SSL connection uses ~60KB for buffers. Plan accordingly for high connection counts.
-
Session resumption: TLS 1.3 supports 0-RTT resumption for returning clients (not yet implemented in FastSocket).
FastSocket uses a compact binary protocol for efficient communication.
┌──────────────┬───────────────┬──────────────┬─────────────┐
│ START_MARKER │ FRAME_LENGTH │ DATA_TYPE │ PAYLOAD │
│ (1 byte) │ (4 bytes) │ (1 byte) │ (N bytes) │
└──────────────┴───────────────┴──────────────┴─────────────┘
| Field | Size | Description |
|---|---|---|
| START_MARKER | 1 byte | Always 0x01 - marks frame start |
| FRAME_LENGTH | 4 bytes | Total frame length (big-endian int) |
| DATA_TYPE | 1 byte | Type marker for deserialization |
| PAYLOAD | N bytes | Actual data |
| Type | Marker | Encoding |
|---|---|---|
| String | 0x01 |
UTF-8 bytes |
| Integer | 0x02 |
4 bytes, big-endian |
| Long | 0x03 |
8 bytes, big-endian |
| Float | 0x04 |
4 bytes, IEEE 754 |
| Double | 0x05 |
8 bytes, IEEE 754 |
| Boolean | 0x06 |
1 byte (0 or 1) |
| Bytes | 0x07 |
Raw bytes |
| Heartbeat | 0x10 |
No payload |
The maxFrameSize configuration limits the maximum frame size to prevent memory exhaustion attacks:
// Default: 64KB
// Increase for large payloads:
FastSocket.create(config -> config.maxFrameSize(10 * 1024 * 1024)); // 10MBFrames exceeding this limit cause the connection to be closed.
FastSocket uses off-heap native memory via Panama FFM for efficient I/O.
| Tier | Size | Use Case |
|---|---|---|
| SMALL | 256 bytes | Headers, small messages |
| MEDIUM | 4096 bytes | Typical messages |
| LARGE | 65536 bytes | Large payloads |
NativeBufferPool.PoolStats stats = server.bufferStats();
System.out.println("Small buffers - total: " + stats.smallTotal() +
", available: " + stats.smallAvailable());
System.out.println("Medium buffers - total: " + stats.mediumTotal() +
", available: " + stats.mediumAvailable());
System.out.println("Large buffers - total: " + stats.largeTotal() +
", available: " + stats.largeAvailable());// More buffers for high connection count
FastSocket.create(config -> config.bufferPoolSize(512));import io.github.sk8erboi17.api.FastSocket;
public class ChatServer {
public static void main(String[] args) {
FastSocket server = FastSocket.create()
.onConnect(conn -> {
// Set default username
conn.attr("username", "Anonymous-" + conn.id());
// Add to lobby
server.group("lobby").add(conn);
conn.attr("room", "lobby");
// Announce join
server.group("lobby").broadcastExcept(
"[SERVER] " + conn.attr("username") + " joined the lobby",
conn
);
// Send welcome
conn.send("[SERVER] Welcome! Commands: /name <name>, /join <room>, /whisper <id> <msg>, /list");
})
.onMessage((conn, msg) -> {
String text = (String) msg;
String username = conn.attr("username");
String currentRoom = conn.attr("room");
if (text.startsWith("/name ")) {
// Change username
String newName = text.substring(6).trim();
String oldName = username;
conn.attr("username", newName);
server.group(currentRoom).broadcast(
"[SERVER] " + oldName + " is now known as " + newName
);
} else if (text.startsWith("/join ")) {
// Join a different room
String newRoom = text.substring(6).trim();
// Leave current room
server.group(currentRoom).remove(conn);
server.group(currentRoom).broadcast(
"[SERVER] " + username + " left the room"
);
// Join new room
server.group(newRoom).add(conn);
conn.attr("room", newRoom);
server.group(newRoom).broadcastExcept(
"[SERVER] " + username + " joined the room",
conn
);
conn.send("[SERVER] You joined " + newRoom);
} else if (text.startsWith("/whisper ")) {
// Private message: /whisper <id> <message>
String[] parts = text.substring(9).split(" ", 2);
if (parts.length == 2) {
try {
long targetId = Long.parseLong(parts[0]);
String message = parts[1];
boolean sent = server.send(targetId,
"[WHISPER from " + username + "] " + message);
if (sent) {
conn.send("[WHISPER to " + targetId + "] " + message);
} else {
conn.send("[SERVER] User not found");
}
} catch (NumberFormatException e) {
conn.send("[SERVER] Invalid user ID");
}
}
} else if (text.equals("/list")) {
// List users in current room
StringBuilder sb = new StringBuilder("[SERVER] Users in " + currentRoom + ": ");
for (var member : server.group(currentRoom).members()) {
sb.append(member.attr("username")).append(" (").append(member.id()).append("), ");
}
conn.send(sb.toString());
} else {
// Regular message - broadcast to room
server.group(currentRoom).broadcast(
"[" + username + "] " + text
);
}
})
.onDisconnect(conn -> {
String username = conn.attr("username");
String room = conn.attr("room");
// Announce departure (connection already removed from groups automatically)
server.group(room).broadcast("[SERVER] " + username + " disconnected");
})
.onError((conn, err) -> {
System.err.println("Error on " + conn.id() + ": " + err.getMessage());
})
.start(8080);
System.out.println("Chat server started on port 8080");
server.awaitTermination();
}
}import io.github.sk8erboi17.api.FastSocket;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
public class GameLobbyServer {
private static final AtomicLong gameIdCounter = new AtomicLong(1);
public static void main(String[] args) {
FastSocket server = FastSocket.create(config -> config
.workerThreads(4)
.bufferPoolSize(256)
)
.onConnect(conn -> {
conn.attr("state", "lobby");
conn.attr("ready", false);
server.group("lobby").add(conn);
conn.send("WELCOME:" + conn.id());
broadcastLobbyUpdate(server);
})
.onMessage((conn, msg) -> {
String[] parts = ((String) msg).split(":", 2);
String cmd = parts[0];
String arg = parts.length > 1 ? parts[1] : "";
switch (cmd) {
case "SET_NAME" -> {
conn.attr("name", arg);
broadcastLobbyUpdate(server);
}
case "CREATE_GAME" -> {
String gameId = "game-" + gameIdCounter.getAndIncrement();
server.group("lobby").remove(conn);
server.group(gameId).add(conn);
conn.attr("state", "waiting");
conn.attr("gameId", gameId);
conn.attr("isHost", true);
conn.send("GAME_CREATED:" + gameId);
broadcastLobbyUpdate(server);
}
case "JOIN_GAME" -> {
String gameId = arg;
var gameGroup = server.group(gameId);
if (gameGroup.size() < 4) {
server.group("lobby").remove(conn);
gameGroup.add(conn);
conn.attr("state", "waiting");
conn.attr("gameId", gameId);
conn.attr("isHost", false);
conn.send("JOINED:" + gameId);
gameGroup.broadcast("PLAYER_JOINED:" + conn.attr("name"));
broadcastLobbyUpdate(server);
} else {
conn.send("ERROR:Game is full");
}
}
case "READY" -> {
conn.attr("ready", true);
String gameId = conn.attr("gameId");
server.group(gameId).broadcast("PLAYER_READY:" + conn.attr("name"));
// Check if all ready
boolean allReady = true;
for (var player : server.group(gameId).members()) {
if (!(boolean) player.attr("ready")) {
allReady = false;
break;
}
}
if (allReady && server.group(gameId).size() >= 2) {
for (var player : server.group(gameId).members()) {
player.attr("state", "playing");
}
server.group(gameId).broadcast("GAME_START");
}
}
case "GAME_ACTION" -> {
String gameId = conn.attr("gameId");
server.group(gameId).broadcastExcept(
"ACTION:" + conn.attr("name") + ":" + arg,
conn
);
}
case "LEAVE_GAME" -> {
String gameId = conn.attr("gameId");
server.group(gameId).remove(conn);
server.group(gameId).broadcast("PLAYER_LEFT:" + conn.attr("name"));
server.group("lobby").add(conn);
conn.attr("state", "lobby");
conn.attr("gameId", null);
conn.attr("ready", false);
conn.send("LEFT_GAME");
broadcastLobbyUpdate(server);
}
}
})
.onDisconnect(conn -> {
String gameId = conn.attr("gameId");
if (gameId != null) {
server.group(gameId).broadcast("PLAYER_DISCONNECTED:" + conn.attr("name"));
}
broadcastLobbyUpdate(server);
})
.start(9000);
System.out.println("Game lobby server started on port 9000");
}
private static void broadcastLobbyUpdate(FastSocket server) {
int lobbyCount = server.group("lobby").size();
server.group("lobby").broadcast("LOBBY_UPDATE:" + lobbyCount);
}
}import io.github.sk8erboi17.api.FastSocket;
import java.util.*;
public class PubSubServer {
public static void main(String[] args) {
FastSocket server = FastSocket.create()
.onConnect(conn -> {
conn.attr("subscriptions", new HashSet<String>());
conn.send("READY");
})
.onMessage((conn, msg) -> {
String[] parts = ((String) msg).split(" ", 3);
String cmd = parts[0];
switch (cmd) {
case "SUBSCRIBE" -> {
if (parts.length >= 2) {
String topic = parts[1];
Set<String> subs = conn.attr("subscriptions");
subs.add(topic);
server.group("topic:" + topic).add(conn);
conn.send("SUBSCRIBED " + topic);
}
}
case "UNSUBSCRIBE" -> {
if (parts.length >= 2) {
String topic = parts[1];
Set<String> subs = conn.attr("subscriptions");
subs.remove(topic);
server.group("topic:" + topic).remove(conn);
conn.send("UNSUBSCRIBED " + topic);
}
}
case "PUBLISH" -> {
if (parts.length >= 3) {
String topic = parts[1];
String message = parts[2];
server.group("topic:" + topic).broadcast(
"MESSAGE " + topic + " " + message
);
}
}
case "LIST" -> {
Set<String> subs = conn.attr("subscriptions");
conn.send("SUBSCRIPTIONS " + String.join(",", subs));
}
}
})
.start(7000);
System.out.println("Pub/Sub server started on port 7000");
}
}// Creation
static FastSocket create()
static FastSocket create(Consumer<FastSocketConfig> configurer)
// Event Handlers
FastSocket onConnect(Consumer<Connection> handler)
FastSocket onMessage(BiConsumer<Connection, Object> handler)
FastSocket onMessage(Class<String> type, BiConsumer<Connection, String> handler)
FastSocket onBinaryMessage(BiConsumer<Connection, byte[]> handler)
FastSocket onDisconnect(Consumer<Connection> handler)
FastSocket onError(BiConsumer<Connection, Throwable> handler)
// Lifecycle
FastSocket start(int port)
FastSocket start(String host, int port)
FastSocket start(InetSocketAddress address)
void stop()
void close() // AutoCloseable
void awaitTermination()
boolean isRunning()
boolean isSsl() // Returns true if SSL is enabled
// Messaging
boolean send(long connectionId, Object message)
boolean sendBytes(long connectionId, byte[] data)
void broadcast(Object message)
void broadcastBytes(byte[] data)
void broadcastExcept(Object message, Connection except)
void broadcastExcept(Object message, long exceptId)
void broadcastIf(Object message, Predicate<Connection> filter)
boolean multicast(String groupName, Object message)
boolean multicastExcept(String groupName, Object message, Connection except)
// Registry & Groups
ConnectionRegistry registry()
ConnectionGroup group(String name)
Connection connection(long id)
int connectionCount()
// Configuration & Stats
FastSocketConfig config()
NativeBufferPool.PoolStats bufferStats()// Identity
long id()
InetSocketAddress remoteAddress()
boolean isOpen()
// SSL Info
boolean isSsl() // True if connection uses SSL/TLS
String sslProtocol() // e.g., "TLSv1.3" or "NONE"
String sslCipherSuite() // e.g., "TLS_AES_256_GCM_SHA384" or null
// Sending
Connection send(Object data)
Connection sendBytes(byte[] data)
Connection sendHeartbeat()
// Attributes (index-based)
<T> T attr(int index)
Connection attr(int index, Object value)
// Attributes (string-keyed)
<T> T attr(String key)
Connection attr(String key, Object value)
boolean hasAttr(String key)
// Lifecycle
void close()// Membership
ConnectionGroup add(Connection connection)
ConnectionGroup remove(Connection connection)
boolean contains(Connection connection)
int size()
boolean isEmpty()
Set<Connection> members()
// Metadata
<T> T metadata()
ConnectionGroup metadata(Object metadata)
// Messaging
ConnectionGroup broadcast(Object message)
ConnectionGroup broadcastExcept(Object message, Connection except)
ConnectionGroup broadcastIf(Object message, Predicate<Connection> filter)
ConnectionGroup broadcastBytes(byte[] data)
// Maintenance
int prune()
void closeAll()// Connection Management
void register(Connection connection)
void unregister(Connection connection)
Connection get(long connectionId)
boolean contains(long connectionId)
int connectionCount()
Collection<Connection> connections()
// Group Management
ConnectionGroup group(String name)
ConnectionGroup getGroup(String name)
ConnectionGroup removeGroup(String name)
boolean hasGroup(String name)
int groupCount()
Set<String> groupNames()
int pruneEmptyGroups()
// Unicast
boolean send(long connectionId, Object message)
boolean sendBytes(long connectionId, byte[] data)
// Broadcast
void broadcast(Object message)
void broadcastExcept(Object message, Connection except)
void broadcastExcept(Object message, long exceptId)
void broadcastIf(Object message, Predicate<Connection> filter)
void broadcastBytes(byte[] data)
// Multicast
boolean multicast(String groupName, Object message)
boolean multicastExcept(String groupName, Object message, Connection except)
boolean multicastBytes(String groupName, byte[] data)
// Utility
List<Connection> findByAttribute(String key, Object value)
Connection findFirstByAttribute(String key, Object value)
int pruneClosedConnections()
void closeAll()// Creation
static FastSocketClient create()
static FastSocketClient create(Consumer<FastSocketClientConfig> configurer)
// Event Handlers
FastSocketClient onConnect(Runnable handler)
FastSocketClient onMessage(Consumer<Object> handler)
FastSocketClient onDisconnect(Runnable handler)
FastSocketClient onError(Consumer<Throwable> handler)
// SSL Configuration
FastSocketClient ssl(SslConfig config) // Enable SSL with given config
// Connection
FastSocketClient connect(String host, int port)
boolean isConnected()
void close()
// SSL Info
boolean isSsl() // True if SSL is enabled
String sslProtocol() // e.g., "TLSv1.3" or "NONE"
// Sending
void send(Object data)
void send(byte[] data)MIT License