0

Do 3 chunks sent by 3 calls to Linux 'C' write(), through TCP, get received as the same, 3 chunks by Windows C# .BeginReceive(), or a single, contiguous chunk, or however many have been received when .BeginReceived is called?

A 'C' app on Linux sends a message by 3 calls to write(), through TCP connection, to a Windows C# app, which receives using BeginReceive().

Does BeginReceive() need to be called three times, to receive each of the three chunks sent by write()? Or is the size received by BeginReceive() equal to the size of what Windows has received when BeginReceive() is called? Which could be all bytes sent by the 3 writes(), or a partial amount, so .BeginReceive() should be called UNTIL all are received?

The Linux C app is running on an embedded TI ARM, and inside the same box the Windows C# app is running a Single Board Computer. The ARM has a direct Ethernet connection to the SBC.

The communication between the ARM and SBC sometimes fails to start at boot time, and I'm reverse engineering the source code to check for bad design.

ARM side is TCP listener, and Windows client initiates the TCP connection.

Using MontaVista(R) Linux(R) Professional Edition 5.0.0 (0702774) and Windows-7 Visual-Studio 2010 Visual-C#.

Here is the ARM sending software, and the Windows receiving software........................

LINX 'C'

        char msgBuffer[64];
        sprintf(msgBuffer, START_MSG_ENVELOPE, msgId++, ack);
        write(connection->fd, msgBuffer, strlen(msgBuffer));
        write(connection->fd, msg, strlen(msg));
        write(connection->fd, END_MSG_ENVELOPE, strlen(END_MSG_ENVELOPE));

HERE IS THE WINDOWS C# SIDE OF IT.............................................

        private static void makeConnection(Socket clientSocket, int iPortNo)
    {
        TimeoutObject.Reset();
        socketexception = null;

        IPAddress ip;
        //create the end point 
        IPEndPoint ipEndPoint;

        ip = IPAddress.Parse(ipAddress);

        try
        {
            ipEndPoint = new IPEndPoint(ip, iPortNo);

            //connect to the remote host...
            clientSocket.BeginConnect(ip, iPortNo, new AsyncCallback(CallBackMethod), clientSocket);

            if (TimeoutObject.WaitOne(5 * 1000, false))    //5 secs connection timeout
            {
                if (!IsConnectionSuccessful)
                {
                    string msg = VNResourceManager.Instance.GetString(VNMessages.DAM_NOT_FOUND);
                    if (socketexception != null)
                        msg += ": " + socketexception.Message;
                    throw new Exception(msg);
                }
            }
            else
            {
                clientSocket.Close();
                throw new TimeoutException(VNResourceManager.Instance.GetString(VNMessages.CONNECTION_TO_DAM_TIMED_OUT));
            }
            //watch for data ( asynchronously )...
            WaitForData();
        }
        catch (SocketException e)
        {
            socketexception = e;
            throw;
        }
    }

    private static void CallBackMethod(IAsyncResult asyncresult)
    {
        try
        {
            IsConnectionSuccessful = false;
            Socket socket = asyncresult.AsyncState as Socket;

            if (socket.Connected)
            {
                socket.EndConnect(asyncresult);
                IsConnectionSuccessful = true;
            }
        }
        catch (Exception ex)
        {
            IsConnectionSuccessful = false;
            socketexception = ex;
        }
        finally
        {
            TimeoutObject.Set();
        }
    }

    public static void WaitForData()
    {
        try
        {
            if (asyncCallBack == null)
            {
                asyncCallBack = new AsyncCallback(OnDataReceived);
            }
            CSocketPacket theSocPkt = new CSocketPacket();
            theSocPkt.thisSocket = clientSocket;
            asyncResult = clientSocket.BeginReceive(theSocPkt.dataBuffer, 0, theSocPkt.dataBuffer.Length, SocketFlags.None, asyncCallBack, theSocPkt);
        }
        catch (SocketException se)
        {
            notifyErrorEventSubscribers(se);
        }
    }

    public static void send(string message)
    {
        try
        {
            byte[] byData = System.Text.Encoding.ASCII.GetBytes(message);
            clientSocket.Send(byData);
        }
        catch (SocketException se)
        {
            notifyErrorEventSubscribers(se);
            throw;
        }
    }

    //[MethodImpl(MethodImplOptions.Synchronized)]
    public static void OnDataReceived(IAsyncResult result)
    {
        try
        {
            CSocketPacket theSockId = (CSocketPacket)result.AsyncState;

            //end receive...
            int messageSize = 0;

            messageSize = theSockId.thisSocket.EndReceive(result);

            Console.WriteLine(">>>>>>>>>  messageSize = " + messageSize); // !!!

            char[] chars = new char[messageSize + 1];

            System.Text.Decoder d = System.Text.Encoding.ASCII.GetDecoder();
            int charLen = d.GetChars(theSockId.dataBuffer, 0, messageSize, chars, 0);

            string replyMessage = new System.String(chars);

            lock (syncLock)  //LastIndexOf function accesses the current culture info and we clear it in WM_TIMECHANGE handler (protecting from that race condition here)
            {
                if (replyMessage.LastIndexOf("\0") > 0)
                    replyMessage = replyMessage.Remove(replyMessage.LastIndexOf("\0"), 1);
                if (replyMessage.LastIndexOf(Terminator) > 0)
                    replyMessage = replyMessage.Remove(replyMessage.LastIndexOf(Terminator), 1);
            }


            // Continue the waiting for data on the Socket
            WaitForData();

            receivedMsg += replyMessage;


            // only serialize when we feel we have a message or we have reached the message line limit  
            if (((receivedMsg.Contains("message") && receivedMsg.Contains("/>")) || receivedMsg.Contains("</message>")) /* || (mRecvdMsgLineCount == Message.kMaxLines) */ )
            {
                List<XmlMessage> msgList = new List<XmlMessage>();

                int index = -1;
                do
                {
                    index = receivedMsg.IndexOf("</message>");

                    if (index != -1)
                    {
                        XmlMessage message;
                        string strMessage = receivedMsg.Substring(0, index + "</message>".Length);
                        //MessageBox.Show(strMessage);
                        strMessage = strMessage.TrimStart(new char[] { '\r', '\n' });

                        receivedMsg = receivedMsg.Remove(0, index + "</message>".Length);

                        try
                        {
                            message = (XmlMessage)XmlMessage.GetXmlSerializer().Deserialize(XmlReader.Create(new StringReader(strMessage)));
                        }
                        catch (InvalidOperationException error)
                        {
                            string strErrorMessage = error.Message;
                            if (error.InnerException != null)
                                strErrorMessage += "\r\n" + error.InnerException.Message;

                            notifyErrorEventSubscribers(new Exception(strErrorMessage + "\r\n-----------------------------------------------------------------\r\n" + strMessage));

                            return;
                        }

                        msgList.Add(message);
                    }
                } while (index != -1);

                StringWriter sw = new StringWriter();
                string serializedXml = string.Empty;
                string strXmlMessage = string.Empty;

                foreach (XmlMessage message in msgList)
                {
                    if (message.ack_required && (message.update == null))
                    {
                        XmlMessage messageAcknowledge = new XmlMessage();
                        messageAcknowledge.ack_required = false;
                        messageAcknowledge.ack = new ack();
                        messageAcknowledge.ack.success = true;
                        messageAcknowledge.ack.id = message.id;

                        try
                        {
                            sendMessage(messageAcknowledge);
                        }
                        catch(Exception ex)
                        {
                            Logger.Log(EnumLoggingLevel.Error, "SocketCommunicationXMl.OnDataReceived", ex.Message);
                        }
                    }

                    if (dataReceivedEvent != null)
                    {
                        dataReceivedEvent(new object(), new DataReceivedEventArgs(message));
                    }

                    if ((ackRequiredMsg != null) && (message.ack != null))
                    {
                         if ((message.ack.id == ackRequiredMsg.id) && message.ack.success)
                         {
                             eventWaitForAck.Set();
                         }
                    }
                }
            }
        }
        catch (ObjectDisposedException objectDisposedException)
        {
            //              Dispatcher.dispatchDebug(Debug.Level_3,"Socket has been closed", this);
            notifyErrorEventSubscribers(objectDisposedException);
        }
        catch (SocketException se)
        {
            if (se.ErrorCode == 10054)
            {
                /*
                for (int i = 0; i < 50; i++)
                {
                    Thread.Sleep(1000);
                }

                try
                {
                    SocketCommunicationDaq.Reconnect();
                }
                catch(Exception ex)
                {
                    VNMsgBox.Show(ex.Message, MsgButtonType.OkOnly, MsgIconType.Error);
                    return;
                }

                clientSocket.Shutdown(SocketShutdown.Both);
                clientSocket.Close();

                for (int i = 0; i < 3; i++)
                {
                    try
                    {
                        connect();
                        break;
                    }
                    catch (Exception ex)
                    {
                        System.Threading.Thread.Sleep(5000);
                    }
                }
                */
                Logger.Log(EnumLoggingLevel.Error, "OnDataReceived: ", se.ToString());
            }
            else
            {
                notifyErrorEventSubscribers(se);
            }
        }
    }
