I'm writing a TCP server to open a port and talk to some hardware that just sends string data as a byte array.
The environment is in Unity so I went with async callbacks to avoid blocking the program. This seems to work okay, connections are valid, the correct data comes across, I can send messages to the hardware, but the socket buffer never clears. Data just stacks up and does not empty out when I do Socket.EndReceive(ar)
.
How does the async loop work? I don't understand why this code is not completing the loop and clearing the buffer. I've spent quite a bit of time trying to understand the process and can't figure out why this code shouldn't work.
protected TcpListener ListenServer;
protected Socket SimNetSocket;
protected byte[] ReadBuffer = new byte[1024];
protected string MessageBuffer;
[....] [....]
public void BeginReceive()
{
SimNetSocket.BeginReceive(ReadBuffer, 0, ReadBuffer.Length, SocketFlags.None, EndReceive, null);
}
protected void EndReceive(IAsyncResult async)
{
string msg = "";
try { msg = ByteArrayToString(ReadBuffer); }
catch (Exception e) { Debug.LogError(e); }
Debug.Log("RAW RECEIVE: " + msg);
MessageBuffer += msg;
ReadBuffer = new byte[1024];
SimNetSocket.EndReceive(async);
BeginReceive();
}
MessageBuffer
is a stack which is cleared later in the Update loop where the messages are processed and unrelated to the problem of the ReadBuffer
compounding on the socket.
Again, connections are valid (code not shown) and communication is definitely working. I can see data successfully coming and going from both sides but I don't have any control over what the other end hardware is doing. Does it require some implementation to receive these calls and confirm the buffer can clear?
What I actually see is the hardware sending a message, then later another message which gets stacked on it's last message, again, and again, and again. I'm processing every message through the code above, though. So I'm rather confused.
It is not clear what you expect to happen. Nothing in the Winsock API, nor the thin .NET layer over that API, would "clear" any buffer you provide it. All the API does is copy bytes from read operations into a buffer, or copy bytes for write operations from a buffer.
Looking at your EndReceive()
callback, you do seem to have misunderstood some aspects of this. You are processing the ReadBuffer
contents before you have even concluded the read operation (by calling EndReceive()
), and you are not doing anything to take into account the actual number of bytes received. Without a good Minimal, Complete, and Verifiable code example to start with, it's impossible to know for sure what your code should be doing, but a better implementation of your method would look something like this:
protected void EndReceive(IAsyncResult async)
{
try
{
int byteCount = SimNetSocket.EndReceive(async);
// For example (you didn't share ByteArrayToString(), so it's not clear
// what encoding you're using, or if you're even processing the bytes
// correctly. Feel free to modify as needed...just make sure you take
// into account the byteCount value!
string msg = Encoding.ASCII.GetString(ReadBuffer, 0, byteCount);
Debug.Log("RAW RECEIVE: " + msg);
MessageBuffer += msg;
// There is no need to allocate a new buffer. Just reuse the one you had
BeginReceive();
}
catch (IOException e)
{
// Don't catch all exceptions. Only exceptions that should be expected
// here would be IOException. Other unexpected exceptions should be left
// unhandled
Debug.LogError(e);
// You should close the socket here. Don't try to use that connection again
}
}
Note that you can in fact handle encodings other than ASCII, without having to worry about partial reads. To do that, you have to keep track of character decoding state from one read operation to the next. The easiest way to do that is to use a Decoder
object, which has an internal buffer that will hold on to partial characters until you perform your next decode operation.