# 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: ```sh 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 ```orchid import std::socket::tcp::(bind, accept, connect, read, write_all, close) ``` Or import all functions: ```orchid 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 ```sh rustup run nightly cargo orcx -- exec "std::socket::tcp::bind \"127.0.0.1:8080\"" ``` Output: ``` ``` ### Testing Connection (expect error if no server running) ```sh 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` ```orchid 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: ```sh rustup run nightly cargo orcx -- exec --proj ./examples/tcp-echo-server "src::main::main" ``` ### TCP Client Location: `examples/tcp-client/src/main.orc` ```orchid 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): ```sh rustup run nightly cargo orcx -- exec --proj ./examples/tcp-client "src::main::main" ``` ### HTTP Server Location: `examples/http-server/src/main.orc` ```orchid 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

Hello Orchid!

" 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: ```sh 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: ```sh 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 variable - `cps expression;` - Execute an expression for its side effects - `cps pass value;` - Return a value from the block Example breakdown: ```orchid 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 address - `Address already in use` - Port is already bound by another process - `Connection reset by peer` - Remote end closed the connection unexpectedly ## Building a Simple Protocol Here's an example of a simple request/response protocol: ```orchid 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: 1. Wait for the previous process to release the port 2. Use a different port 3. Kill the process using the port: `lsof -i :8080` then `kill ` ### Connection Refused Ensure the server is running before starting the client. ### Build Errors Make sure to use the nightly toolchain: ```sh rustup run nightly cargo build ```