【輪子狂魔】拋棄IIS,向天借個HttpListener - 基礎篇(附帶源碼)


這一次我們要玩什么?

 先聲明一下,由於這篇是基礎篇主要是通過這篇文章讓大家對使用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/WebServerDemo

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM