Search code examples
c#tcp

C# `BinaryReader.ReadBoolean` stuck


Client side:

Something like my code:

var socket = Connect();
var reader = new BinaryReader(socket.GetStream());
var writer = new BinaryWriter(socket.GetStream());

// In other thread
writer.Write((byte) 5);
writer.Write("C:\\no-file.exe");
var exists = reader.ReadBoolean();

Stuck in System.Net.Sockets.Socket.Receive:

// Decompiled with JetBrains decompiler
UnsafeNclNativeMethods.OSSOCK.recv(this.m_Handle.DangerousGetHandle(), numPtr + offset, size, socketFlags);

I can see packet with my boolean value in WireShark, but ReadBoolean still executing and socket.Available equals zero.

Server prototype:

Minimal code that acts like a real server and causes the same problem.

var server = new TcpListener(IPAddress.Any, 2386);
server.Start();

var socket = server.AcceptTcpClient();
socket.NoDelay = true;
Console.WriteLine("Connected: " + socket.Client.RemoteEndPoint);

var stream = socket.GetStream();
var reader = new BinaryReader(stream);
var writer = new BinaryWriter(stream);

while (socket.Connected) {
    while (!stream.DataAvailable);

    if (stream.ReadByte() != 5) {
        break;
    }

    var path = reader.ReadString();
    Console.WriteLine("Requested file " + path);

    var exists = File.Exists(path);
    writer.Write(exists);

    writer.Flush();
    Console.WriteLine(exists ? "File exists" : "Not found");
}

Console.WriteLine("Disconnected: " + socket.Client.RemoteEndPoint);

Solution

  • I found in my code that I have other thread that stole my boolean.

    // TcpClient thread inside Connect method
    while (client.Connected) {
        DispatchPacketType(stream.ReadByte());
    }
    
    // Thread that calls Connect
    reader.ReadBoolean(); // stuck!
    

    EDIT:

    So I made WaitPacket method to fix this problem. Not the best solution, but this works.

    Making special waiting method was completely wrong, because it caused a lot of problems. The only one normal decision is project refactoring.

    In same case you can try:

    1. Refuse of while loop for reading packets (DispatchPacketType). Now client sends packet's type byte, its contents and reads server reply in same method (ex. FetchSomeData), because server (in my situation) responds sequentially.
    2. Avoid interaction with the same TCPClient from multiple threads or use lockers as mentioned @500-Internal Server Error in his comment. I rewrote many lines of code and now it works more safier and performant.