【轮子狂魔】抛弃IIS,向天借个HttpListener

8/3/2015来源:C#应用人气:1476

【轮子狂魔】抛弃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