tags:

views:

126

answers:

3

Problem:

When I do something like this:

for (int i = 0; i < 100; i++)
{
    SendMessage( sometSocket, i.ToString());
    Thread.Sleep(250); // works with this, doesn't work without
}

With or without the sleep the server logs sending of separate messages. However without the sleep the client ends up receiving multiple messages in single OnDataReceived so the client will receive messages like:

0, 1, 2, 34, 5, 678, 9 ....

Server sending Code:

private void SendMessage(Socket socket, string message)
{
    logger.Info("SendMessage: Preparing to send message:" + message);            

    byte[] byteData = Encoding.ASCII.GetBytes(message);

    if (socket == null) return;
    if (!socket.Connected) return;

    logger.Info("SendMessage: Sending message to non " +
                "null and connected socket with ip:" + socket.RemoteEndPoint);

    // Record this message so unit testing can very this works.

    socket.Send(byteData);
}

Client receiving code:

private void OnDataReceived(IAsyncResult asyn)
{
    logger.Info("OnDataReceived: Data received.");

    try
    {
        SocketPacket theSockId = (SocketPacket)asyn.AsyncState;
        int iRx = theSockId.Socket.EndReceive(asyn);
        char[] chars = new char[iRx + 1];
        System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
        int charLen = d.GetChars(theSockId.DataBuffer, 0, iRx, chars, 0);
        System.String szData = new System.String(chars);

        logger.Info("OnDataReceived: Received message:" + szData);

        InvokeMessageReceived(new SocketMessageEventArgs(szData));

        WaitForData();  // .....  

Socket Packet:

public class SocketPacket
{
    private Socket _socket;
    private readonly int _clientNumber;
    private byte[] _dataBuffer = new byte[1024]; ....

My hunch is it's something to do with the buffer size or its just the between the OnDataReceived and EndReceive we're getting multiple messages.

Update: It turns out when I put a Thread.Sleep at the start of OnDataReceived it gets every message. Is the only solution to this wrapping my message in a prefix of length and an string to signify the end?

A: 

TCP sockets don't always send data right away -- in order to minimize network traffic (because each packet sent requires an acknowledgement by the receiver), TCP/IP implementations will often buffer the data for a bit and send it when it sees there's a lull (or when the buffer's full).

If you want to ensure that the messages are processed one by one, you'll need to either set socket.NoDelay = true (which might not help much, since data received may still be bunched up together in the receive buffer), implement some protocol to separate messages in the stream (like prefixing each message with its length), or use a message-oriented protocol like UDP.

cHao
attempted the socket.NoDelay = true to no avail. by looking at my server logs it definitely is sending the messages one by one.its looking more and more likely I'll need something to wrap the messages to separate them :(
nextgenneo
Yeah...TCP was made for stream-oriented stuff. You'll need some way to tell when a message starts and ends. I'd actually recommend UDP or SCTP if you can use them, as a malformed message in TCP can cause corruption of the whole stream. UDP, on the other hand, is harder to mess up like that -- each message is discrete, and you don't need to worry about whether the length was set correctly.
cHao
+3  A: 

This is expected behaviour. A TCP socket represents a linear stream of bytes, not a sequence of well-delimited “packets”. You must not assume that the data you receive is chunked the same way it was when it was sent.

Notice that this has two consequences:

  1. Two messages may get merged into a single callback call. (You noticed this one.)
  2. A single message may get split up (at any point) into two separate callback calls.

Your code must be written to handle both of these cases, otherwise it has a bug.

Timwi
Nitpick: A *TCP* socket represents a linear stream of bytes. UDP sockets are message-oriented, and the data received is chunked together the same way it was sent.
cHao
Thanks, fixed ☺
Timwi
great! this basically clarifies everything and makes sense why I am getting this behavior. thanks both CHao and Timiwi. I will think about how UDP affects my overall architecture vs. wrapping a message in something to signify its a complete message.
nextgenneo
When you think about UDP vs a message protocol, also consider what you'll do if a message hasn't been completely read in yet. In TCP, if you're not careful, you'll either read in part of a message or block waiting for the rest (which, if it was lost in transit, could take a while to arrive).
cHao
A: 

There is no need to abandon Tcp because it is stream oriented.

You can fix the problems that you are having by implementing message framing.

See

http://blogs.msdn.com/malarch/archive/2006/06/26/647993.aspx

also:

http://nitoprograms.blogspot.com/2009/04/message-framing.html

feroze
You'd need to make sure that the code that reads messages processes exactly one message at a time (not two, not part of one). And you better pray the messages are always framed correctly, or that there's some kind of protocol for restoring the stream to a usable state.
cHao