【问题标题】:c# server socket data not receiving properly [duplicate]c#服务器套接字数据没有正确接收[重复]
【发布时间】:2018-12-14 17:02:49
【问题描述】:

我开发了一个应用程序,它将使用 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#


    【解决方案1】:

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

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

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

    无论您选择哪种解决方案,Stack Overflow 上已经有数百个类似问题可供您查看;例如Handling messages on top of TCP。基本解决方案是:

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

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

    【讨论】:

    • 我们只想分享像 R 这样的单个字符
    • @sudhakarnayak TCP 不保证单次发送会在另一端产生单次接收。事实上,出于性能原因(除其他外),它设计不是。 TCP 用于流,而不是消息。这是为了可靠性,而不是低延迟。如果您想通过 TCP 发送不同的消息,您需要编写一个通信协议来执行此操作(或使用库) - TCP 没有。您的代码还有许多其他问题,但这是根本问题。
    • 还要确保在每条数据上都有一个终止符,比如返回,这样你就可以正确地吐出数据。 Luaan 变得比必要的复杂。只要接收者知道每条消息的结尾位置并在处理之前读取每条消息的结尾,简单的 TCP 就可以工作。同样使用 TCP,当使用 Keep-Alive 时,您可以获得零字节的接收数据。在以下 e 上使用 1) ASCII 数据:以不在数据中的字符终止。 2) ASCII 或二进制:在每条消息之前加上字节数。 3) ASCII 或二进制:使用固定长度的消息。
    • @jdweng 为什么还要对我的问题发表评论?只需写一个答案;)评论不是用来写答案的。在任何情况下,“知道每条消息的结束位置”一种通信协议。 UDP 更适合 OP 提出的问题,并且在特定情况下更易于使用。 TCP 之上的自定义协议是否比 UDP 更好是 OP 需要决定的。
    • @Luaan :正如我所说,您的请求比必要的复杂得多。这相当于说 CSV 文件是一种协议。 CSV 文件可以通过每行一条指令生成和读取。是的,CSV 是一种协议,但没有人称它为协议,因为它非常简单。
    猜你喜欢
    • 1970-01-01
    • 2016-06-20
    • 2015-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-30
    • 1970-01-01
    相关资源
    最近更新 更多