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);
}