AtlatestRepositorysigil-socket
1# Socket
2
3> TCP, UDP, and Unix domain socket operations for network programming.
4
5```scheme
6(import (sigil socket))
7```
8
9## TCP Client
11```scheme
12;; Connect to a TCP server (returns socket or #f)
13(define sock (tcp-connect "example.com" 80))
15;; Send an HTTP request
16(socket-write-line sock "GET / HTTP/1.0")
17(socket-write-line sock "")
19;; Read the full response
20(display (socket-read-all sock))
21(socket-close sock)
22```
24`call-with-tcp-connection` ensures the socket is closed when the procedure returns:
26```scheme
27(call-with-tcp-connection "example.com" 80
28 (lambda (sock)
29 (socket-write-line sock "GET / HTTP/1.0")
30 (socket-write-line sock "")
31 (socket-read-all sock)))
32```
34## TCP Server
36```scheme
37;; Listen on a port (0 = OS-assigned)
38(define server (tcp-listen 8080))
40;; Accept loop
41(let loop ()
42 (let ((client (tcp-accept server)))
43 (socket-write-line client "Hello!")
44 (socket-close client)
45 (loop)))
46```
48`tcp-listen` accepts an optional backlog argument (default 5). Pass port `0` for an OS-assigned port, then inspect it with `socket-local-address`.
50`call-with-tcp-server` wraps the listen/close lifecycle:
52```scheme
53(call-with-tcp-server 0
54 (lambda (server)
55 (let ((client (tcp-accept server)))
56 (socket-write-line client "Welcome")
57 (socket-close client))))
58```
60## Unix Domain Sockets
62Connect to a Unix domain socket path. Not available on Windows.
64```scheme
65(define sock (unix-connect "/var/run/app.sock"))
66(socket-write-line sock "STATUS")
67(display (socket-read-line sock))
68(socket-close sock)
69```
71## Reading and Writing
73### socket-read
75Read up to `max-bytes` (default 4096) from a socket. Returns a string, empty string if no data is available in non-blocking mode, or `eof-object` when the connection is closed.
77```scheme
78(socket-read sock) ; read up to 4096 bytes
79(socket-read sock 1024) ; read up to 1024 bytes
80```
82### socket-read-line
84Read a single line (up to the next newline character).
86```scheme
87(socket-read-line sock) ; => "HTTP/1.0 200 OK"
88```
90### socket-write
92Write a string or bytevector. Returns the number of bytes written, or `#f` on error.
94```scheme
95(socket-write sock "Hello") ; => 5
96(socket-write sock #u8(1 2 3)) ; => 3
97```
99### socket-write-line
101Write a string followed by CRLF (`\r\n`).
103```scheme
104(socket-write-line sock "GET / HTTP/1.0")
105```
107### socket-read-all
109Read all data until EOF, returning the accumulated string.
111```scheme
112(define response (socket-read-all sock))
113```
115### socket-send-all
117Send all data, retrying on partial writes. Returns `#t` on success.
119```scheme
120(socket-send-all sock large-string) ; => #t
121```
123### make-line-reader
125Create a buffered line reader that handles fragmented input and CRLF line endings. Returns a procedure that yields one complete line per call.
127```scheme
128(define reader (make-line-reader sock))
129(reader) ; => "first line"
130(reader) ; => "second line"
131(reader) ; => eof-object (connection closed)
132```
134## UDP
136```scheme
137;; Create and bind a UDP socket
138(define sock (udp-socket))
139(udp-bind sock 5000)
141;; Send a datagram (sock data host port)
142(udp-send sock "ping" "127.0.0.1" 5000)
144;; Receive a datagram — returns (data host port)
145(let ((result (udp-receive sock)))
146 (display (car result))) ; => "ping"
148(socket-close sock)
149```
151`udp-bind` accepts an optional host argument: `(udp-bind sock port host)`.
153`udp-receive` accepts an optional max-bytes argument (default 65535).
155## Non-blocking I/O
157```scheme
158;; Enable non-blocking mode
159(socket-set-non-blocking! sock #t)
160(socket-non-blocking? sock) ; => #t
162;; Check if data is available without blocking
163(socket-ready? sock) ; => #t or #f
164```
166### socket-select
168Wait for activity on multiple sockets. Returns `(readable writable)` — two lists of ready sockets.
170```scheme
171(let* ((result (socket-select (list sock1 sock2) '() 1000))
172 (readable (car result))
173 (writable (cadr result)))
174 (for-each handle-data readable))
175```
177The third argument is a timeout in milliseconds.
179## Socket Info
181```scheme
182;; Type predicate
183(socket? sock) ; => #t
184(socket-closed? sock) ; => #f
186;; Local address — returns (host port)
187(socket-local-address sock) ; => ("0.0.0.0" 8080)
189;; Remote address — returns (host port)
190(socket-remote-address sock) ; => ("127.0.0.1" 54321)
192;; DNS resolution — returns list of IP strings
193(resolve-hostname "example.com") ; => ("93.184.216.34" ...)
195;; Local machine hostname
196(gethostname) ; => "my-computer"
197```
199## Options
201Disable Nagle's algorithm for lower latency on interactive protocols:
203```scheme
204(socket-set-tcp-nodelay! sock #t)
205```
207## Common Patterns
209### TCP Echo Server
211```scheme
212(import (sigil socket))
214(define (echo-server port)
215 (call-with-tcp-server port
216 (lambda (server)
217 (let loop ()
218 (let* ((client (tcp-accept server))
219 (reader (make-line-reader client)))
220 (let read-loop ()
221 (let ((line (reader)))
222 (cond
223 ((eof-object? line) (socket-close client))
224 (else (socket-write-line client line)
225 (read-loop)))))
226 (loop))))))
227```
229### Line-based Protocol Client
231```scheme
232(import (sigil socket))
234(call-with-tcp-connection "localhost" 6379
235 (lambda (sock)
236 (socket-write-line sock "PING")
237 (let ((reader (make-line-reader sock)))
238 (display (reader))))) ; => "+PONG"
239```
241### UDP Messaging
243```scheme
244(import (sigil socket))
246;; Sender
247(let ((sock (udp-socket)))
248 (udp-send sock "hello" "127.0.0.1" 9000)
249 (socket-close sock))
251;; Receiver
252(let ((sock (udp-socket)))
253 (udp-bind sock 9000)
254 (let ((msg (udp-receive sock)))
255 (display (car msg)) ; data
256 (display (cadr msg)) ; sender host
257 (display (caddr msg))) ; sender port
258 (socket-close sock))
259```