Newb problems with tcp clients

Networking Newbie here. I want to send something to a server, but have heard conflicting information about if its possible to send something without closing the stream.

Code:

const stream = try std.net.tcpConnectToHost(allocator, "www.example.org", 1234);
defer stream.close();

for (1..4) {
    try stream.writeAll("whooo");
    // this is where i, if its possible, want to send the "whooo", but I cant
    // find a way to do so, without reopening the stream every single time
    // inside this loop
}
// here, at the end of the scope, is where the all the "whooo"s are actually sent

I dont think that Ive phrased this very well, but am struggling to do so. If something is unclear, please ask.

EDIT: changed port num from 0x0 to 1234 for convetions sake

I’m not sure why do you think that you can’t send a message without reopening the stream.

Also, it is wrong to say that all the messages are actually sent at the end of the scope. Simply, the kernel choose when to send the data.

Another advice is to not use the hex base for the port number; instead use the decimal base.

Okay, thank you, this is very valuable information already. (More than my informatics teacher could provide lol). I didnt know the kernel decides this.
Is there a way to manually tell the kernel to send?

See Nagle's algorithm - Wikipedia for the reason why your messages are not sent as soon as possible.

You can use the TCP_NODELAY socket option to disable it.

1 Like

Thank you very much. Could you provide a code sample?

@mperillo This is TCP client sample.

A host is echo server built with rust (I/O | Tokio - An asynchronous Rust runtime).

const std = @import("std");

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    const cli = try std.net.tcpConnectToHost(allocator, "127.0.0.1", 6142);
    defer cli.close();

    for (1..4) |_| {
        try cli.writeAll("Wooo");
        
        var buf: [1024]u8 = undefined;

        const len = try cli.read(&buf);

        std.debug.print("length: {}\n", .{len});
        std.debug.print("res: {s}\n", .{buf[0..len]});
    }
}

And to disable Nagle, something like:

const empty: c_int = 0;
try std.os.setsockopt(stream.handle, std.os.SOL.SOCKET, std.os.TCP.NODELAY, std.mem.asBytes(&empty));

Before using advanced features like TCP_DELAY I think it is better to describe what you want to do.

I want to be able to in real time, in order just save messages coming in in an arraylist.

In school we are learning tcp and me using zig instead of java am now struggling with my messages only sending when either closing the stream or the os flushing, which i dont know how to invoke by myself

The project is to just make an unencrypted tcp based chat app, so i wanna save everything in the order its been sent

It is os.IPPROTO.TCP, not os.SOL.SOCKET. The former returns Permission Denied when TCP_NODELAY is set to 1.

1 Like

Ok; in this case it makes sense to set TCP_NODELAY.

Here is an example, with some options so that you can experiment with it:

const std = @import("std");
const debug = std.debug;
const heap = std.heap;
const mem = std.mem;
const net = std.net;
const os = std.os;
const time = std.time;

pub fn main() !void {
    var arena = heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    const stream = try net.tcpConnectToHost(allocator, "127.0.0.1", 7777);
    defer {
        stream.close();
        debug.print("stream.close\n", .{});
    }

    // Options
    const msg_num: usize = 4;
    const msg_delay = time.ns_per_s * 1;
    const close_delay = time.ns_per_s * 3;
    const tcp_nodelay: c_int = 0;

    try std.os.setsockopt(stream.handle, os.IPPROTO.TCP, os.TCP.NODELAY, mem.asBytes(&tcp_nodelay));
    for (0..msg_num) |_| {
        try stream.writeAll("W" ** msg_num ++ "\n");
        debug.print("writeAll()\n", .{});
        time.sleep(msg_delay);
    }
    time.sleep(close_delay);
}

I tried to test the code using netcat -l -p 7777 as the server, but it seems that on localhost the messages are received without delays.

1 Like

The issue was a broken server.

The server was using std.io.Reader.readAll which waits for EOF. My client was only sending EOF upon closing. The solution was to let the server use read, that fixed it all.

readAll does not wait for EOF, but tries to read all the data as specified by the buffer length, unless the socket is closed by the remote peer. The difference between read and readAll is that read may return less bytes.

1 Like

Okay, thanks