En SOCKS5-server skriver fire byte separat, og klienten virker først, når disse skrivninger er samlet. Samling kan ændre timingen, men TCP lover stadig ikke én læsning per skrivning.

Klienten har brug for en svarparser. Efter metodevalg og eventuel krævet godkendelse begynder et SOCKS5-kommandosvar med fire byte: version, resultat, reserveret byte og adressetype. Den resterende længde afhænger af den adressetype.

En .NET 7+-parser, der kun skal forbruge et vellykket CONNECT-svar, kan bruge:

static async Task ReadConnectReply(Stream stream, CancellationToken ct)
{
    byte[] prefix = new byte[4];
    await stream.ReadExactlyAsync(prefix, ct);
    if (prefix[0] != 5 || prefix[2] != 0)
        throw new IOException("Invalid SOCKS5 reply");
    if (prefix[1] != 0)
        throw new IOException($"SOCKS5 request failed: {prefix[1]}");

    int remaining;
    switch (prefix[3])
    {
        case 1: remaining = 4 + 2; break;
        case 4: remaining = 16 + 2; break;
        case 3:
            byte[] length = new byte[1];
            await stream.ReadExactlyAsync(length, ct);
            remaining = length[0] + 2;
            break;
        default:
            throw new IOException("Unknown SOCKS5 address type");
    }
    await stream.ReadExactlyAsync(new byte[remaining], ct);
}

De sidste to byte er den bundne port. IPv4 kræver fire byte; IPv6 kræver seksten; en domæneadresse bærer en længdebyte før navnet.

Angiv et annulleringstoken med den tilsigtede forhandlingsfrist. ReadExactlyAsync håndterer kortere enkeltlæsninger og kaster en undtagelse, når strømmen slutter før den ønskede længde. Ved fejl skal den mislykkede forbindelse kasseres via ejerens oprydningsvej.

Ved succes skal du stoppe ved svargrænsen. Bytes efter den tilhører destinationsprotokollen. En større spekulativ læsning skal bevare overskydende byte i stedet for at kaste begyndelsen af applikationsstrømmen væk.

Relaterede artikler