1

httpListenerを小さなサーバーアプリに組み込むことを検討しています。それを読んでいる間、私はstackoverflowのこの質問で次のコードスニペットに遭遇しました

Public Class HTTPServer

Shared Listener As HttpListener = New HttpListener

Public Shared Sub Start()

    ServicePointManager.DefaultConnectionLimit = 500
    ServicePointManager.Expect100Continue = False
    ServicePointManager.MaxServicePoints = 500

    Listener.Prefixes.Add("http://localhost/")
    Listener.Start()

    For i As Integer = 1 To (System.Environment.ProcessorCount * 2)

        Dim NewThread As New System.Threading.Thread(AddressOf ListenerThread)
        NewThread.Priority = ThreadPriority.Normal
        NewThread.IsBackground = True
        NewThread.Start()

    Next

End Sub



Private Shared Sub ListenerThread()

    Dim SyncResult As IAsyncResult

    While True

        SyncResult = Listener.BeginGetContext(New AsyncCallback(AddressOf ListenerCallback), Listener)
        SyncResult.AsyncWaitHandle.WaitOne()

    End While

End Sub



Private Shared Sub ListenerCallback(ByVal StateObject As IAsyncResult)

    Dim Listener As HttpListener = DirectCast(StateObject.AsyncState, HttpListener)

    Dim Context As HttpListenerContext = Listener.EndGetContext(StateObject)
    Dim Request As HttpListenerRequest = Context.Request

    Dim Response As HttpListenerResponse = Context.Response

    Dim ResponseString As String = "OK"

    Dim Buffer As Byte() = System.Text.Encoding.UTF8.GetBytes(ResponseString)
    Response.ContentLength64 = Buffer.Length
    Dim OutputStream As System.IO.Stream = Response.OutputStream
    OutputStream.Write(Buffer, 0, Buffer.Length)

    OutputStream.Close()
    OutputStream.Dispose()

End Sub

End Class

これは非常に単純に見え、msdnの例によく似ています。ただし、ダミープロジェクトでテストしたところ、混乱することがいくつかありました。たとえば、コールバックサブからUIオブジェクトに直接アクセスできるため、クロススレッド例外が発生するはずです。

明確にするために、単純なwinformsプロジェクトのメインフォーム内で実行するようにこのコードを少し変更しました。

コードを完全に理解していないようです。たとえば、AsyncWaitHandle.WaitOne()は私にとってまったく新しいものです。

誰かがこのスニペットを簡単に説明してもらえますか?助けていただければ幸いです。

4

2 に答える 2

2

このスニペットコードは次のようになります。

  1. HttpListenerを作成し、URIでlinseningを開始します。
  2. いくつかのスレッド(System.Environment.ProcessorCount * 2の数)を作成して、非同期要求をリスナーに送信し、コンテキストを取得しようとします。
  3. 次に、SyncResult.AsyncWaitHandle.WaitOne()の呼び出しによってスレッドがブロックされました。AsyncWaitHandle.WaitOneについては、AsyncWaitHandle.WaitOneの詳細を確認してください。
  4. クライアントリクエストが着信すると、ListenerCallback()がトリガーされ、クライアントに応答を返そうとします。
  5. 次に、ループの次のラウンドに進みます。

SyncResult.AsyncWaitHandle.WaitOne()を呼び出してそのスレッドをブロックしたため、同期呼び出しでも同じ結果が得られます。

私の意見では、単純なOK応答を返すだけなので、他のスレッドも必要ありません。すぐにコードをお見せします(重い操作を行うと、私のコードはうまく機能しません。フォームアプリケーションでこれを行わないでください)。

    static void Main(string[] args)
    {
        ServicePointManager.DefaultConnectionLimit = 500;
        ServicePointManager.Expect100Continue = false;
        ServicePointManager.MaxServicePoints = 500;

        HttpListener listener = new HttpListener();
        listener.Prefixes.Add("http://localhost:999/");
        listener.Start();
        listener.BeginGetContext(ListenerCallBack, listener);


        Console.ReadLine();
    }

    private static void ListenerCallBack(IAsyncResult result)
    {
        HttpListener httpListener = (HttpListener) result.AsyncState;
        // Call EndGetContext to complete the asynchronous operation.
        HttpListenerContext context = httpListener.EndGetContext(result);
        HttpListenerRequest request = context.Request;
        // Obtain a response object.
        HttpListenerResponse response = context.Response;
        // Construct a response. 
        string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
        byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
        // Get a response stream and write the response to it.
        response.ContentLength64 = buffer.Length;
        System.IO.Stream output = response.OutputStream;
        output.Write(buffer,0,buffer.Length);
        // You must close the output stream.
        output.Close();


        // we call BeginGetContext() to send async request again for next coming client
        httpListener.BeginGetContext(ListenerCallBack, httpListener);
    }

