Reputation: 71
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)
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
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
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