← Back to Documentation

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) {
    Start the <socket-server> with { port: 9000 }.
    Log "Socket server listening on port 9000" to the <console>.
    Keepalive the <application> for the <events>.
    Return an <OK: status> for the <startup>.
}

Connection Events

Socket servers emit events for connection lifecycle:

EventWhen Triggered
ClientConnectedClient connects
DataReceivedData received from client
ClientDisconnectedClient disconnects

Handling Connections

(Handle Connection: ClientConnected Handler) {
    Extract the <connection-id> from the <event: connectionId>.
    Extract the <remote-address> from the <event: remoteAddress>.

    Log "Client connected: ${remote-address}" to the <console>.

    (* 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 "Received from ${connection-id}: ${data}" to the <console>.

    (* Process the data — any OWN action, or a user-defined one *)
    Compute the <response: uppercase> from <data>.

    (* Send the response back down the same connection *)
    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 "Client ${connection-id} disconnected: ${reason}" to the <console>.

    (* 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

(* A feature-set name cannot contain a preposition word such as `to`. *)
(Connect Server: External Service) {
    Create the <host> with "192.168.1.100".
    Connect the <server-connection> to the <host> with { port: 8080 }.

    (* Send an initial handshake — `type` is a keyword, so quote the key *)
    Create the <handshake> with { "type": "hello", client: "my-app" }.
    Send the <handshake> to the <server-connection>.

    Return an <OK: status> for the <server-connection>.
}

Sending Data

(Send Message: Messaging) {
    (* `type` is a reserved word — quote it to use it as a key *)
    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 <message>.
}

Common Patterns

Echo Server

(Application-Start: Echo Server) {
    Start the <socket-server> with { port: 9000 }.
    Log "Echo server listening on port 9000" to the <console>.
    Keepalive the <application> for the <events>.
    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) {
    Start the <socket-server> with { port: 9000 }.
    Keepalive the <application> for the <events>.
    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>.

    (* Repositories are the shared state a handler can reach *)
    Create the <client> with { id: <connection-id> }.
    Store the <stored-client> into the <client-repository> with <client>.

    (* Notify others *)
    Broadcast the <announcement> to the <socket-server> with "User joined".

    Return an <OK: status> for the <stored-client>.
}

Data Formats

Text Data

Send the <text> to the <connection> with "Hello, World!".

JSON Data

(* `type` is a reserved word — quote it to use it as an object key *)
Create the <message> with {
    "type": "update",
    data: { value: 42 }
}.
Send the <message: JSON> 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>.

Best Practices

Validate Incoming Data

(Handle Data: DataReceived Handler) {
    Extract the <raw-data> from the <event: data>.

    (* Validate the data format *)
    Parse the <message: JSON> from the <raw-data>.

    (* ARO has no `if`. A guard rides on the statement it protects. *)
    Log "Invalid message format" to the <console> when <message> is empty.
    Return an <OK: status> for the <raw-data> when <message> is empty.

    Validate the <valid> for the <message>.
    Return an <OK: status> for the <message> when not <valid>.

    Compute the <result: length> from <message>.
    Return an <OK: status> with <result>.
}

Clean Up on Shutdown

(Application-End: Success) {
    Log "Closing all connections..." to the <console>.

    (* Notify all clients *)
    Broadcast the <shutdown-notice> to the <socket-server> with "Server shutting down".

    (* Close the server *)
    Close the <socket-server> with <application>.

    Return an <OK: status> for the <shutdown>.
}

Next Steps

Events - Event-driven patterns
HTTP Services - HTTP communication
Application Lifecycle - Startup and shutdown