7.3 KiB
Networking in Orchid
This document explains how to use TCP networking in the Orchid programming language.
Prerequisites
Build the project with the nightly Rust toolchain:
rustup run nightly cargo build
Socket Module Overview
The socket module is available at std::socket::tcp and provides functions for TCP client/server communication.
Importing
import std::socket::tcp::(bind, accept, connect, read, write_all, close)
Or import all functions:
import std::socket::tcp
API Reference
Server Functions
| Function | Signature | Description |
|---|---|---|
bind |
String -> TcpListener |
Create a TCP listener bound to an address |
accept |
TcpListener -> TcpStream |
Accept an incoming connection |
listener_addr |
TcpListener -> String |
Get the address the listener is bound to |
Client Functions
| Function | Signature | Description |
|---|---|---|
connect |
String -> TcpStream |
Connect to a TCP server |
Stream Functions
| Function | Signature | Description |
|---|---|---|
read |
TcpStream -> Int -> String |
Read up to N bytes as UTF-8 string |
read_bytes |
TcpStream -> Int -> String |
Read up to N bytes as raw byte string |
read_exact |
TcpStream -> Int -> String |
Read exactly N bytes |
write |
TcpStream -> String -> Int |
Write data, returns bytes written |
write_all |
TcpStream -> String -> Int |
Write all data |
flush |
TcpStream -> Int |
Flush the stream buffer |
close |
TcpStream -> Int |
Close the connection |
peer_addr |
TcpStream -> String |
Get the remote peer's address |
local_addr |
TcpStream -> String |
Get the local address |
Quick Start Examples
Testing Socket Bind
rustup run nightly cargo orcx -- exec "std::socket::tcp::bind \"127.0.0.1:8080\""
Output:
<TcpListener 127.0.0.1:8080>
Testing Connection (expect error if no server running)
rustup run nightly cargo orcx -- exec "std::socket::tcp::connect \"127.0.0.1:8080\""
Output (if no server):
error: IO error: Connection refused (os error 111): @
Example Scripts
TCP Echo Server
Location: examples/tcp-echo-server/src/main.orc
import std::socket::tcp::(bind, accept, read, write_all, close)
const handle_client := \client. do cps {
cps data = read client 1024;
cps _ = write_all client data;
cps _ = close client;
cps pass 0;
}
const accept_loop := \server. do cps {
cps client = accept server;
cps _ = handle_client client;
cps pass $ accept_loop server;
}
const main := do cps {
cps server = bind "127.0.0.1:8080";
cps pass $ accept_loop server;
}
Run the echo server:
rustup run nightly cargo orcx -- exec --proj ./examples/tcp-echo-server "src::main::main"
TCP Client
Location: examples/tcp-client/src/main.orc
import std::socket::tcp::(connect, read, write_all, close, peer_addr)
import system::io::println
const main := do cps {
cps conn = connect "127.0.0.1:8080";
cps addr = peer_addr conn;
cps println $ "Connected to ${addr}";
cps _ = write_all conn "Hello from Orchid!\n";
cps response = read conn 1024;
cps println $ "Server response: ${response}";
cps _ = close conn;
cps pass 0;
}
Run the client (requires a server running on port 8080):
rustup run nightly cargo orcx -- exec --proj ./examples/tcp-client "src::main::main"
HTTP Server
Location: examples/http-server/src/main.orc
import std::socket::tcp::(bind, accept, read, write_all, close, peer_addr, listener_addr)
import system::io::println
const http_response := "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 44\r\nConnection: close\r\n\r\n<html><body><h1>Hello Orchid!</h1></body></html>"
const handle_client := \client. do cps {
cps addr = peer_addr client;
cps println $ "Client connected: ${addr}";
cps request = read client 4096;
cps println $ "Received request from ${addr}";
cps _ = write_all client http_response;
cps _ = close client;
cps println $ "Connection closed: ${addr}";
cps pass 0;
}
const accept_loop := \server. do cps {
cps client = accept server;
cps _ = handle_client client;
cps pass $ accept_loop server;
}
const main := do cps {
cps server = bind "127.0.0.1:8080";
cps addr = listener_addr server;
cps println $ "HTTP Server listening on ${addr}";
cps println "Press Ctrl+C to stop";
cps pass $ accept_loop server;
}
Run the HTTP server:
rustup run nightly cargo orcx -- exec --proj ./examples/http-server "src::main::main"
Then open http://127.0.0.1:8080 in your browser or test with curl:
curl http://127.0.0.1:8080
Understanding the CPS Syntax
Orchid uses Continuation-Passing Style (CPS) for side effects. The do cps { ... } block is used for sequential operations:
cps variable = expression;- Bind the result of an expression to a variablecps expression;- Execute an expression for its side effectscps pass value;- Return a value from the block
Example breakdown:
const main := do cps {
cps server = bind "127.0.0.1:8080"; -- Bind socket, store in 'server'
cps client = accept server; -- Accept connection, store in 'client'
cps data = read client 1024; -- Read data from client
cps _ = write_all client data; -- Write data back (ignore result)
cps _ = close client; -- Close connection
cps pass 0; -- Return 0
}
Error Handling
Socket operations return errors when they fail. Errors are displayed with the IO error message:
error: IO error: Connection refused (os error 111): @
Common errors:
Connection refused- No server listening on the target addressAddress already in use- Port is already bound by another processConnection reset by peer- Remote end closed the connection unexpectedly
Building a Simple Protocol
Here's an example of a simple request/response protocol:
import std::socket::tcp::(bind, accept, read, write_all, close)
const handle_request := \request.
if request == "PING\n" then "PONG\n"
else if request == "TIME\n" then "2024-01-01 00:00:00\n"
else "UNKNOWN\n"
const handle_client := \client. do cps {
cps request = read client 1024;
cps response = pass $ handle_request request;
cps _ = write_all client response;
cps _ = close client;
cps pass 0;
}
const server_loop := \server. do cps {
cps client = accept server;
cps _ = handle_client client;
cps pass $ server_loop server;
}
const main := do cps {
cps server = bind "127.0.0.1:9000";
cps pass $ server_loop server;
}
Limitations
- The current implementation handles one client at a time (sequential accept loop)
- Sockets are non-serializable (cannot be persisted or transferred across boundaries)
- No UDP support (TCP only)
- No TLS/SSL support
Troubleshooting
Port Already in Use
If you get "Address already in use", either:
- Wait for the previous process to release the port
- Use a different port
- Kill the process using the port:
lsof -i :8080thenkill <pid>
Connection Refused
Ensure the server is running before starting the client.
Build Errors
Make sure to use the nightly toolchain:
rustup run nightly cargo build