SOCKS5 სერვერი ოთხ ბაიტს ცალ-ცალკე წერს, და კლიენტი მუშაობს მხოლოდ მას შემდეგ, რაც ეს ჩაწერები გაერთიანდება. გაერთიანებამ შეიძლება დრო შეცვალოს, მაგრამ TCP კვლავ არ გვპირდება ერთ წაკითხვას თითოეულ ჩაწერაზე.

კლიენტს სჭირდება პასუხის პარსერი. მეთოდის არჩევისა და საჭირო ავთენტიფიკაციის შემდეგ SOCKS5 ბრძანების პასუხი იწყება ოთხი ბაიტით: ვერსია, შედეგი, დაცული ბაიტი და მისამართის ტიპი. დარჩენილი სიგრძე დამოკიდებულია ამ მისამართის ტიპზე.

.NET 7+ პარსერს, რომელსაც მხოლოდ წარმატებული CONNECT პასუხის მოხმარება სჭირდება, შეუძლია გამოიყენოს:

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);
}

ბოლო ორი ბაიტი დაკავშირებული პორტია. IPv4-ს სჭირდება ოთხი ბაიტი; IPv6-ს სჭირდება თექვსმეტი; დომენის მისამართი სახელის წინ სიგრძის ბაიტს ატარებს.

მიაწოდეთ გაუქმების ტოკენი განზრახული მოლაპარაკების ვადით. ReadExactlyAsync ამუშავებს უფრო მოკლე ცალკეულ წაკითხვებს და ისვრის გამონაკლისს, როდესაც ნაკადი მოთხოვნილ სიგრძემდე ადრე მთავრდება. წარუმატებლობისას გადააგდეთ წარუმატებელი კავშირი მისი მფლობელის გასუფთავების გზით.

წარმატებისას გაჩერდით პასუხის საზღვარზე. მის შემდეგ ბაიტები დანიშნულების პროტოკოლს ეკუთვნის. უფრო დიდი სპეკულაციური წაკითხვა უნდა ინახავდეს ზედმეტ ბაიტებს და არა აპლიკაციის ნაკადის დასაწყისის გადაგდებას.

დაკავშირებული სტატიები