【问题标题】:Socket not reading all the packets that arrive套接字没有读取所有到达的数据包
【发布时间】:2015-03-20 13:46:52
【问题描述】:

我正在使用 C# TCP 客户端并使用 Microsoft 网络监视器监视我的数据包。有一个服务器基本上是一个黑盒子,它发送 N 个数据包(现在它的数量级为 10-20),每个数据包与下一个数据包之间的延迟为 0.1 毫秒到 1 毫秒。

我的客户端读取的数据包是有序的,当然大多数到达的数据块比网络监视器上显示的数据块更大,因为 TCP 接收流。我的问题是一些数据包无法到达(我确实检查了前面的信息块以确保它不存在。它们也没有以错误的顺序存储。)。

那么我的客户是否有可能以某种方式遗漏了一些信息?数据包是否发送过于频繁?可悲的是,我无法篡改他们的频率。我在这里添加了一些我的代码,如果你能告诉我为什么没有读取到达的数据包以及如何解决这个问题,我将不胜感激。

这是我第一次调用 BeginReceive 的地方:

private static void AcceptCallback(IAsyncResult result)
{
    ConnectionInfo connection = new ConnectionInfo();
    MensajeRecibido msj = new MensajeRecibido();
    try
    {
        // Finish Accept

        Socket s = (Socket)result.AsyncState;
        connection.Socket = s.EndAccept(result);
        msj.workSocket = connection.Socket;
        connection.Socket.Blocking = false;
        connection.Buffer = new byte[255];
        lock (connections) connections.Add(connection);

        // Start Receive

        connection.Socket.BeginReceive(msj.buffer, 0,
        msj.buffer.Length, SocketFlags.None,
        new AsyncCallback(ReceiveCallback), msj);
        // Start new Accept
        serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), result.AsyncState);
    }
    catch (SocketException exc)
    {
        //Log here
    }
    catch (Exception exc)
    {
        //Log here
    }
}

这是回调:

private async static void ReceiveCallback(IAsyncResult result)
{
    MensajeRecibido mensaje = new MensajeRecibido();
    mensaje = (MensajeRecibido)result.AsyncState;

    try
    {
        mensaje.workSocket.EndReceive(result);
        mensaje.EstaCompleto();
        mensaje.workSocket.BeginReceive(mensaje.buffer, 0,
        mensaje.buffer.Length, SocketFlags.None,
        new AsyncCallback(ReceiveCallback), mensaje);
    }
    catch (SocketException)
    {
        //Log
    }
    catch (Exception)
    {
        //Log
    }
}

这是 EstaCompleto() 方法,它基本上转换消息并将其添加到列表中。(它返回 true 或 false,因为它实际上是要进入 if 子句,但直到我得到摆脱这个真的没有用的问题)

public bool EstaCompleto()
{
    MensajeActual = Destuffing(ByteToFrame_Decoder(buffer)); //This translates the message to an understandable string
    Mensajes.Enqueue(MensajeActual);
    if(MensajeActual.Contains("<ETX>"))
    {
        return true;
    }
    else return false;
}

25/3/15 编辑: 这是 MensajeRecibido 课程的其余部分。

 public class MensajeRecibido
{

    public Socket workSocket = null;
    // Size of receive buffer.
    public const int BufferSize = 25500; 
    // Receive buffer.
    public byte[] buffer = new byte[BufferSize];
    public string UltimoMensajeLeido = "0";
    public string MensajeActual = "0";
    public Queue<string> Mensajes = new Queue<string>();
    public IPAddress IPRack;


    //*******************************************************************

    public bool EstaCompleto()
    ///See code in the previous sample


    //*******************************************************************
    public string ByteToFrame_Decoder(byte[] frame)
    {
        string answer = null;
        UTF8Encoding ObjDecoder = new System.Text.UTF8Encoding();
        char[] array_chares = new char[frame.Length];
        string msj_neg = null;
        string titlemsg = "Atención";
        try
        {
            int cant = ObjDecoder.GetChars(frame, 0, frame.Length, array_chares, 0);
        }
        catch (EncoderFallbackException EncFbackEx)
        {
            msj_neg = "No hay comunicación";
            //   System.Windows.Forms.MessageBox.Show(msj_neg, titlemsg, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }
        answer = decode_string(array_chares);
        return answer;
    } // fin método ByteToFrame_Decoder()

    //*******************************************************************
    public string Destuffing(string msjstuff)
    {
        string destuffed = null;
        string matched = null;
        string original = null;
        int largo = msjstuff.Length;

        for (int i = 0; i < (largo - 1); i++)
        {
            matched = msjstuff.Substring(i, 2);
            original = msjstuff.Substring(i, 1);
            if (original != " ")
            {
                switch (matched)
                {
                    case "EX":
                        { original = "D"; i++; } break;

                    case "ex":
                        { original = "d"; i++; } break;

                    case "EE":
                        { original = "E"; i++; } break;

                    case "ee":
                        { original = "e"; i++; } break;

                    case "  ":
                        { original = ""; i += 2; } break;
                }
                destuffed = destuffed + original;
            }
            else
            {
                i++;
            }

        }
        destuffed = destuffed + ">";
        return destuffed;

    } //fin método Destuffing()
    //*******************************************************************
    public static string decode_string(char[] ArrChar)
    {
        string text = null;
        string reply = null;
        foreach (char letter in ArrChar)
        {
            int value = Convert.ToInt32(letter);
            string hexOutput = String.Format("{0:X}", value); // Convert the decimal value to a hexadecimal value in string form.
            switch (hexOutput)
            {
                case "20":
                    text = " ";
                    break;
                case "1":
                    text = "<SOH>";
                    break;
                case "2":
                    text = "<STX>";
                    break;
                case "3":
                    text = "<ETX>";
                    break;
                case "4":
                    text = "<EOT>";
                    reply = reply + text;
                    goto Finish;
                case "5":
                    text = "<ENQ>";
                    break;
                case "6":
                    text = "<ACK>";
                    break;
                case "15":
                    text = "<NAK>";
                    break;
                case "17":
                    text = "<ETB>";
                    break;
                case "1E":
                    text = "<RS>";
                    break;
                /*case "23":
                    text = "#";
                    break;
                case "24":
                    text = "$";
                    break;
                case "26":
                    text = "&";
                    break;*/
                default:
                    text = letter.ToString();
                    break;
            }
            reply = reply + text;
        }
    Finish: ; //salimos del foreach
        return reply;
    } //fin método decode_string()
    //*******************************************************************

}

【问题讨论】:

