【问题标题】:How many times should C# .BeginReceive() be called to receive 3 chunks sent be 3 calls to Linux C write() across TCP?应该调用多少次 C# .BeginReceive() 来接收通过 TCP 对 Linux C write() 进行 3 次调用发送的 3 个块?
【发布时间】:2014-03-15 01:43:19
【问题描述】:

通过 TCP 对 Linux 'C' write() 的 3 次调用发送的 3 个块被接收为相同的,由 Windows C# .BeginReceive() 接收的 3 个块,或者单个连续的块,或者已经接收到的多个块在调用 .BeginReceived 时收到?

Linux 上的“C”应用程序通过 TCP 连接通过 3 次 write() 调用向 Windows C# 应用程序发送消息,该应用程序使用 BeginReceive() 接收。

BeginReceive() 是否需要调用 3 次才能接收 write() 发送的三个块中的每一个?或者 BeginReceive() 接收到的大小是否等于调用 BeginReceive() 时 Windows 接收到的大小?哪个可能是 3 个 writes() 发送的所有字节,或者是部分数量,所以应该调用 .BeginReceive() 直到所有都被接收?

Linux C 应用程序在嵌入式 TI ARM 上运行,在同一个盒子内,Windows C# 应用程序正在运行单板计算机。 ARM 与 SBC 有直接的以太网连接。

ARM 和 SBC 之间的通信有时无法在启动时启动,我正在对源代码进行逆向工程以检查是否存在不良设计。

ARM端为TCP监听,Windows客户端发起TCP连接。

使用 MontaVista(R) Linux(R) Professional Edition 5.0.0 (0702774) 和 Windows-7 Visual-Studio 2010 Visual-C#。

这里是ARM发送软件,Windows接收软件............

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));

这里是它的 WINDOWS C# 端...................................... ......

        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);
            }
        }
    }

【问题讨论】:

  • 由于 TCP 是一个流,并且不保留 write() 边界,因此无法说明。接收它们可能需要 71 个电话,也可能需要 1 个电话。你有什么办法知道你需要读取多少数据,例如数据中的长度前缀,或数据中的特殊终止符?
  • 另外,您不能依赖 C 的 write() 真正写了它被告知要写多少。 总是检查write/send*()返回的值,而不仅仅是错误检查!仔细阅读精美的手册是个好主意。

标签: c# c linux sockets tcp


【解决方案1】:

正如其他人所提到的,TCP 是一种流式协议,因此您永远无法知道获取所有 100 个字节需要多少 DataReceived 回调。可能是 1,可能是 100。

接收代码相当复杂,性能可以提高(字符串操作太多)。很难判断是否存在控制流问题。我建议打破 DataReceived 方法以简化。这是一个合理的骨架:

    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
    }

此外,如果您使用以长度为前缀的消息格式,它可以帮助简化框架。

【讨论】:

    【解决方案2】:

    正如@nos 已经说过的,接收的数量不等于发送的数量,无论客户端应用程序被写入。

    另见When does TcpClient's NetworkStream finish one read operation?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-09
      • 2015-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多