(sigil socket)

Network Socket Library

TCP and UDP socket operations for network programming. Supports both blocking and non-blocking I/O for use with coroutines and event loops.

TCP Client

(import (sigil socket))

(let ((sock (tcp-connect "example.com" 80)))
  (socket-write-line sock "GET / HTTP/1.0")
  (socket-write-line sock "")
  (display (socket-read-all sock))
  (socket-close sock))

TCP Server

(let ((server (tcp-listen 8080)))
  (let loop ()
    (let ((client (tcp-accept server)))
      (socket-write-line client "Hello!")
      (socket-close client)
      (loop))))

UDP

(let ((sock (udp-socket)))
  (udp-send sock "127.0.0.1" 5000 "Hello")
  (socket-close sock))

Exports

socket-pairprocedure

Return two connected nonblocking local stream sockets, or #f if unavailable. Both descriptors are closed on exec. Currently POSIX only.

socket?procedure

Check if a value is a socket object.

(socket? (tcp-connect "localhost" 80))  ; => #t
(socket? "not a socket")                ; => #f
tcp-connectprocedure

Connect to a TCP server.

Returns a socket on success, or #f on failure.

(tcp-connect "example.com" 80)  ; => #<socket>

Connect to a TCP server from a specific local SOURCE address.

The local end of the socket is bound to source-ip before connecting, so the peer observes the connection as coming from that address. Only the address is pinned: the kernel still picks the source port, so rapid reconnects cannot fail on TIME_WAIT.

source-ip must be a numeric address that is local to this machine. Resolved destination addresses of a different family from source-ip are skipped. Returns #f if no candidate could be connected, which includes the case where source-ip is well-formed but not an address this machine holds, since that fails at bind.

(tcp-connect-from "127.0.0.1" 8080 "127.0.0.2")  ; => #<socket>

KNOWN SHARP EDGE, disclosed rather than fixed because this procedure has never been executed (see the note at the top of test/test-socket.sgl). source-ip is not validated here; its family is inferred from whether it contains a colon. So a source that is not an address at all reaches the primitive and RAISES when the resolver returned IPv4 candidates, but is silently skipped and yields #f when it happens to contain a colon and the resolver returned only IPv4. One kind of caller error, two shapes, chosen by the garbage's punctuation and by DNS. Validate source-ip before calling if you need one shape.

tcp-listenprocedure

Create a listening TCP server socket.

Binds to the specified port and begins listening for connections.

(tcp-listen 8080)  ; => #<socket>
tcp-acceptprocedure

Accept an incoming connection on a listening socket.

Blocks until a client connects. Returns a new socket for communicating with the client.

(let ((client (tcp-accept server)))
  (socket-write-line client "Hello!"))
udp-socketprocedure

Create a UDP socket.

(udp-socket)  ; => #<socket>
udp-bindprocedure

Bind a UDP socket to a local port.

(udp-bind sock 5000)
udp-sendprocedure

Send data via UDP to a host and port.

(udp-send sock "127.0.0.1" 5000 "Hello")
udp-receiveprocedure

Receive data from a UDP socket.

Returns a list (data host port) or #f on error.

socket-closeprocedure

Close a socket.

(socket-close sock)

Check if a socket has been closed.

(socket-closed? sock)  ; => #t or #f
socket-readprocedure

Read data from a socket.

Returns a string, empty string (no data in non-blocking mode), or eof-object when connection is closed.

Read raw bytes from a socket into a bytevector.

Unlike socket-read (which returns a UTF-8 string), this preserves raw bytes without any encoding interpretation. Essential for binary protocols and file downloads.

Returns a bytevector, empty bytevector (no data in non-blocking mode), or eof-object when connection is closed.

Read a line from a socket (up to newline).

socket-writeprocedure

Write data to a socket.

Returns the number of bytes written, or #f on error.

Write a line to a socket (appends newline).

Set a socket to non-blocking mode.

(socket-set-non-blocking! sock #t)

Check if a socket is in non-blocking mode.

socket-ready?procedure

Check if a socket has data ready to read.

Returns #t if data is available without blocking.

socket-selectprocedure

Wait for activity on multiple sockets.

Returns a list of sockets that are ready.

Disable Nagle's algorithm for lower latency.

(socket-set-tcp-nodelay! sock #t)
gethostnameprocedure

Get the local hostname.

(gethostname)  ; => "my-computer"

Resolve a hostname to IP addresses.

(resolve-hostname "localhost")  ; => ("127.0.0.1")

Get the local address and port of a socket.

Returns (host . port).

Get the remote address and port of a connected socket.

Returns (host . port).

unix-connectvariable

Connect to a Unix domain socket.

Returns a socket on success, or #f on connection failure. Backlog saturation parks only the calling fiber inside with-async. timeout-ms: is a finite nonnegative real millisecond limit, or #f (unlimited). Expiry raises "unix-connect: connection timed out". An exception cancelling the operation closes its unfinished socket and propagates. Successful sockets use blocking I/O, like tcp-connect. Not available on Windows or web/WASI builds.

(unix-connect "/var/run/docker.sock")  ; => #<socket>
unix-listenprocedure

Create a listening Unix domain socket.

Binds to the specified path and begins listening for connections. Removes any stale socket file at the path before binding. Use tcp-accept to accept incoming connections. Not available on Windows.

(unix-listen "/tmp/my-server.sock")  ; => #<socket>

Connect to a TCP server, call proc with the socket, then close it.

Ensures the socket is closed even if an error occurs.

(call-with-tcp-connection "example.com" 80
  (lambda (sock)
    (socket-write-line sock "GET / HTTP/1.0")
    (socket-read-all sock)))

Create a TCP server, call proc with the listening socket, then close it.

Useful for one-shot server scenarios or tests.

(call-with-tcp-server 8080
  (lambda (server)
    (let ((client (tcp-accept server)))
      (socket-write-line client "Hello!")
      (socket-close client))))

Send all data to a socket, retrying on partial writes.

Returns #t on success, #f on failure.

(socket-send-all sock "Hello, world!")  ; => #t

Read all available data from a socket until EOF.

Returns the accumulated string, or #f on error.

(socket-read-all sock)  ; => "HTTP/1.0 200 OK\r\n..."

Create a buffered line reader for a socket.

Returns a procedure that reads complete lines from the socket, buffering partial lines internally. Useful for line-based protocols where data may arrive in fragments.

(define read-line (make-line-reader sock))
(read-line)  ; => "hello" or #f if no complete line yet
(read-line)  ; => "world"

(No description)

(No description)

(No description)

(No description)

(No description)

(No description)

(No description)

(No description)

(No description)

fd-selectvariable

(No description)

socket-fdvariable

(No description)

(No description)

(No description)

(No description)

(No description)