0

私は C# TCP クライアントで作業しており、Microsoft Network Monitor を使用してパケットを監視しています。基本的にブラックボックスであるサーバーがあり、それぞれのパケットと次のパケットの間に 0.1 ミリ秒から 1 ミリ秒の遅延があり、N 個のパケット (現在は 10 ~ 20 のオーダー) を送信します。

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()基本的にメッセージを変換してリストに追加する方法です。(実際にはif句に入れることを意図しているため、trueまたはfalseを返しますが、実際には何の役にも立たないこの問題を取り除くまで)

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()メソッドは、理由によりバイト数を返します。受信されるバイト数の保証はありません。

ネットワーク プログラミングの初心者は、通常、次の 2 つの異なる動作について不満を漏らします。

  1. それらのコードは、「パケット」(または「メッセージ」、または同様の用語) の一部のみを受け取ります。
  2. 彼らのコードは、送信された「パケット」の一部を受信できません。

どちらの問題も、TCP プロトコルには「パケット」などというものがないことを理解していないという 1 つの原因に起因しています。

メッセージ境界を定義するのはアプリケーション次第です。TCPが提供するのは、送信されたバイトが実際に受信された場合、それらが送信されたのと同じ順序で受信されるという保証だけであり、特定のバイトが受信された場合、以前に送信されたバイトもすべて受信されました(つまり、データにギャップはありません)。

上記の問題 #1 は、初心者のプログラマーが「パケット」として送信したものの一部のみを TCP が配信する場合に発生します。これは、TCP 側では完全に正当な動作であり、これまでに受信したデータを追跡し、「パケット」全体をいつ受信したかを把握するのはアプリケーション次第です。

問題 #2 (あなたが経験していること) は、TCP が 1 回の受信操作で 2 つ以上の「パケット」を配信するときに発生します。繰り返しますが、これは TCP 側では完全に合法であり、受信したデータを処理して、1 つの「パケット」がどこで終わり、次のパケットがどこで始まるかを識別するのは、アプリケーション次第です。

これらすべてを正しく行うための最初のステップは、EndReceive()メソッドの戻り値を実際に変数にコピーし、その値を受信データの処理の一部として使用することです。コード例は値をどこにも保存していないか、まったく見ていないため、データの「パケット」境界を正しく処理していないことを保証できます。

それらの境界をどのように処理する必要がありますか? 何も思いつきません。データの送信方法と結果の処理方法によって異なります。完全なコード例 (上記のリンクで説明されているように) がなければ、それに対処することはできません。ただし、正しく行う方法については、多数の例があることに注意してください。何を探すべきかがわかったので、うまくいけば、自分で修正できるようになります。そうでない場合は、良いコード例を自由に作成し、それについての助けを求める新しい質問を投稿してください。

于 2015-03-20T15:56:12.417 に答える