首页 > 解决方案 > c#服务器套接字数据没有正确接收

问题描述

我开发了一个应用程序,它将使用 TCP 服务器套接字程序与 plc 交互。每个循环 plc 都以字符('R')的形式向我的应用程序发送一个信号。
但有时我没有收到任何字符。这意味着事件没有触发。但是下次当 plc 发送“R”时,我会收到它作为“RR”。我的代码是`

private void _tcpServerFortest_OnRead(Socket soc)
    {
      //  rec = new byte[1];
        byte[] rec = _tcpServerFortest.ReceivedBytes;

        string str = System.Text.ASCIIEncoding.ASCII.GetString(rec);`
}

我的启动 TCP 服务器的代码是

   _tcpServerFortest = new CServerSocket(2005);
                _tcpServerFortest .OnConnect += _tcpServerFortest _OnConnect;
                _tcpServerFortest .OnDisconnect += _tcpServerFortest _OnDisconnect;
               _tcpServerFortest .OnRead += _tcpServerFortest _OnRead;
               _tcpServerFortest .Active();

在我的类 cServerSocket 中,数据接收方法如下

 private void OnDataReceived(IAsyncResult asyn)
    {
        SocketPacket socketData = (SocketPacket)asyn.AsyncState;
        try
        {
            int iRx = socketData.m_currentSocket.EndReceive(asyn);
            if (iRx < 1)
            {
                socketData.m_currentSocket.Close();
                if (!socketData.m_currentSocket.Connected)
                {
                    if (OnDisconnect != null)
                        OnDisconnect(socketData.m_currentSocket);
                    Clients.Remove(socketData.m_currentSocket);
                    socketData.m_currentSocket = null;
                }
            }
            else
            {

                mBytesReceived = socketData.dataBuffer;
                char[] chars = new char[iRx + 1];
                Decoder d = Encoding.UTF8.GetDecoder();
                d.GetChars(socketData.dataBuffer, 0, iRx, chars, 0);
                mTextReceived = new String(chars);
                if (OnRead != null)
                    OnRead(socketData.m_currentSocket);
                WaitForData(socketData.m_currentSocket);
            }
        }
        catch (InvalidOperationException ex)
        {
            if (socketData.m_currentSocket.Connected)
                socketData.m_currentSocket.Close();
            if (!socketData.m_currentSocket.Connected)
            {
                if (OnDisconnect != null)
                    OnDisconnect(socketData.m_currentSocket);
                Clients.Remove(socketData.m_currentSocket);
                socketData.m_currentSocket = null;
            }
            else
                if (OnError != null)
                    OnError(ex.Message, null, 0);
        }
        catch (SocketException se)
        {
            if (OnError != null)
                OnError(se.Message, socketData.m_currentSocket, se.ErrorCode);
            if (!socketData.m_currentSocket.Connected)
            {
                if (OnDisconnect != null)
                    OnDisconnect(socketData.m_currentSocket);
                Clients.Remove(socketData.m_currentSocket);
                socketData.m_currentSocket = null;
            }
        }
    }

谁能帮我解决这个问题?

标签: c#

解决方案


TCP 适用于流,而不是消息。看来您真的想发送消息,所以 UDP 可能是更好的选择。

当您在 TCP 上发送数据时,您不能保证这会导致另一端的单次接收。一次发送可能会导致多次接收,并且多次发送可以批处理为一次接收(如您所见)。您应该连续读取数据,并根据通信协议的要求对其进行解释。在您的情况下,这可以像“一次读取一个字节”一样简单 - 但只有您知道您到底想要什么。

另外,我建议不要使用基于 Delphi 的通信包装器;编程模型不同,似乎创建这个库的唯一原因是让熟悉 Delphi 的人不了解在 .NET 中是如何完成的。它的级别太低而无用,同时在处理数据时并没有给您太多选择。

无论您选择哪种解决方案,Stack Overflow 上已经有数百个类似的问题可供您查看;例如在 TCP 之上处理消息。基本解决方案是:

  • 实现某种形式的消息框架。这可以像“每条消息都是一个字节长”一样简单,或者“每条消息都以 4 个字节为前缀,表示消息的长度”或“每条消息都以分隔符结尾”。
  • 切换到 UDP,这是一种基于消息的协议。由于您似乎关心设备的当前状态而不是单个消息流,因此无论如何这可能是一个更好的选择 - 您可以获得更低的延迟和消息,以换取失去排序和可靠性。你需要决定这是否对你有好处。

如果保留 TCP,则需要学习如何使用 TCP。这意味着处理正常关闭(receive 返回 0 字节)、正确读取数据(继续调用接收直到获得整个消息,将其余部分保存在缓冲区中以备后用,...),错误处理。这意味着了解延迟如何在 TCP 上工作(发送并不总是立即发送,除非您明确要求他们这样做)。这意味着了解您收到的数据可能会延迟,因为您不关心的某些消息需要重新传输。TCP 并不简单,而棘手的部分是它很容易在大多数情况下工作——然后你的所有假设都会失效。


推荐阅读