Max N
Max N

Reputation: 71

TCP Client working with BeginRead DataReturned is NULL after first Response of given Bytes

I am struggling with the C# TCP Client BeginRead Method.

I am communicating with a machine as Server.

Since the machine sends push packets when a button is pressed, I need to listen all the time. I am doing that asynchronously via BeginRead(..).

To the first request sent to the machine (10 Bytes), the Response is correctly read (3 Bytes). If i repeat some other kind of request, the BeginRead just returns an Array of 00 Bytes (Lenght 1024).

The Wireshark Screenshots prove that this is a problem of my c# application.

The Push Messages from the machine aren't working after that second request too.

internal void StartRecieveContinuous()
{
    Open();

    int dataLenght = 1024;

    this.LastRecievedData = new byte[dataLenght];

    NetworkStream stream = Client?.GetStream() ?? throw new Exception("The TCP Client wasn't initialized!");

    stream.BeginRead(LastRecievedData, 0, dataLenght, CallbackDataRecieved, stream);

    ListenerRunning = true;
}

private void CallbackDataRecieved(IAsyncResult ar)
{
    ListenerRunning = false;

    // Debug
    System.Diagnostics.Debug.WriteLine(string.Join(" ",this.LastRecievedData.Take(20).Select(x => x.ToString("X"))));
    // Debug

    if((this.LastRecievedData?.Count(x => x > 0) ?? 0) > 0)
    {
        EventDataRecieved?.Invoke(this, new DataRecievedArgs(this.LastRecievedData.Take(this.LastRecievedData?.Length ?? 0).ToArray()));
    }

    StartRecieveContinuous();
}

Debug Console Logs (hex):

5 1 80 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 (First Response)

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 (Second Response)

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 (Button Press Event Data)

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 (Button Release Event Data)

Request to Maschine

Response from Machine

Edit:

I tried using endRead but my problem remains:

private void CallbackDataRecieved(IAsyncResult ar)
{
    NetworkStream myNetworkStream = (NetworkStream)ar.AsyncState;

    myNetworkStream.EndRead(ar);

    ListenerRunning = false;

    System.Diagnostics.Debug.WriteLine(">>> " + string.Join(" ", this.LastRecievedData.Take(20).Select(x => x.ToString("X"))));

    if((this.LastRecievedData?.Count(x => x > 0) ?? 0) > 0)
    {
        EventDataRecieved?.Invoke(this, new DataRecievedArgs(this.LastRecievedData.Take(this.LastRecievedData?.Length ?? 0).ToArray()));
    }

    StartRecieveContinuous();
}

Upvotes: 3

Views: 556

Answers (2)

Max N
Max N

Reputation: 71

I got the BeginRead/EndRead soltuion to work.

The problem was that I started the BeginRead again when sending a command without checking if it was allready running.

I took a look at these pipelines, but I aint switching right now. Maybe sometime later in the next project.

Thanks guys!

Upvotes: 0

Marc Gravell
Marc Gravell

Reputation: 1062840

Basically, you are required to call EndRead in the callback, and until you do that: you are in undefined behaviour land, attempting to perform multiple overlapped read operations. To clarify: EndRead doesn't cause the read operation to end; rather: BeginFoo and EndFoo are simply the two pieces of an async operation of a given pattern, that mirror a synchronous Foo call.

You are correct (comments) that EndRead simply returns the number of bytes read, but that mirrors exactly with what the non-async Read API returns; and similarly, the data is stored into the buffer that you passed in. Just like with Read, you are also required to pay attention to the integer that EndRead returns; it is the most important thing you get from a read! You should not attempt to process more than this much data from the buffer your provided.


However! There is quite honestly a lot of things to pick apart in the code shown - threading, buffer management, sync vs async operations; the TCP APIs are very hard to get write, when used manually. If you're using .NET Core / .NET 5 or above, then honestly: you can move all of this to Kestrel using the "pipelines" API, which deals with all of this scrappy code for you, so that all you need to write is your protocol code (the actual data parsing/processing code). An overview of this can be found in this multi-part series

Upvotes: 1

Related Questions