alk
  • 69,737
  • 10
  • 105
  • 255
Doug Null
  • 7,989
  • 15
  • 69
  • 148
  • 6
    Since TCP is a stream, and do not preserve write() boundaries, there's no telling. It could take 71 calls to receive them, or it could take 1 call. Do you have any way to know how much data you need to read, e.g. a length prefix in the data, or a special terminator in the data ? – nos Feb 15 '14 at 16:58
  • Also you cannot rely on C's `write()` having really written how much it was told to write. **Always** check the values returned by `write`/`send*()`, and not only for error checking! It is good idea to read the fine manual closely. – alk Feb 15 '14 at 17:04

2 Answers2

1

As @nos already stated, number of receives does not equal the number of sends, regardless of the client application was written in.

See also When does TcpClient's NetworkStream finish one read operation?

Community
  • 1
  • 1
Kay Tsar
  • 1,428
  • 11
  • 14
1

As others have mentioned, TCP is a streaming protocol, so you never can tell how many DataReceived callbacks it will take to get all 100 bytes. Could be 1, could be 100.

The receive code is fairly complex and performance could be improved (too many string operations). Hard to tell if there are control-flow issues. I would suggest breaking the DataReceived method up to simplify. Here's a reasonable skeleton:

    public static void OnDataReceived(IAsyncResult result)
    {
        //1) copy all data received into a buffer of some sort, like MemoryStream

        //2) Dispatch any/all full messages that have been received
          // to a queue or handler method (maybe handle on another thread)
          //(hold onto any leftover bytes received that are part of the next message)

        //Call BeginReceive() again
    }

Also, it can help simplify Framing if you use a Length-Prefixed message format.

Chi_Town_Don
  • 111
  • 6