TCP

TCP is the plain-transport entrypoint in Reseau. The same connect and listen surface supports both concrete socket addresses and hostname-based string addresses. Read Name Resolution for the resolver and policy objects behind the string-address overloads.

Address Model

Use concrete addresses when you already know the exact family and endpoint you want to target. Reseau exposes both IPv4 and IPv6 address snapshots:

Reseau.TCP.SocketAddrV4Type
SocketAddrV4

IPv4 endpoint snapshot.

The address bytes are stored in presentation order and the port is stored in host byte order. Conversion to platform sockaddr structs happens lazily when a socket operation actually needs one.

source
Reseau.TCP.SocketAddrV6Type
SocketAddrV6

IPv6 endpoint snapshot.

scope_id is used for scoped link-local addresses and is preserved all the way down to the platform sockaddr representation so bind/connect can target the same interface the caller selected.

source
Reseau.TCP.any_addrFunction
any_addr(port) -> SocketAddrV4

Convenience constructor for 0.0.0.0:port, typically used for wildcard binds.

source
Reseau.TCP.any_addr6Function
any_addr6(port; scope_id=0) -> SocketAddrV6

Convenience constructor for the IPv6 wildcard bind address [::]:port.

source

Concrete addresses are especially useful when you want to bind explicitly to a family or pass a preselected local address to outbound dialing.

Connections and Listeners

The core connection surface is small and intentionally transport-focused:

Reseau.TCP.ConnType
Conn

User-facing connected TCP stream.

Reads and writes are forwarded to IOPoll, which means blocking operations are actually readiness waits against the shared low-level poller rather than thread-per-socket blocking syscalls. Because Conn <: IO, standard Base stream helpers like read, read!, readbytes!, eof, and write apply directly.

source
Reseau.TCP.ListenerType
Listener

User-facing passive TCP listener.

Accepted children are returned as Conn values whose underlying sockets are already non-blocking, poll-registered, and configured with the default TCP options Reseau wants.

source
Reseau.TCP.connectFunction
connect

Connect a TCP client using either a concrete SocketAddr or a string-address overload added later in the file load order.

source
Reseau.TCP.listenFunction
listen

Create a TCP listener from either a concrete SocketAddr or a string-address overload added later in the file load order.

source

For "host:port" dialing, the string-address overloads accept:

  • timeout_ns
  • deadline_ns
  • local_addr
  • fallback_delay_ns
  • resolver
  • policy

Those knobs feed the resolver layer described in Name Resolution, while the actual socket lifecycle still lands in the same TCP.Conn and TCP.Listener types.

Stream I/O and Lifecycle

TCP.Conn follows Julia's standard stream conventions for read!, write, and close, while still exposing explicit half-close helpers when you need them:

Base.read!Method
read!(conn, buf) -> buf

Read exactly length(buf) bytes into buf or throw EOFError.

Because Conn <: IO, Base's generic read! implementation already supports mutable byte views like @view bytes[2:5] in addition to plain vectors.

Use readbytes! or readavailable when you want a count-returning read that may stop early.

source
Base.readbytes!Method
readbytes!(conn, buf, nb=length(buf); all::Bool=true) -> Int

Read up to nb bytes into buf, returning the byte count.

Unlike read!(conn, buf), this API may return after a short read or EOF. It is the count-returning TCP read entrypoint once TCP.Conn follows Julia's IO contract.

If all is true (the default), the call keeps reading until nb bytes have been transferred, EOF is reached, or an error occurs. If all is false, at most one underlying socket read is performed.

Resizable Vector{UInt8} buffers grow when needed, matching Julia's standard readbytes! behavior. Fixed-size contiguous byte views must satisfy nb <= length(buf).

source
Base.readavailableMethod
readavailable(conn) -> Vector{UInt8}

Read and return the bytes that are currently ready without requiring a full-buffer exact read.

source
Base.eofMethod
eof(conn) -> Bool

Report whether the peer has cleanly closed the read side of the connection.

source
Base.isopenMethod
isopen(conn) -> Bool

Return true while conn still owns an open socket.

source
Base.isopenMethod
isopen(listener) -> Bool

Return true while listener still owns an open listening socket.

source
Base.writeMethod
write(conn, buf) -> Int

Write all bytes from buf and return the number of bytes written.

On success, the return value is always length(buf). If the socket cannot currently accept data, the call waits for write readiness and resumes until the entire buffer has been written or an error/deadline interrupts the operation.