C#5とNet 4.5には、同期方式があり、はるかに簡単になります。

static void Main(string[] args)
    {
        ServicePointManager.DefaultConnectionLimit = 500;
        ServicePointManager.Expect100Continue = false;
        ServicePointManager.MaxServicePoints = 500;

        HttpListener listener = new HttpListener();
        listener.Prefixes.Add("http://localhost:999/");
        listener.Start();

        while (true)
        {
            var t = listener.GetContextAsync();
            HttpListenerContext context = t.Result;
            HttpListenerRequest request = context.Request;
            // Obtain a response object.
            HttpListenerResponse response = context.Response;
            // Construct a response. 
            string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
            byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
            // Get a response stream and write the response to it.
            response.ContentLength64 = buffer.Length;
            System.IO.Stream output = response.OutputStream;
            output.Write(buffer, 0, buffer.Length);
            // You must close the output stream.
            output.Close();
        }
    }

UIの「クロススレッド例外」に関する別の質問については、その通りです。作成された新しいスレッドのSynchronizationContext.Currentは、スレッドプールスレッドであるため、フォームのSynchronizationContextでpost()を実行する必要があります。詳細については、http: //msdn.microsoft.com/en-us/magazine/gg598924.aspxおよびhttp://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspxを参照してください。

于 2012-09-04T07:46:15.597 に答える
0

HttpListener(名前:サーバー)、BackgroundWorker(名前:bw_server)、2つのボタン(名前:btn_start&btn_stop)、およびTextBox(名前:tb_log)を備えた単純なC#Webサーバー:

public partial class Form1 : Form
{
    HttpListener server;
    private void btn_start_Click(object sender, EventArgs e)
    {
        bw_server.RunWorkerAsync(@"http://+:80/");
    }

    private void bw_server_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
    {
        string prefix = e.Argument.ToString();
        bw_server.ReportProgress(0, "Starting server...");
        if (!bw_server.CancellationPending)
        {
            try
            {
                start_server(prefix);
            }
            catch (Exception ex)
            {
                bw_server.ReportProgress(0, ex.Message);
                bw_server.CancelAsync();
            }
        }
        else
        {
            e.Cancel = true;
            return;
        }
    }

    private void bw_server_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
    {
        log(e.UserState.ToString());
    }

    private void bw_server_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled)
        {
            log("Server was stopped");
        }
        else
        {
            log("Server work was completed");
        }
    }

    private void log(string msg)
    {
        if (String.IsNullOrEmpty(msg)) return;
        tb_log.AppendText((tb_log.Text == String.Empty ? String.Empty : Environment.NewLine) + DateTime.Now.ToLongTimeString() + " " + msg);
    }

    private void btn_stop_Click(object sender, EventArgs e)
    {
        WebRequest.Create("http://localhost:" + port + "/?stop=1").GetResponse();
        bw_server.CancelAsync();
    }

    private void start_server(string prefix)
    {
        server = new HttpListener();
        if (!HttpListener.IsSupported) return;
        if (String.IsNullOrEmpty(prefix))
        {
            throw new ArgumentException("prefix");
        }
        server.Prefixes.Add(prefix);
        server.Start();
        while (server.IsListening)
        {
            HttpListenerContext context = server.GetContext();
            HttpListenerRequest request = context.Request;
            if (request.HttpMethod == "GET")
            {
                HttpListenerResponse response = context.Response;
                response.ContentType = "text/html; charset=UTF-8";
                if (request.QueryString["stop"] == "1")
                {
                    write_to_resp(response, "server was stopped");
                    server.Stop();
                }
                else
                {
                    write_to_resp(response, "bad params");
                }
            }
        }
    }

    private static void write_to_resp(HttpListenerResponse response, string str_resp)
    {
        byte[] buffer = Encoding.UTF8.GetBytes(str_resp);
        response.ContentLength64 = buffer.Length;
        response.OutputStream.Write(buffer, 0, buffer.Length);
    }
}
于 2016-12-05T09:20:52.290 に答える