Sockets
ARO provides built-in TCP socket support for bidirectional real-time communication. This chapter covers socket servers and clients.
Socket Server
Starting a Server
Listen for TCP connections in Application-Start:
(Application-Start: Socket Server) {
<Listen> on port 9000 as <socket-server>.
<Log> the <message> for the <console> with "Socket server listening on port 9000".
<Return> an <OK: status> for the <startup>.
}
Connection Events
Socket servers emit events for connection lifecycle:
| Event | When Triggered |
|---|---|
ClientConnected |
Client connects |
DataReceived |
Data received from client |
ClientDisconnected |
Client disconnects |
Handling Connections
(Handle Connection: ClientConnected Handler) {
<Extract> the <connection-id> from the <event: connectionId>.
<Extract> the <remote-address> from the <event: remoteAddress>.
<Log> the <message> for the <console> with "Client connected: ${remote-address}".
(* Send welcome message *)
<Send> the <welcome> to the <event: connection> with "Welcome to the server!".
<Return> an <OK: status> for the <connection>.
}
Receiving Data
(Handle Data: DataReceived Handler) {
<Extract> the <data> from the <event: data>.
<Extract> the <connection> from the <event: connection>.
<Extract> the <connection-id> from the <event: connectionId>.
<Log> the <message> for the <console> with "Received from ${connection-id}: ${data}".
(* Process the data *)
<Process> the <response> from the <data>.
(* Send response back *)
<Send> the <response> to the <connection>.
<Return> an <OK: status> for the <data>.
}
Handling Disconnections
(Handle Disconnect: ClientDisconnected Handler) {
<Extract> the <connection-id> from the <event: connectionId>.
<Extract> the <reason> from the <event: reason>.
<Log> the <message> for the <console> with "Client ${connection-id} disconnected: ${reason}".
(* Clean up client state *)
<Delete> the <client-state> from the <client-registry> where id = <connection-id>.
<Return> an <OK: status> for the <disconnect>.
}
Socket Client
Connecting to a Server
(Connect to Server: External Service) {
<Connect> to <host: "192.168.1.100"> on port 8080 as <server-connection>.
(* Send initial handshake *)
<Send> the <handshake> to the <server-connection> with { type: "hello", client: "my-app" }.
<Return> an <OK: status> for the <connection>.
}
Sending Data
(Send Message: Messaging) {
<Create> the <message> with {
type: "chat",
content: <content>,
timestamp: <current-time>
}.
<Send> the <message: JSON> to the <server-connection>.
<Return> an <OK: status> for the <send>.
}
Receiving Responses
(Handle Server Response: DataReceived Handler) {
<Extract> the <data> from the <event: data>.
<Parse> the <message: JSON> from the <data>.
<Extract> the <type> from the <message: type>.
match <type> {
case "ack" {
<Log> the <message> for the <console> with "Message acknowledged".
}
case "error" {
<Extract> the <error> from the <message: error>.
<Log> the <error: message> for the <console> with <error>.
}
case "data" {
<Process> the <result> from the <message: payload>.
}
}
<Return> an <OK: status> for the <response>.
}
Common Patterns
Echo Server
(Application-Start: Echo Server) {
<Listen> on port 9000 as <echo-server>.
<Log> the <message> for the <console> with "Echo server listening on port 9000".
<Return> an <OK: status> for the <startup>.
}
(Echo Data: DataReceived Handler) {
<Extract> the <data> from the <event: data>.
<Extract> the <connection> from the <event: connection>.
(* Echo back the received data *)
<Send> the <data> to the <connection>.
<Return> an <OK: status> for the <echo>.
}
Chat Server
(Application-Start: Chat Server) {
<Listen> on port 9000 as <chat-server>.
<Create> the <clients> with [].
<Publish> as <connected-clients> <clients>.
<Return> an <OK: status> for the <startup>.
}
(Register Client: ClientConnected Handler) {
<Extract> the <connection> from the <event: connection>.
<Extract> the <connection-id> from the <event: connectionId>.
(* Add to connected clients *)
<Store> the <connection> into the <connected-clients> with id <connection-id>.
(* Notify others *)
<Broadcast> the <announcement> to the <chat-server> with "User joined".
<Return> an <OK: status> for the <registration>.
}
(Broadcast Message: DataReceived Handler) {
<Extract> the <message> from the <event: data>.
<Extract> the <sender-id> from the <event: connectionId>.
<Create> the <broadcast> with {
from: <sender-id>,
message: <message>,
timestamp: <current-time>
}.
(* Send to all connected clients *)
<Broadcast> the <broadcast: JSON> to the <chat-server>.
<Return> an <OK: status> for the <broadcast>.
}
(Remove Client: ClientDisconnected Handler) {
<Extract> the <connection-id> from the <event: connectionId>.
(* Remove from connected clients *)
<Delete> the <client> from the <connected-clients> where id = <connection-id>.
(* Notify others *)
<Broadcast> the <announcement> to the <chat-server> with "User left".
<Return> an <OK: status> for the <removal>.
}
Protocol Handler
(Handle Protocol: DataReceived Handler) {
<Extract> the <raw-data> from the <event: data>.
<Extract> the <connection> from the <event: connection>.
(* Parse protocol message *)
<Parse> the <message: JSON> from the <raw-data>.
<Extract> the <type> from the <message: type>.
<Extract> the <payload> from the <message: payload>.
match <type> {
case "ping" {
<Send> the <pong> to the <connection> with { type: "pong" }.
}
case "auth" {
<Validate> the <payload> for the <auth-schema>.
if <validation> is success then {
<Send> the <auth-success> to the <connection> with { type: "auth_ok" }.
} else {
<Send> the <auth-failed> to the <connection> with { type: "auth_fail" }.
}
}
case "subscribe" {
<Extract> the <channel> from the <payload: channel>.
<Subscribe> the <connection> to the <channel>.
<Send> the <subscribed> to the <connection> with { type: "subscribed", channel: <channel> }.
}
case "publish" {
<Extract> the <channel> from the <payload: channel>.
<Extract> the <data> from the <payload: data>.
<Publish> the <data> to the <channel>.
}
}
<Return> an <OK: status> for the <protocol>.
}
Client Reconnection
(Application-Start: Resilient Client) {
<Connect> to <host: "server.example.com"> on port 9000 as <server>.
<Return> an <OK: status> for the <startup>.
}
(Handle Disconnect: ClientDisconnected Handler) {
<Extract> the <reason> from the <event: reason>.
<Log> the <message> for the <console> with "Disconnected: ${reason}. Reconnecting...".
(* Wait before reconnecting *)
<Wait> for 5 seconds.
(* Attempt reconnection *)
<Connect> to <host: "server.example.com"> on port 9000 as <server>.
<Return> an <OK: status> for the <reconnect>.
}
Data Formats
Text Data
<Send> the <text> to the <connection> with "Hello, World!".
JSON Data
<Create> the <message> with {
type: "update",
data: { value: 42 }
}.
<Send> the <message: JSON> to the <connection>.
Binary Data
<Read> the <binary: bytes> from the <file: "./data.bin">.
<Send> the <binary> to the <connection>.
Broadcasting
To All Clients
<Broadcast> the <message> to the <socket-server>.
To Specific Clients
(* Send to specific connection *)
<Send> the <message> to the <connection>.
(* Send to connection by ID *)
<Retrieve> the <client> from the <connected-clients> where id = <client-id>.
<Send> the <message> to the <client: connection>.
Connection Management
Tracking Connections
(Track Connection: ClientConnected Handler) {
<Extract> the <connection> from the <event: connection>.
<Extract> the <connection-id> from the <event: connectionId>.
<Extract> the <remote-address> from the <event: remoteAddress>.
<Create> the <client-info> with {
id: <connection-id>,
address: <remote-address>,
connection: <connection>,
connectedAt: <current-time>
}.
<Store> the <client-info> into the <client-registry>.
<Return> an <OK: status> for the <tracking>.
}
Closing Connections
(Kick Client: Admin Action) {
<Extract> the <client-id> from the <request: parameters>.
<Retrieve> the <client> from the <client-registry> where id = <client-id>.
if <client> is not empty then {
<Send> the <kick-notice> to the <client: connection> with "You have been disconnected".
<Close> the <client: connection>.
<Delete> the <client> from the <client-registry> where id = <client-id>.
}
<Return> an <OK: status> for the <kick>.
}
Best Practices
Handle Connection Errors
(Connect to Server: Client Setup) {
<Connect> to <host: "server.example.com"> on port 9000 as <server>.
if <server> is empty then {
<Log> the <error> for the <console> with "Failed to connect to server".
<Return> a <ServiceUnavailable: status> for the <connection: error>.
}
<Return> an <OK: status> for the <connection>.
}
Validate Incoming Data
(Handle Data: DataReceived Handler) {
<Extract> the <raw-data> from the <event: data>.
(* Validate data format *)
<Parse> the <message: JSON> from the <raw-data>.
if <message> is empty then {
<Log> the <warning> for the <console> with "Invalid message format".
<Return> an <OK: status> for the <validation>.
}
<Validate> the <message> for the <message-schema>.
if <validation> is failed then {
<Send> the <error> to the <event: connection> with { error: "Invalid message" }.
<Return> an <OK: status> for the <validation>.
}
(* Process valid message *)
<Process> the <result> from the <message>.
<Return> an <OK: status> for the <processing>.
}
Clean Up on Shutdown
(Application-End: Success) {
<Log> the <message> for the <console> with "Closing all connections...".
(* Notify all clients *)
<Broadcast> the <shutdown-notice> to the <socket-server> with "Server shutting down".
(* Close server *)
<Close> the <socket-server>.
<Return> an <OK: status> for the <shutdown>.
}
Next Steps
- Events - Event-driven patterns
- HTTP Services - HTTP communication
- Application Lifecycle - Startup and shutdown