source
Base.closeMethod
close(conn)

Close the connection. Repeated closes are treated as no-ops.

source
Base.closeMethod
close(listener)

Close the listening socket. Repeated closes are treated as no-ops.

source

The key contract is:

  • read!(conn, buf) fills buf or throws EOFError, just like other Julia IO types.
  • readbytes!(conn, buf, nb) and readavailable(conn) are the count-returning entrypoints when you want short-read behavior.
  • write(conn, buf) writes the full payload unless an error or deadline interrupts the operation.
  • close(conn) and close(listener) are idempotent, which keeps cleanup paths safe in finally blocks.
  • isopen(conn) and isopen(listener) report whether the underlying socket is still live.

Deadlines, Socket Options, and Address Inspection

Deadline management lives on the live connection, not in helper tasks or external timeout wrappers:

Reseau.TCP.set_deadline!Function
set_deadline!(conn, deadline_ns)

Set both read and write deadlines on conn.

  • deadline_ns is an absolute monotonic timestamp in nanoseconds, using the same clock as time_ns().
  • deadline_ns == 0 disables both deadlines.
  • deadline_ns <= time_ns() marks both sides as immediately timed out.

After the deadline is reached, blocking read!/write operations fail with DeadlineExceededError until the deadline is cleared or moved forward.

source
set_deadline!(listener, deadline_ns)

Set the accept deadline on listener.

  • deadline_ns uses the same absolute monotonic time_ns() clock as connection deadlines.
  • deadline_ns == 0 disables accept timeouts.
  • deadline_ns <= time_ns() causes the next blocking accept to time out immediately.

This affects accept(listener) only.

source
Reseau.TCP.set_read_deadline!Function
set_read_deadline!(conn, deadline_ns)

Set only the read deadline on conn.

  • Uses absolute monotonic nanoseconds (time_ns() clock).
  • deadline_ns == 0 disables read timeouts.
  • deadline_ns <= time_ns() causes read waits to time out immediately.

This affects read! wait paths only.

source
Reseau.TCP.set_write_deadline!Function
set_write_deadline!(conn, deadline_ns)

Set only the write deadline on conn.

  • Uses absolute monotonic nanoseconds (time_ns() clock).
  • deadline_ns == 0 disables write timeouts.
  • deadline_ns <= time_ns() causes write waits to time out immediately.

This affects write wait paths only.

source
Reseau.TCP.DeadlineExceededErrorType
DeadlineExceededError

Raised when blocking TCP I/O or accept(listener) exceeds the active deadline.

Catch TCP.DeadlineExceededError when a deadline set by set_deadline!, set_read_deadline!, or set_write_deadline! expires. This aliases the underlying poller timeout type so downstream code does not need to depend on Reseau.IOPoll directly.

source
Reseau.TCP.local_addrFunction
local_addr(conn) -> Union{Nothing, SocketAddr}

Return the cached local endpoint for conn, if known.

source
local_addr(listener) -> Union{Nothing, SocketAddr}

Return the listener's bound local endpoint.

This is an alias for addr(listener).

source
Reseau.TCP.remote_addrFunction
remote_addr(conn) -> Union{Nothing, SocketAddr}

Return the cached remote endpoint for conn, if known.

source
Reseau.TCP.addrFunction
addr(listener) -> Union{Nothing, SocketAddr}

Return the listener's bound local endpoint, if known.

source

Use absolute monotonic nanoseconds from time_ns() for deadline APIs. Setting a deadline to 0 clears it, while setting it to a time in the past makes the next blocking wait time out immediately.

Deadline expiry is surfaced as TCP.DeadlineExceededError. Catch that alias instead of reaching into Reseau.IOPoll directly:

try
    TCP.set_read_deadline!(conn, time_ns() + 100_000_000)
    read!(conn, buf)
catch err
    if err isa TCP.DeadlineExceededError
        TCP.set_read_deadline!(conn, 0) # clear or move the deadline before retrying
    else
        rethrow()
    end
end

Listeners use the same set_deadline! name for accept deadlines. That deadline applies only to accept(listener); established connections still use their own per-connection deadline state. local_addr(listener) is also available as a discoverable alias for addr(listener).

Where To Go Next

  • Read TLS for the TLS wrapper layer that reuses the same transport and deadline model.
  • Read Name Resolution for the advanced resolver controls that sit behind string-address dialing.
  • Read API Reference for the canonical docstrings for the entire TCP surface.