• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

【轮子狂魔】抛弃IIS,打造个性的Web Server - WebAPI/Lua/MVC(附带源码) ...

原作者: [db:作者] 来自: [db:来源] 收藏 邀请
引言

此篇是《【轮子狂魔】抛弃IIS,向天借个HttpListener - 基础篇(附带源码)》的续篇,也可以说是提高篇,如果你对HttpListener不甚了解的话,建议先看下基础篇。

这次玩的东西有点多了,大致分为如下几个方向:

1.支持静态页面

2.Ur映射l执行方法

3.Url映射执行Lua脚本

4.模仿MVC中的C

 

这些东西有什么用?

支持静态页面:这个纯属玩具吧,只是基础篇作为引子的一个简单示例而已。

Url映射执行方法:类似Web API,可以提供一些基于Http协议的交互方式。

Url映射执行Lua脚本:与上面一样,不同的是,在某些场景下更合适,比如业务频繁变动、频繁发布。Lua脚本我就不细说了,不清楚的可以百度一下,是个很好玩的东西。

模仿MVC中的C:这个实例只是想说基于HttpListener我们可以做很多事情,如果你对ASP.NET、MVC很熟悉,你可以做个整整的Web Server出来,甚至做个Web框架都可以。

 

那么除了这些还可以做什么?

其实太多了,比如反向代理、负载均衡、黑名单等等,你都可以做。如果你有兴趣可以跟帖讨论 ^_^

 

改造HttpServer支持横向扩展

 抽离出一个接口:HttpImplanter

1 interface HttpImplanter
2     {
3         void Start();
4         void Stop();
5         void MakeHttpPrefix(HttpListener server);
6         ReturnCode ProcessRequest(HttpListenerContext context);
7         byte[] CreateReturnResult(HttpListenerContext context, ReturnCode result);
8     }
View Code

改造HttpServer的一些执行细节

  1 /// <summary>
  2     /// 可接收Http请求的服务器
  3     /// </summary>
  4     class HttpServer
  5     {
  6         Thread _httpListenThread;
  7 
  8         /// <summary>
  9         /// HttpServer是否已经启动
 10         /// </summary>
 11         volatile bool _isStarted = false;
 12 
 13         /// <summary>
 14         /// 线程是否已经结束
 15         /// </summary>
 16         volatile bool _terminated = false;
 17         volatile bool _ready = false;
 18         volatile bool _isRuning = false;
 19         HttpImplanter _httpImplanter;
 20 
 21         public void Start(HttpImplanter httpImplanter)
 22         {
 23             if (!HttpListener.IsSupported)
 24             {
 25                 Logger.Exit("不支持HttpListener!");
 26             }
 27 
 28             if (_isStarted)
 29             {
 30                 return;
 31             }
 32             _isStarted = true;
 33             _ready = false;
 34             _httpImplanter = httpImplanter; 
 35 
 36             RunHttpServerThread();
 37 
 38             while (!_ready) ;
 39         }
 40 
 41         private void RunHttpServerThread()
 42         {
 43             _httpListenThread = new Thread(new ThreadStart(() =>
 44             {
 45                 HttpListener server = new HttpListener();
 46                 try
 47                 {
 48                     _httpImplanter.MakeHttpPrefix(server);
 49                     server.Start();
 50                 }
 51                 catch (Exception ex)
 52                 {
 53                     Logger.Exit("无法启动服务器监听,请检查网络环境。");
 54                 }
 55 
 56                 _httpImplanter.Start();
 57 
 58                 IAsyncResult result = null;
 59                 while (!_terminated)
 60                 {
 61                     while (result == null || result.IsCompleted)
 62                     {
 63                         result = server.BeginGetContext(new AsyncCallback(ProcessHttpRequest), server);
 64                     }
 65                     _ready = true;
 66                     Thread.Sleep(10);
 67                 }
 68 
 69                 server.Stop();
 70                 server.Abort();
 71                 server.Close();
 72                 _httpImplanter.Stop();
 73             }
 74             ));
 75 
 76             _httpListenThread.IsBackground = true;
 77             _httpListenThread.Start();
 78         }
 79 
 80         private void ProcessHttpRequest(IAsyncResult iaServer)
 81         {
 82             HttpListener server = iaServer.AsyncState as HttpListener;
 83             HttpListenerContext context = null;
 84             try
 85             {
 86                 context = server.EndGetContext(iaServer);
 87                 Logger.Info("接收请求" + context.Request.Url.ToString());
 88                 //判断上一个操作未完成,即返回服务器正忙,并开启一个新的异步监听
 89                 if (_isRuning)
 90                 {
 91                     Logger.Info("正在处理请求,已忽略请求" + context.Request.Url.ToString());
 92                     RetutnResponse(context, _httpImplanter.CreateReturnResult(context, new ReturnCode((int)CommandResult.ServerIsBusy, EnumHelper.GetEnumDescription(CommandResult.ServerIsBusy))));
 93                     server.BeginGetContext(new AsyncCallback(ProcessHttpRequest), server);
 94                     return;
 95                 }
 96 
 97                 _isRuning = true;
 98                 server.BeginGetContext(new AsyncCallback(ProcessHttpRequest), server);
 99             }
100             catch
101             {
102                 Logger.Warning("服务器已关闭!");
103                 return;
104             }
105 
106             string scriptName = new UrlHelper(context.Request.Url).ScriptName;
107             byte[] resultBytes = null;
108             if (scriptName.ToLower().EndsWith(".html")||scriptName == "favicon.ico")
109             {
110                 string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Web", scriptName);
111                 if (File.Exists(filePath))
112                 {
113                     resultBytes = File.ReadAllBytes(filePath);
114                 }
115                 else
116                 {
117                     resultBytes = _httpImplanter.CreateReturnResult(context, new ReturnCode((int)CommandResult.FileNotExists, EnumHelper.GetEnumDescription(CommandResult.FileNotExists)));
118                 }
119             }
120             else
121             {
122                 ReturnCode result = _httpImplanter.ProcessRequest(context);
123                 resultBytes = _httpImplanter.CreateReturnResult(context, result);
124             }
125             RetutnResponse(context, resultBytes);
126             _isRuning = false;
127         }
128 
129         private static void RetutnResponse(HttpListenerContext context, byte[] resultBytes)
130         {
131             context.Response.ContentLength64 = resultBytes.Length;
132             System.IO.Stream output = context.Response.OutputStream;
133             try
134             {
135                 output.Write(resultBytes, 0, resultBytes.Length);
136                 output.Close();
137             }
138             catch
139             {
140                 Logger.Warning("客户端已经关闭!");
141             }
142         }
143 
144         public void Stop()
145         {
146             if (!_isStarted)
147             {
148                 return;
149             }
150 
151             _terminated = true;
152             _httpListenThread.Join();
153 
154             _isStarted = false;
155         }
156 
157     }
View Code

 

