Bittorrent, peer handshake response is painfully slow. #ca4

Hi, I already completed the Peer Handshake stage however I noticed I have to wait around 2 minutes for the peer to response.
Is this some normal behaviour or am I doing something wrong?

pd: im doing it in zig and these are the logs from my program

> timeit { zbr -- handshake sample.torrent 165.232.38.164:51433 }
info: Parsed file sample.torrent
info: Created handshake struct
info: Peer ip: 165.232.38.164:51433
info: Trying to connect to peer...
info: Connected to peer
info: Sending handshake to peer...
info: Waiting for response...
info: Got a response from peer
info: Peer ID: 2d524e302e302e302d9d5e0226875575c6233a13
2min 108ms 115µs 598ns

and here is a link to the source code from github.

UPDATE: Okai so I looked for some other people solution and converted the handshake struct into a extern struct and just used try writer.writeStruct(handshake); and try reader.readStruct(HandShake) and the times went to 75ms which good but i have no idea why does this happen??

1 Like

Hey @Direwolfesp, ming sharing what you’re using before the conversion?

Sure,
before:

.handshake => {
            // . . . . .

            std.log.info("Trying to connect to peer...", .{});
            var connection = try std.net.tcpConnectToAddress(addr);
            std.log.info("Connected to peer", .{});
            const writer = connection.writer();
            const reader = connection.reader();

            std.log.info("Sending handshake to peer...", .{});
            try handshake.dumpToWriter(writer);
            std.log.info("Waiting for response...", .{});
            const response: []u8 = try reader.readAllAlloc(allocator, std.math.maxInt(usize));
            defer allocator.free(response);
            std.log.info("Got a response from peer ", .{});
            const resp_handshake = HandShake.createFromBuffer(response);
            const peer_id = std.fmt.fmtSliceHexLower(&resp_handshake.peer_id);
            std.log.info("Peer ID: {s}", .{peer_id});
        },

after (correct):

        .handshake => {
            // . . . . .

            // connect to peer
            std.log.info("Trying to connect to peer...", .{});
            var connection = try std.net.tcpConnectToAddress(addr);
            std.log.info("Connected to peer", .{});
            const writer = connection.writer();
            const reader = connection.reader();

            // send and receive handshake
            std.log.info("Sending handshake to peer...", .{});
            try writer.writeStruct(handshake);
            std.log.info("Waiting for response...", .{});
            const resp_handshake = try reader.readStruct(HandShake);
            std.log.info("Got a response from peer ", .{});
            const peer_id = std.fmt.fmtSliceHexLower(&resp_handshake.peer_id);
            std.log.info("Peer ID: {s}", .{peer_id});

also declared the handshake as a extern struct:

pub const HandShake = extern struct {
    // layout matters
    pstrlen: u8 align(1) = 19,
    pstr: [19]u8 align(1) = "BitTorrent protocol".*,
    reserved: [8]u8 align(1) = std.mem.zeroes([8]u8),
    info_hash: [20]u8 align(1) = undefined,
    peer_id: [20]u8 align(1) = "-qB6666-weoiuv8324ns".*,
  // . . . . .
};

Okai, i also tried using:

var a = try reader.readBytesNoEof(@sizeOf(HandShake));
const resp_handshake = HandShake.createFromBuffer(&a);

and seems to also work, so the problem had to be:

const response: []u8 = try reader.readAllAlloc(allocator, std.math.maxInt(usize));

which might be allocating to many wasted memory.

@Direwolfesp readAllAlloc internally calls readAllArrayList, which reads until the end of the stream. This behavior makes it unsuitable for use with TCP streams.

Since TCP streams remain open until explicitly closed by the peer, this approach can block indefinitely or lead to unexpected hangs.

1 Like

oh that has sense, thank you very much ^^

1 Like