2

Windows Embedded CE 6 プラットフォームで .NET CF 3.5 アプリケーションを開発しています。私たちは、WebApp を提供し、単純な REST 要求に応答する小さな (HTTP 1.0) Web サーバーを .NET に実装しようとしています。

この実装は、MSDN の記事http://msdn.microsoft.com/en-us/library/aa446537.aspxに示されているパターンに従います。TCP リッスン ソケット、async-callbacks を BeginAccept、EndAccept、BeginRecieve、および EndReceive と組み合わせて使用​​します。

リスニング ポートでの着信接続は、非同期受け入れコールバックによって処理されます。( http://msdn.microsoft.com/en-us/library/5bb431f9.aspxを参照)。この非同期受け入れコールバック内で EndAccept メソッドを呼び出すことにより、リスニング ポートに接続を新しいソケットに渡し、ポートを解放するように指示します。これにより、新しい着信接続要求をリスニング ポートで受け入れることができます。すでに受け付けたリクエストは、(非同期コールバックで処理されるため)独自のスレッドで処理されます。

BeginAccept と EndAccept の間の時間を最小限に抑えることは既に試みました。これは、BeginAccept と EndAccept の呼び出しの間のこの期間中に、着信接続要求がリッスン ソケットのバックログ キューに置かれるためです。このキューの長さは、いわゆる backlog パラメーターを介して構成できます。このパラメーターには、プラットフォームに依存する最大値があります。バックログ キューが使い果たされると、新しい tcp 接続要求はスリーウェイ ハンドシェイク中に拒否されます (クライアント/ブラウザーは、syn への応答として RST を取得します)。

ここで、Firefox、Chrome、Safari などの最新のブラウザーのほとんどが、サーバーからデータをロードするために最大 15 (またはそれ以上) の同時接続を使用するという問題に遭遇しました (ホストごとの同時接続の最大数は、Firefox で次のように構成できます)。 about:config -> network.http.max-connections-per-server)。ページが読み込まれると、ブラウザは必要に応じて 15 の接続を確立します。これは、読み込む必要のあるリソース (画像、javascript、css ファイルなど) の数に応じて異なります。

.NET CF socket.listen メソッド ( http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.listen.aspxを参照) を使用すると、バックログ番号を定義できます。
私たちの理解では、バックログは 15 より大きく、たとえば 20 程度あるはずです。これは、すべての接続要求がブラウザーによって同時にトリガーされ、小さな Web サーバーが 15 の同時接続要求にヒットしたためです。バックログ キューのサイズが小さすぎると、TCP 接続が中止されます。これは、リッスンしているソケットがすべてを受け入れるまで、すべての着信接続をキューに入れることができないためです。Firebug または Chrome では、これらのリクエストは「中止」として表示されます。そのため、socket.listen(20) によってこのケースのバックログを 20 に増やし、すべてが問題なく、最も貪欲なブラウザーにも耐える準備ができていることを望みました。

問題は、socket.listen() 呼び出しの backlog パラメータがサイレントに SOMAXXCON に設定されていることです (この場合、最大 5 つの接続)。それより高い数値を設定しても効果はありません。ブラウザがたとえば 16 個の同時ソケット接続を確立すると、一部のソケットが単に 5 のバックログ キューに収まらず、TCP 接続が Web サーバーから TCP-RST を取得し、一部のリソースが欠落しているという事実により、一部のソケットが失われました。ウェブページで。

Windows Embedded CE 6.0 で SOMAXXCON を変更する方法はありますか? (プラットフォーム イメージを変更できます - プラットフォーム ビルダーを利用します)。それとも、その問題に対する私たちの理解に誤りがありますか?

現在使用しているソース コードを添付します。

public class StateObject
    {
        // Client  socket.
        public Socket workSocket = null;
        // Size of receive buffer.
        public const int BufferSize = 1024;
        // Receive buffer.
        public byte[] buffer = new byte[BufferSize];
        // Received data string.
        public StringBuilder sb = new StringBuilder();
    }

public void StartListening()
    {
       logger.Debug("Started Listening at : " + this.listeninghostIp + ":" + this.listeningport);
       IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(this.listeninghostIp), Convert.ToInt32(this.listeningport));
       listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

       listener.Bind(localEP);
       listener.Listen(10);
       ThreadPool.QueueUserWorkItem(new WaitCallback(CheckForConnections) );
    }

public void CheckForConnections()
    {
        try
        {
            logger.Debug("listening successfully started! Waiting for incoming connections...");
            listener.BeginAccept(new AsyncCallback(acceptCallback), listener);
          }
        catch (Exception ex)
        {
            logger.Error("Exception Occured while starting Listening : " + ex.Message.ToString());
        }
     }

private void acceptCallback(IAsyncResult ar)
    {
        try
        {

            Socket listener = (Socket)ar.AsyncState;
            listener.BeginAccept(new AsyncCallback(acceptCallback), listener);
            Socket handler = listener.EndAccept(ar);
            StateObject state = new StateObject();
            state.workSocket = handler;
            handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
        new AsyncCallback(ReadCallback), state);

            logger.Debug("listening socket accepted connection...");

         }
        catch (Exception ex)
        {
            logger.Error("Error on acceptCallback. Error: " + ex.Message);
        }

public void ReadCallback(IAsyncResult ar)
    {
        String content = String.Empty;

        StateObject state = (StateObject)ar.AsyncState;
        Socket handler = state.workSocket;
        int bytesRead = handler.EndReceive(ar);

        if (bytesRead > 0)
        {
            state.sb.Append(Encoding.ASCII.GetString(
            state.buffer, 0, bytesRead));
        }
        ClientConnectionFactory.createConnection(ar.AsyncState);
 }
4

1 に答える 1

-1

あなたは間違った道を進んでいると思います。バックログのリクエスト番号を変更しなくても、このシナリオを確実に処理できます。

クライアントの要求はポート 80 で受信されますが、応答はポート 80 でクライアントに返されません。つまり、非同期ソケット処理を使用して要求を受信し、解析のためにそれを渡し、そのように応答することができます。後続のリクエストは、前のリクエストの完全な処理を待っていません。この手法を Padarn Web サーバーで使用しており、単一のクライアント ブラウザーからの複数の要求、または複数の同時クライアントからの複数の要求を処理しても問題はありません。

于 2012-04-13T15:43:45.003 に答える