Broken TCP messages
I have a simple TCP server that communicates with some devices via GPRS. It works correctly without any problem for a long time. Nowdays there is a change in the devices (client devices, that send data) and they send lot more data than earlier and TCP message size can be 10 times bigger than earlier. Earlier it was max 1024 bytes/data message, now it can be more开发者_StackOverflow than 10 thousand bytes/message.
As I see - as the nature of TCP predicted - there are a lot of messages that are 'broken', so instead of a complete message only a part of that arrives, and later can come the other part etc.
I think my server - that is based on asynchronous pattern - handles this situation in a wrong way and I think the bug is in my RecieveCallBack function. I could not find to correct way to handle this situation. How can I do this?
Here is my RecieveCallBack function:
public void RecieveCallBack(IAsyncResult ar)
{
StateObject state = (StateObject)ar.AsyncState;
try
{
Socket handler = state.WorkSocket;
int read = handler.EndReceive(ar);
if (read > 0)
{
var data = new byte[read];
Array.Copy(state.Buffer, 0, data, 0, read);
}
else
{
if ((handler.Connected == false) || (handler.Available == 0))
{
Close(state);
return;
}
}
}
catch (System.Net.Sockets.SocketException)
{
Close(state);
}
catch (System.Exception exc)
{
Debug.Assert(false, exc.Message);
HandleSocketError(state, exc);
}
}
.net4/c#/vs2010
Thanks
UPDATE: Clients are connected as they can be connected, so it is up to the GSM network. They can be connected even for days and they send data at every minutes. The protocol of the client devices is different, there are different kind of devices with different kind of protocols. One of them has delimiter and CRC at the end of the message, others does not have.
You need message framing. The protocol must specify how large the messages are - usually either a constant well-known size, a length prefix, or using message delimiters.
The is no such thing as packets in TCP. It is a stream oriented protocol. That means when you read the socket you can get less data than you're expecting. You need to check the read size and if you got a short read then read again for the rest of the data.
For an example in C:
int read_data(int sock, int size, unsigned char *buf) {
int bytes_read = 0, len = 0;
while (bytes_read < size &&
((len = recv(sock, buf + bytes_read,size-bytes_read, 0)) > 0)) {
bytes_read += len;
}
if (len == 0 || len < 0) doerror();
return bytes_read;
}
I am not sure how the rest of the application looks but I think the problem is with the following line :-
Array.Copy(state.Buffer, 0, data, 0, read);
If a packet is fragmented i.e the receive callback is called multiple times for the same connection the state.Buffer will be overwritten with the last received packet, this as you copy data to offset 0 in state.Buffer.
Depending on what your needs are you need to probably need more state in StateObject :) so you can append the data rather than overwrite.
Hope this hint helps.
精彩评论