0

我正在使用 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()
    //*******************************************************************

}
4

1 回答 1

0

如果没有一个好的、最小的完整的代码示例来可靠地演示该问题,就不可能为该错误提供精确的修复。

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

mensaje.workSocket.EndReceive(result);

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

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

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

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

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

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

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

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

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

于 2015-03-20T15:56:12.417 回答