Concurrency & Networking
Introduced in v1.17, with TLS and UDP added in v1.19. Thread-pool mapping, raw TCP/UDP sockets, and WebSockets — all built on the Python standard library, zero external dependencies.
parallel_map
Thread-pool map for I/O-bound work: HTTP calls, shell commands, file reads. Runs the block across a pool of worker threads and returns results in input order.
urls = [
"https://api.example.com/a",
"https://api.example.com/b",
"https://api.example.com/c"
]
pages = parallel_map(urls, workers: 8) do |u|
http_get(u)
end
workers:defaults to4.- Results always come back in the same order as the input vector.
- The first exception raised by any worker is re-raised.
- An empty vector returns
[]immediately.
Because Frankie threads share one Python interpreter, parallel_map shines for I/O-bound work (waiting on the network or disk), not CPU-bound number crunching.
# Run several shell commands at once
results = parallel_map(["ls", "date", "whoami"], workers: 3) do |cmd|
shell(cmd)["stdout"]
end
TCP Sockets
tcp_connect(host, port, timeout: nil, tls: false) — client
Opens a TCP connection and returns a socket object:
sock = tcp_connect("example.com", 7777, timeout: 5)
sock.send_line("ping")
puts sock.recv_line()
puts sock.peer() # "93.184.216.34:7777"
sock.close()
Pass tls: true for a certificate-verified TLS connection (v1.19), via Python's stdlib ssl:
sock = tcp_connect("example.com", 443, tls: true)
sock.send_line("GET / HTTP/1.0\r\n\r\n")
puts sock.recv_line()
sock.close()
Server-side TLS stays on the wish list — tcp_listen/tcp_serve are plaintext only.
Socket methods
| Method | Description |
|---|---|
sock.send(s) |
Send a string — returns bytes sent |
sock.send_line(s) |
Send a string followed by a newline |
sock.recv(n) |
Receive up to n bytes as a string (default 4096) — nil when the peer closes |
sock.recv_line() |
Receive until newline (exclusive) — nil when the peer closes |
sock.peer() |
Remote end as "host:port" |
sock.close() |
Close the connection |
Use send/recv(n) for raw data, send_line/recv_line for line-based protocols.
tcp_listen(port, host: "0.0.0.0") — manual server
server = tcp_listen(7777)
client = server.accept() # blocks until a client connects
msg = client.recv_line()
client.send_line("got: #{msg}")
client.close()
server.close()
tcp_serve(port) do |client| ... end — threaded server loop
One thread per client, sockets closed automatically when the block returns. Blocks forever (Ctrl+C to stop):
tcp_serve(7777) do |client|
msg = client.recv_line()
client.send_line("echo: #{msg}")
end
# 🧟 Frankie TCP server listening on 0.0.0.0:7777
Example: a tiny client/server pair
# server.fk
tcp_serve(7777) do |client|
name = client.recv_line()
client.send_line("Hello, #{name}!")
end
# client.fk
sock = tcp_connect("localhost", 7777)
sock.send_line("Frankie")
puts sock.recv_line() # Hello, Frankie!
sock.close()
UDP Sockets (v1.19)
Connectionless datagrams — no handshake, no ordering guarantees, fire-and-forget or request/response as you like.
udp_listen(port, host: "0.0.0.0")
sock = udp_listen(9999)
msg = sock.recv() # blocks for the next datagram
puts msg["data"] # the payload as a string
puts msg["host"] # sender's address
sock.send_to(msg["host"], msg["port"], "pong")
sock.close()
udp_send(host, port, message)
Fire-and-forget — opens a socket, sends one datagram, closes:
udp_send("127.0.0.1", 9999, "ping")
UDP socket methods
| Method | Description |
|---|---|
sock.recv(n: 4096) |
Block for the next datagram → {data:, host:, port:} |
sock.send_to(host, port, msg) |
Send a datagram to a specific address |
sock.close() |
Close the socket |
WebSockets (v1.18) + TLS (v1.19)
Full WebSocket coverage lives on the Web Server page — app.websocket for the server side, ws_connect for clients. Since v1.19, ws_connect also accepts wss:// URLs for TLS-secured connections:
sock = ws_connect("wss://echo.example.com/socket")
sock.send("hello")
puts sock.recv()
sock.close()
Error Handling
Socket operations raise on failure — connection refused, timeouts, etc. Rescue them like any other error:
begin
sock = tcp_connect("localhost", 9999, timeout: 2)
rescue TimeoutError => e
puts "Connection timed out: #{e}"
rescue e
puts "Could not connect: #{e}"
end
Quick Reference
| Function | Description |
|---|---|
parallel_map(vec, workers: 4) do \|x\| ... end |
Thread-pool map, results in input order |
tcp_connect(host, port, timeout: nil, tls: false) |
Open a TCP connection — tls: true for TLS (v1.19) |
tcp_listen(port, host: "0.0.0.0") |
Listen on a port; .accept() returns a client socket |
tcp_serve(port) do \|client\| ... end |
Threaded accept loop, auto-closes clients |
sock.send(s) / sock.send_line(s) |
Send a string (newline appended by send_line) |
sock.recv(n) / sock.recv_line() |
Receive a string — nil when peer closes |
sock.peer() / sock.close() |
Remote "host:port" / close the connection |
udp_listen(port, host: "0.0.0.0") |
Bind a UDP socket (v1.19) |
udp_send(host, port, msg) |
Fire-and-forget datagram (v1.19) |
sock.recv(n) / sock.send_to(host, port, msg) |
UDP receive/send (v1.19) |
ws_connect(url, timeout: nil) |
WebSocket client — ws:// or wss:// (v1.18 / TLS in v1.19) |