Url映射执行方法

1.继承HttpImplanter

2.增加监听前缀,用于过滤Url

3.创建返回结果

   3.1 解析Url

   3.2 定位访问的类

   3.3 执行Url所表示的方法

 1 public class MethodHttpServer : HttpImplanter
 2     {
 3         #region HttpImplanter 成员
 4 
 5         public void Start()
 6         {
 7             //nothing to do
 8         }
 9 
10         public void Stop()
11         {
12             //nothing to do
13         }
14 
15         public void MakeHttpPrefix(System.Net.HttpListener server)
16         {
17             server.Prefixes.Clear();
18             server.Prefixes.Add("http://localhost:8081/");
19             Logger.Info("MethodHttpServer添加监听前缀:http://localhost:8081/");
20         }
21 
22         public ReturnCode ProcessRequest(System.Net.HttpListenerContext context)
23         {
24             return new ReturnCode((int)CommandResult.Success, EnumHelper.GetEnumDescription(CommandResult.Success));
25         }
26 
27         public byte[] CreateReturnResult(System.Net.HttpListenerContext context, ReturnCode result)
28         {
29             string responseString = string.Empty;
30             UrlHelper urlHelper = new UrlHelper(context.Request.Url);
31             var type = Type.GetType("HttpListenerDemo.Test." + urlHelper.ScriptName);
32             if (type != null)
33             {
34                 object obj = Activator.CreateInstance(type);
35                 responseString = obj.GetType().GetMethod(urlHelper.Parameters["method"]).Invoke(obj, new object[] { urlHelper.Parameters["param"] }) as string;
36             }
37 
38             return System.Text.Encoding.UTF8.GetBytes(responseString);
39         }
40 
41         #endregion
42     }
View Code

测试方法

1 public class TestMethod
2     {
3         public string Test(string msg)
4         {
5             return "TestMethod:" + msg;
6         }
7     }
View Code

 

Url映射执行Lua脚本

 

  使用Lua前要填坑

 首先需要注意的是使用Lua有个坑,我使用的是Lua51.dll,而这个是 .net 2.0版本,而我的程序是 4.0。且这个类库是32位,而我的系统是64位。所以遇到了2个问题。

1.需要修改app.config,增加startup节点,让程序运行时以.net 2.0来加载lua51和LuaInterface这两个程序集。

 1 <startup useLegacyV2RuntimeActivationPolicy="true">
 2     <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
 3     <runtime>
 4       <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
 5         <dependentAssembly>
 6           <assemblyIdentity name="Lua51" publicKeyToken="32ab4ba45e0a69a1" culture="neutral"/>
 7           <codeBase version="v2.0.50727" href="Lua51.dll"/>
 8         </dependentAssembly>
 9         <dependentAssembly>
10           <assemblyIdentity name="LuaInterface" publicKeyToken="32ab4ba45e0a69a1" culture="neutral"/>
11           <codeBase version="v2.0.50727" href="LuaInterface.dll"/>
12         </dependentAssembly>
13       </assemblyBinding>
14     </runtime>
15   </startup>
View Code

