先聲明一下,由於這篇是基礎篇主要是通過這篇文章讓大家對使用HttpListener響應Http請求有個大概了解,所以正式的花樣輪子在下一篇推出,敬請期待 ^_^
嗯哼,還有,我標題黨了一下,看完我這個系列的話,在特定場景下可拋棄IIS,但如果完全拋棄IIS就不要想咯 ^_^
HttpListener:提供一個簡單的、可通過編程方式控制的 HTTP 協議偵聽器。(好吧,我承認這句是從MSDN上抄過來的)
既然引子出來了,說明我們要開始玩Http請求了。
那麼我們基礎篇要做的是,如何把一個 html 文件從服務器返回給客戶端。
一個Http請求我們需要做些什麼?
1.監聽一個地址前綴,如:http://localhost/
2.解析Url
3.執行Url所代表的指令
4.返回執行結果
監聽一個Http請求
下面貼出的是主要的代碼,實際源碼中做了一些其他的處理,比如多線程防止界面卡死、HttpListener運行環境檢測、資源釋放、容錯等等。
1 HttpListener server = new HttpListener(); 2 try 3 { 4 MakeHttpPrefix(server); 5 server.Start(); 6 } 7 catch (Exception ex) 8 { 9 Logger.Exit("無法啟動服務器監聽,請檢查網絡環境。"); 10 } 11 12 IAsyncResult result = null; 13 while (!_terminated) 14 { 15 while (result == null || result.IsCompleted) 16 { 17 result = server.BeginGetContext(new AsyncCallback(ProcessHttpRequest), server); 18 } 19 _ready = true; 20 Thread.Sleep(10); 21 } 22 23 server.Stop(); 24 server.Abort(); 25 server.Close(); View Code解析Url
解析Url時需要做幾個事情:
1.Url的長度限制
2.是否包含特殊字符
3.拆分指令與參數
1 /// <summary> 2 /// Url輔助類:對Url進行初步的解析 3 /// </summary> 4 public class UrlHelper 5 { 6 const int MAX_URI_LENGTH = 512; 7 string _scriptName = string.Empty; 8 CommandResult _parseResult = CommandResult.Success; 9 NameValueCollection _parameters = new NameValueCollection(); 10 char[] _uriInvalidChar = new char[] { '/', '\\' }; 11 char[] _pathInvalidChar = new char[] { '/', '\\', ':', '*', '?', '\"', '<', '>', '|' }; 12 public Uri _uri = null; 13 14 public string ScriptName 15 { 16 get { return _scriptName; } 17 } 18 19 public NameValueCollection Parameters 20 { 21 get { return _parameters; } 22 } 23 24 public CommandResult ParseResult 25 { 26 get { return _parseResult; } 27 } 28 29 public UrlHelper(Uri originalUri) 30 { 31 _uri = originalUri; 32 33 if (IsUriLengthError()) 34 { 35 return; 36 } 37 38 if (CheckPathAndQuery()) 39 { 40 ParsePathAndQuery(); 41 } 42 } 43 44 private bool IsUriLengthError() 45 { 46 if (_uri == null || _uri.ToString().Length > MAX_URI_LENGTH) 47 { 48 _parseResult = CommandResult.UrlTooLong; 49 return true; 50 } 51 return false; 52 } 53 54 private bool CheckPathAndQuery() 55 { 56 string pathAndQuery = _uri.PathAndQuery.Substring(1); 57 58 if (IsUrlInvalidChar(pathAndQuery)) 59 { 60 return false; 61 } 62 63 if (pathAndQuery.IndexOfAny(_uriInvalidChar) >= 0) 64 { 65 _parseResult = CommandResult.UrlInvalidChar; 66 return false; 67 } 68 else if (pathAndQuery.Length == 0) 69 { 70 _parseResult = CommandResult.NoExistsMethod; 71 return false; 72 } 73 74 string[] splitPathAndQuery = new string[] { }; 75 if (IsFileNameInvalidChar(pathAndQuery, splitPathAndQuery)) 76 { 77 return false; 78 } 79 80 return true; 81 82 } 83 84 private bool IsFileNameInvalidChar(string pathAndQuery, string[] splitPathAndQuery) 85 { 86 splitPathAndQuery = pathAndQuery.Split(new char[] { '?' }, StringSplitOptions.RemoveEmptyEntries); 87 if (splitPathAndQuery[0].IndexOfAny(_pathInvalidChar) >= 0) 88 { 89 _parseResult = CommandResult.FileNameInvalidChar; 90 return true; 91 } 92 return false; 93 } 94 95 private bool IsUrlInvalidChar(string pathAndQuery) 96 { 97 if (pathAndQuery.IndexOfAny(_uriInvalidChar) >= 0) 98 { 99 _parseResult = CommandResult.UrlInvalidChar; 100 return true; 101 } 102 return false; 103 } 104 105 private void ParsePathAndQuery() 106 { 107 string[] splitPathAndQuery = _uri.PathAndQuery.Substring(1).Split(new char[] { '?' }, StringSplitOptions.RemoveEmptyEntries); 108 SetScriptNameAndParameters(splitPathAndQuery); 109 } 110 111 private void SetScriptNameAndParameters(string[] splitPathAndQuery) 112 { 113 _scriptName = splitPathAndQuery[0]; 114 115 if (splitPathAndQuery.Length > 1) 116 { 117 _parameters = HttpUtility.ParseQueryString(splitPathAndQuery[1], Encoding.UTF8); 118 } 119 } 120 } View Code執行Url所代表的指令和返回執行結果
1.判斷Url的請求文件後綴是否支持
2.檢索本地文件
3.如果文件存在則返回文件,不存在則返回異常(此處在後續擴展活增加更多可變性,比如一些動態執行方法等)
PS:由於此處代碼涉及幾個方法就不貼了,直接看源碼吧。(ProcessHttpRequest 方法)
有圖有真相
請求一個簡單的Hello World的html文件,此處有個細節,就是浏覽器會發送ico請求。聰明的你如果想要顯示ico應該知道怎麼辦吧 ^_^
請求一個不支持的後綴,如:htm
下一次我們玩什麼?
1.豐富一下請求文件類型
2.支持執行方法的請求
3.在HttpListner裡玩一玩LUA腳本
最後,我要放源碼了 ^_^
http://git.oschina.net/doddgu/HttpListenerDemo