    标签: c# sockets tcp packet beginreceive


    【解决方案1】:

    如果没有可靠地证明问题的a good, minimal, complete code example,就不可能为该错误提供准确的修复。

    但是,从您的 ReceiveCallback() 方法中的此语句中,可以清楚地看出问题所在:

    mensaje.workSocket.EndReceive(result);
    

    EndReceive() 方法返回字节数的原因是:无法保证将接收到的字节数。

    网络编程新手通常抱怨两种不同的行为:

    1. 他们的代码只接收“数据包”(或“消息”或类似术语)的一部分。
    2. 他们的代码无法接收一些已发送的“数据包”。

    这两个问题都源于一个单一的根源:未能理解在 TCP 协议中,没有“数据包”之类的东西

    由应用程序定义消息边界。 TCP 为您提供的所有保证是,如果发送的字节确实被接收,它们将按照发送它们的顺序被接收,如果接收到任何给定的字节,则所有先前发送的字节也被接收(即数据中没有空白)。

    当 TCP 只传送新手程序员作为“数据包”发送的部分内容时,就会出现上述问题 #1。这对 TCP 来说是完全合法的行为,由应用程序来跟踪到目前为止接收到的数据并确定何时接收到整个“数据包”。

    当 TCP 在单个接收操作中传送两个或更多“数据包”时,会发生问题 #2(您遇到的问题)。同样,这在 TCP 方面是完全合法的,由应用程序处理接收到的数据并确定一个“数据包”在哪里结束,下一个“数据包”从哪里开始。

    正确执行所有这些操作的第一步是将EndReceive() 方法的返回值实际复制到一个变量中,然后将该值用作处理接收数据的一部分。由于您的代码示例没有将值保存在任何地方,甚至根本没有查看它,我可以保证您没有正确处理数据中的“数据包”边界。

    您应该如何处理这些界限?我不知道。这取决于您发送数据的方式以及您希望如何处理结果。如果没有完整的代码示例(如我上面提供的链接中所述),就不可能解决这个问题。但是请注意,有大量示例说明如何正确执行此操作。希望现在您知道要寻找什么,您将能够自己解决问题。如果没有,请随意创建一个好的代码示例并发布一个新问题寻求帮助。

    【讨论】:

    • 首先,感谢他最小的完整代码链接,我以前没见过。对于不拥有我正在与之通信的设备的人来说,很难重现他的问题,恐怕我不允许共享 IP 来访问它,这相当麻烦。我每次都在这些行上存储信息: MensajeActual = Destuffing(ByteToFrame_Decoder(buffer)); //这会将消息转换为可理解的字符串 Mensajes.Enqueue(MensajeActual);然后我检查队列以查看已读取的内容。如前所述,我没有同时收到两条消息。
    • “很难重现他的问题” -- 然而,任何人若要帮助提供任何类型的精确答案,您必须提供一个可重现的案例。如果你不能,那么这不是获得帮助的正确论坛。
    • “我确实每次都在这些行上存储信息”——不,不是真的。您不使用EndReceive() 的返回值这一事实意味着您的其余代码不可能正常工作。 TCP 的正确实现不会丢失“数据包”。如果您认为这种情况正在发生,那么要么您正在处理 TCP 的错误实现(即在“设备”中),要么您只是无法识别接收到的数据中的组合“数据包”。我发现后者比前者更合理。
    • EndReceive 的返回值是写入缓冲区的字节数。我不使用偏移量继续写它离开的地方,所以它从 0 开始再次写入。每次到达 EndReceive 时,它​​都会将缓冲区中的所有内容转换为字符串并将其保存到队列中。你的意思是说我的转换有问题,需要考虑字节数,所以它实际上转换了整个缓冲区? (请参阅原始帖子)我在检查读取的字节量后对其进行了调试,它永远不会超过我的缓冲区大小(不确定是否可以)。我什至增加了缓冲区大小,但没有帮助。
    • “EndReceive 的返回值是写入的字节数”——通过接收操作,是的。请注意,此数字可以是 1 和缓冲区长度之间的任何值;如果您不考虑返回值,而是假设整个缓冲区充满了有效数据,那么您将得到不正确的结果。正如我所写的,如果没有一个好的代码示例,就不可能准确地说明出了什么问题。但是没有考虑到EndReceive() 的返回值确实保证你的代码是错误的。
    猜你喜欢
    • 2013-07-10
    • 2012-06-17
    • 1970-01-01
    • 1970-01-01
    • 2010-10-30
    • 1970-01-01
    • 1970-01-01
    • 2014-09-15
    • 2017-01-26
    相关资源
    最近更新 更多