套接字没有读取到达的所有数据包

时间:2015-03-20 13:46:52

标签: c# sockets tcp packet beginreceive

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

}

1 个答案:

答案 0 :(得分:0)

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

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

mensaje.workSocket.EndReceive(result);

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

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

  1. 他们的代码只接收部分“数据包”(或“消息”或类似的术语)。
  2. 他们的代码无法收到一些已发送的“数据包”。
  3. 这两个问题源于单一来源:未能理解在TCP协议中,没有“数据包”这样的东西

    由应用程序来定义消息边界。 TCP提供的所有内容都保证,如果发送的字节实际上是接收的,它们将按照发送它们的顺序接收,如果接收到任何给定的字节,则还接收所有先前发送的字节(即数据中没有间隙)。

    当TCP只提供新手程序员作为“数据包”发送的部分内容时,会发生上述问题#1。这在TCP方面是完全合法的行为,并且由应用程序来跟踪到目前为止收到的数据并找出它何时收到整个“数据包”。

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

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

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