二、 編寫一個異步的HTTP服務器和客戶端
本節展示了如何編寫一個簡單的異步HTTP服務器。
1.程序代碼如下。
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace ThreadIODemo { class Program { static void Main(string[] args) { Console.WriteLine("--創建簡單Web服務示例 -- "); var server = new AsyncHttpServer(port: 1234); var t = Task.Run(() => server.Start()); Console.WriteLine("Listening on port 1234.在瀏覽器中打開http://localhost:1234 "); Console.WriteLine("測試連接"); Console.WriteLine(); GetResponseAsync("http://localhost:1234").GetAwaiter().GetResult(); Console.WriteLine(); Console.WriteLine("輸入回車鍵,停止服務"); Console.ReadLine(); string msg= server.Stop().GetAwaiter().GetResult(); Console.WriteLine(msg); Console.Read(); } static async Task GetResponseAsync(string url) { using (var client = new HttpClient()) { HttpResponseMessage responseMessage = await client.GetAsync(url); string responseHeaders = responseMessage.Headers.ToString(); string response = await responseMessage.Content.ReadAsStringAsync(); Console.WriteLine("Response Headers:"); Console.WriteLine(responseHeaders); Console.WriteLine("Response Body:"); Console.WriteLine(response); } } } class AsyncHttpServer { readonly HttpListener _listener; const string RESPONSE_TEMPLATE = "<html><head><title>Test</title></head>
<body><h2>Test Page </h2><h4>ToDay is:{0}</h4></body></html>"; public AsyncHttpServer(int port) { _listener = new HttpListener(); _listener.Prefixes.Add(string.Format("http://+:{0}/", port)); } public async Task Start() { _listener.Start(); while (true) { var ctx = await _listener.GetContextAsync(); Console.WriteLine("{0} Client connected...",DateTime.Now.ToString()); string response = string.Format(RESPONSE_TEMPLATE, DateTime.Now); using (var sw = new StreamWriter(ctx.Response.OutputStream)) { await sw.WriteAsync(response); await sw.FlushAsync(); } } //return string.Format("監聽服務開始 {0} ", DateTime.Now.ToString());
} public async Task<string> Stop() { _listener.Stop(); Console.WriteLine("監聽停止,停止服務"); await Task.Delay(TimeSpan.FromSeconds(2)); return string.Format("監聽服務停止 {0} ",DateTime.Now.ToString()); } } }
2.http服務啟動如下圖。
3.在瀏覽器中輸入http://localhost:1234,之后如下圖1。在等30秒之后,再次刷新,如下圖中的2.
4.在http的web服務程序中可以看到瀏覽器訪問了服務器兩次。如下圖中的紅框。
4.在http的web服務程序中輸入回車,則服務會停止 。如下圖中的紅框。
這里我們通過HTTPListener類實現了一個非常簡單的WEB服務器。也使用了TCPLISTENER類進行TCP套接字I/O操作。我們配置了監聽器接收任何主機到本地機器1234端口的連接。然后在單獨的工作線程中啟動這個監管器,從而在主線程中可以控制這個監聽器。
當使用GetContextAsync方法時會發生異步I/O操作。可惜的是,其並不接收CancellationToken從而實現取消功能。所以如果想關閉這個服務器,只需要調用 _listenter.Abort方法,這將丟棄所有連接並關閉服務器。
為了對這個服務器執行一個異步請求,我們使用了統一命名空間下的System.Net.Http集合中的HttpClient類。我們使用Get.Async方法來發起一個異步的Http Get請求。還有其他的方法用於發起其他的HTTP請求,比如POST,DELETE,PUT。HttpClient還有很多其他的選項,比如使用不同的格式(比如XML和JSON)來序列化和反序列化對象,指定代理服務器地址,認證以及其他配置。
當運行這個程序時,可以看到這個服務器被啟動起來。在服務器端代碼中,我們使用GetContextAsync方法來接收新的客戶端連接。當有新的客戶端連接時這個方法就會返回。我們簡單的輸出一個包含當前日期和時間的非常基礎的HTML作為響應。然后我們請求服務並打印響應頭和內容。