Standard Library
class TcpStream
A connected network socket that reads and writes text.
since 0.1.0-alpha.1linux
Overview
A TcpStream wraps the descriptor of a connected socket and presents it as events on the loop. Reading is event driven: onData registers a callback that receives each chunk as it arrives, and when the other side closes the connection the onClose callback runs and reading stops, so the program can exit. Writing with send or << never blocks and never silently loses part of a message: whatever the operating system cannot take at once is queued and written as room appears. flush waits until the queue is empty.
Streams come from TcpListener (one per accepted client) or are built from a descriptor obtained with connectTimeout. Text is the unit of transfer.
Description
A TcpStream wraps one connected socket. Reading is event-driven; writing is queued.
- Create one from a connected descriptor:
TcpStream(fd). The usual sources arestd::connectTimeout, which gives you a descriptor after a non-blocking connect, andTcpListener.connections, which hands you ready-madeTcpStreams for accepted clients. TheINetcapability (seelang.capability-interfaces) can also open one for you. send(text)and<<send text.<<returns the stream, so sends chain. A send never silently loses data: the socket is non-blocking, so whatever the kernel cannot take at once is queued and delivered as space appears, in order. If the peer is gone, the queue is dropped and the read side reports the close; sends never throw.onData(callback)starts reading. The callback receives each chunk of text as it arrives. Chunk boundaries are whatever the network delivered, not messages; join them yourself.onClose(callback)runs once when the peer closes the connection. After that the stream stops reading, so the program is no longer waiting on it.flush()suspends the current task until every queued byte has been handed to the transport. It returnstruewhen the queue is empty, andfalseif the stream was closed or a write failed first. It is a barrier on the local queue, not a confirmation from the peer. Call it before closing or reusing a descriptor; ordinary sends never need it.close()releases the socket. It is idempotent, and a stream that has been closed ignores later sends instead of writing to some other connection that reused the descriptor number.TcpStreamdoes not implementIDisposable, so it cannot be used withusing; callclose()yourself.rawFd()is the underlying descriptor, which is what secure connections need (seelang.tls).
A stream with an onData callback keeps the program alive until the peer closes it or you call
close(); see lang.event-loop.
Rules
- Data is text. Binary payloads are not supported yet.
- Chunks are arbitrary slices of the byte stream; never assume one chunk is one message.
- Sends are queued, so a program that sends and then immediately closes should
flush()first, or the tail of a large payload may be discarded. - Writes through a closed stream are ignored.
- A
TcpStreamcannot be handed to a worker thread; every thread opens its own. Seelang.threads.
Examples
Connecting with a deadline and reading the reply. The same stream type serves clients and servers:
std::connectTimeout("example.com", 80, 2000, (fd) => {
if (fd < 0) {
console.writeln("could not connect");
return;
}
TcpStream conn = TcpStream(fd);
conn.onData((chunk) => console.writeln("received ${chunk.length()} bytes"));
conn.onClose(() => console.writeln("peer closed"));
conn << "GET / HTTP/1.0\r\n\r\n";
});
Notes
For HTTP, use HttpClient and HttpServer, which are built on TcpStream and handle framing for
you.
Examples
Reading a reply from a server
std::connectTimeout("192.0.2.10", 8080, 3000, (fd) => {
if (fd < 0) {
console.writeln("could not connect");
} else {
TcpStream s = TcpStream(fd);
s.onData((chunk) => { console.writeln("received ${chunk.length()} bytes"); });
s.onClose(() => { console.writeln("server closed the connection"); });
s << "hello\n";
}
});
Echoing every chunk back to the client
void echo(TcpStream client) {
client.onData((chunk) => { client << chunk; });
client.onClose(() => { console.writeln("client left"); });
}
Constructors
new
new(int f)Wrap a connected socket descriptor.
The stream takes over the descriptor: close closes it. Passing a negative number gives a stream that is not connected, which ignores writes.
Parameters
- f
- The descriptor of a connected socket, for example one produced by
connectTimeout.
Methods
close
close() -> voidClose the connection.
Closing is idempotent: a second call, or a call on a stream that was never connected, does nothing. Data still waiting to be written is discarded, a pending flush finishes with false, and the read and close callbacks are released, so they are not called afterwards. Writes after close are ignored.
flush
flush() -> boolWait until everything queued for writing has been handed to the operating system.
The call suspends the current task, not the whole program. It is a barrier on the local queue, not an acknowledgement from the other side: when it returns true the bytes have been accepted by the system, which is what you need before closing or reusing the descriptor. It returns false if the stream is closed, was never connected, or a write failed because the peer is gone. Plain send stays queueing; only callers that use flush wait.
Returns
true once nothing is left to write, false when the stream is closed or a write failed.
onClose
onClose(() => void cb) -> voidRegister a function to call when the other side closes the connection.
It is called once, when the end of the input is reached. A local call to close does not trigger it.
Parameters
- cb
- The function to call when the peer closes.
onData
onData((string) => void cb) -> voidStart reading and call a function with each chunk of text that arrives.
Chunks are delivered on the event loop, up to 4096 bytes at a time, and the boundaries of the chunks do not match the boundaries of what the other side sent. Registering a callback again replaces the previous one. When the other side closes the connection reading stops and the onClose callback runs.
Parameters
- cb
- The function to call with each chunk.
rawFd
rawFd() -> intGet the underlying descriptor.
The descriptor is needed to start TLS on the connection, which works in place on the same descriptor. After close it is -1.
Returns
The socket descriptor, or -1 once the stream is closed.
send
send(string s) -> voidSend text to the other side.
The call returns at once and never throws. If the operating system cannot take all of the text immediately, the rest is queued and written as the connection makes room, so a large message is never cut short. If the peer has gone away the queued data is dropped, and the reading side reports the close. On a stream that is closed or was never connected, send does nothing.
Parameters
- s
- The text to send.
Operators
<<
<<(string s) -> TcpStreamSend text and return the stream, so writes can be chained.
This is send with a result: stream << "a" << "b" writes both parts in order.
Parameters
- s
- The text to send.
Returns
The same stream.
See also
- TcpListener — A listening socket that delivers each incoming connection as a
TcpStream. - connectTimeout — Connect to a TCP server, giving up after a time limit.
- HttpClient — A simple HTTP client that sends one request per connection and delivers the response to a callback.
- InStream — The reading end of a stream: a queue of values of type
Tthat something else produces.