2.设置项目属性->生成->目标平台-> x86

此时,坑已经填完,开始正式撸代码了。

 

  实现调用Lua和Url与Lua之间的映射

LuaApiRegister:这个类用于注册Lua虚拟机,告诉Lua虚拟机如果寻找提供给Lua使用的API。

 1     public class LuaApiRegister
 2     {
 3         private Lua _luaVM = null;
 4 
 5         public LuaApiRegister(object luaAPIClass)
 6         {
 7             _luaVM = new Lua();//初始化Lua虚拟机
 8             BindClassToLua(luaAPIClass);
 9         }
10 
11         private void BindClassToLua(object luaAPIClass)
12         {
13             foreach (MethodInfo mInfo in luaAPIClass.GetType().GetMethods())
14             {
15                 foreach (Attribute attr in Attribute.GetCustomAttributes(mInfo, false))
16                 {
17                     if (!attr.ToString().StartsWith("System.") && !attr.ToString().StartsWith("__"))
18                     {
19                         _luaVM.RegisterFunction((attr as LuaFunction).getFuncName(), luaAPIClass, mInfo);
20                     }
21                 }
22             }
23         }
24 
25         public void ExecuteFile(string luaFileName)
26         {
27             Logger.Info("开始执行脚本:" + luaFileName);
28             _luaVM.DoFile(luaFileName);
29         }
30 
31         public void ExecuteString(string luaCommand)
32         {
33             try
34             {
35                 _luaVM.DoString(luaCommand);
36             }
37             catch (Exception e)
38             {
39                 Console.WriteLine("执行lua脚本指令:" + e.ToString());
40             }
41         }
42     }
43  
44     public class LuaFunction : Attribute
45     {
46         private String FunctionName;
47 
48         public LuaFunction(String strFuncName)
49         {
50             FunctionName = strFuncName;
51         }
52 
53         public String getFuncName()
54         {
55             return FunctionName;
56         }
57     }
View Code

 

LuaScriptEngineer:这个类将会映射Url解析后的指令如何执行Lua脚本。其中包括定位Lua文件、设置变量、执行脚本和回收资源等。

  1     public class LuaScriptEngineer
  2     {
  3         public string _scriptRoot;
  4         private string _throwMessage = "";
  5         private ReturnCode _returnCode = null;
  6 
  7         public void ExecuteScript(string scriptName, NameValueCollection parameters)
  8         {
  9             if (!File.Exists(Path.Combine(_scriptRoot, scriptName + ".lua")))
 10             {
 11                 throw new FileNotFoundException();
 12             }
 13 
 14             LuaApiRegister luaHelper = new LuaApiRegister(new TestLuaApiInterface());
 15 
 16             InitLuaGlobalParameter(luaHelper, parameters);
 17 
 18             ExecuteFile(luaHelper, Path.Combine(_scriptRoot, scriptName + ".lua"));
 19 
 20         }
 21 
 22         private void InitLuaGlobalParameter(LuaApiRegister luaHelper, NameValueCollection parameters)
 23         {
 24             foreach (var item in parameters.AllKeys)
 25             {
 26                 luaHelper.ExecuteString("a_" + item.Trim() + " = \"" + parameters[item].Replace("\\", "\\\\") + "\";");
 27             }
 28         }
 29 
 30         private void ExecuteFile(LuaApiRegister luaHelper, string luaFileName)
 31         {
 32             try
 33             {
 34                 _throwMessage = "";
 35                 _returnCode = null;
 36                 luaHelper.ExecuteFile(luaFileName);
 37             }
 38             catch (ReturnCode returnCode)
 39             {
 40                 _returnCode = returnCode;
 41             }
 42             catch (Exception ex)
 43             {
 44                 _throwMessage = ex.Message;
 45             }
 46 
 47             if (_returnCode != null)
 48             {
 49                 throw _returnCode;
 50             }
 51             else if (string.IsNullOrEmpty(_throwMessage))
 52             {
 53                 Logger.Info("脚本执行完毕:" + luaFileName);
 54             }
 55             else if (!string.IsNullOrEmpty(_throwMessage))
 56             {
 57                 Logger.Error(_throwMessage);
 58                 throw new Exception(_throwMessage);
 59             }
 60 
 61         }
 62 
 63         public void Start()
 64         {
 65             _scriptRoot = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Script");
 66 
 67             Logger.Info("脚本路径-" + _scriptRoot);
 68             if (!Directory.Exists(_scriptRoot))
 69             {
 70                 Logger.Error("脚本根路径不存在!");
 71             }
 72 
 73             if (File.Exists(_scriptRoot + "Startup.lua"))
 74             {
 75                 Logger.Info("开始执行初始化脚本!");
 76                 try
 77                 {
 78                     ExecuteScript("Startup", new NameValueCollection());
 79                 }
 80                 catch
 81                 {
 82                     Logger.Error( 
                       
                    
                    

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
在redis里面使用lua发布时间:2022-07-22
下一篇:
luafor循环发布时间:2022-07-22
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap