【輪子狂魔】拋棄IIS,向天借個HttpListener,狂魔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/HttpListenerDemo