时间:2023-05-24 04:12:01 | 来源:网站运营
时间:2023-05-24 04:12:01 来源:网站运营
自己动手实现网络服务器(Web Server):class Program{ static void Main(string[] args) { string port = "8080"; HttpListener httpListener = new HttpListener(); httpListener.Prefixes.Add(string.Format("http://+:{0}/", port)); httpListener.Start(); httpListener.BeginGetContext(new AsyncCallback(GetContext), httpListener); //开始异步接收request请求 Console.WriteLine("监听端口:" + port); Console.Read(); } static void GetContext(IAsyncResult ar) { HttpListener httpListener = ar.AsyncState as HttpListener; HttpListenerContext context = httpListener.EndGetContext(ar); //接收到的请求context(一个环境封装体) httpListener.BeginGetContext(new AsyncCallback(GetContext), httpListener); //开始 第二次 异步接收request请求 HttpListenerRequest request = context.Request; //接收的request数据 HttpListenerResponse response = context.Response; //用来向客户端发送回复 response.ContentType = "html"; response.ContentEncoding = Encoding.UTF8; using (Stream output = response.OutputStream) //发送回复 { byte[] buffer = Encoding.UTF8.GetBytes("要返回的内容"); output.Write(buffer, 0, buffer.Length); } }}
这个简单的代码已经可以实现用于小白机器人的网络请求处理了,因为大致只用到GET和POST两种HTTP方法,只需要在GetContext方法里判断GET、POST方法,然后分别给出响应就可以了。/// <summary>/// MIME类型/// </summary>public Dictionary<string, string> MIME_Type = new Dictionary<string, string>(){ { "htm", "text/html" }, { "html", "text/html" }, { "php", "text/html" }, { "xml", "text/xml" }, { "json", "application/json" }, { "txt", "text/plain" }, { "js", "application/x-javascript" }, { "css", "text/css" }, { "bmp", "image/bmp" }, { "ico", "image/ico" }, { "png", "image/png" }, { "gif", "image/gif" }, { "jpg", "image/jpeg" }, { "jpeg", "image/jpeg" }, { "webp", "image/webp" }, { "zip", "application/zip"}, { "*", "*/*" }};
剧透一下:其中有PHP类型是我们后面要使用CGI接入的方式使我们的服务器支持PHP。/// <summary>/// 启动本地网页服务器/// </summary>/// <param name="webroot">网站根目录</param>/// <returns></returns>public bool Start(string webroot){ //触发事件 if (OnServerStart != null) OnServerStart(httpListener); WebRoot = webroot; try { //监听端口 httpListener.Prefixes.Add("http://+:" + port.ToString() + "/"); httpListener.Start(); httpListener.BeginGetContext(new AsyncCallback(onWebResponse), httpListener); //开始异步接收request请求 } catch (Exception ex) { Qdb.Error(ex.Message, QDebugErrorType.Error, "Start"); return false; } return true;}
现在把网页服务器的核心处理代码贴出来。/// <summary>/// 网页服务器相应处理/// </summary>/// <param name="ar"></param>private void onWebResponse(IAsyncResult ar){ byte[] responseByte = null; //响应数据 HttpListener httpListener = ar.AsyncState as HttpListener; HttpListenerContext context = httpListener.EndGetContext(ar); //接收到的请求context(一个环境封装体) httpListener.BeginGetContext(new AsyncCallback(onWebResponse), httpListener); //开始 第二次 异步接收request请求 //触发事件 if (OnGetRawContext != null) OnGetRawContext(context); HttpListenerRequest request = context.Request; //接收的request数据 HttpListenerResponse response = context.Response; //用来向客户端发送回复 //触发事件 if (OnGetRequest != null) OnGetRequest(request, response); if (rawUrl == "" || rawUrl == "/") //单纯输入域名或主机IP地址 fileName = WebRoot + @"/index.html"; else if (rawUrl.IndexOf('.') == -1) //不带扩展名,理解为文件夹 fileName = WebRoot + @"/" + rawUrl.SubString(1) + @"/index.html"; else { int fileNameEnd = rawUrl.IndexOf('?'); if (fileNameEnd > -1) fileName = rawUrl.Substring(1, fileNameEnd - 1); fileName = WebRoot + @"/" + rawUrl.Substring(1); } //处理请求文件名的后缀 string fileExt = Path.GetExtension(fileName).Substring(1); if (!File.Exists(fileName)) { responseByte = Encoding.UTF8.GetBytes("404 Not Found!"); response.StatusCode = (int)HttpStatusCode.NotFound; } else { try { responseByte = File.ReadAllBytes(fileName); response.StatusCode = (int)HttpStatusCode.OK; } catch (Exception ex) { Qdb.Error(ex.Message, QDebugErrorType.Error, "onWebResponse"); response.StatusCode = (int)HttpStatusCode.InternalServerError; } } if (MIME_Type.ContainsKey(fileExt)) response.ContentType = MIME_Type[fileExt]; else response.ContentType = MIME_Type["*"]; response.Cookies = request.Cookies; //处理Cookies response.ContentEncoding = Encoding.UTF8; using (Stream output = response.OutputStream) //发送回复 { try { output.Write(responseByte, 0, responseByte.Length); } catch (Exception ex) { Qdb.Error(ex.Message, QDebugErrorType.Error, "onWebResponse"); response.StatusCode = (int)HttpStatusCode.InternalServerError; } }}
这样就可以提供基本的网页访问了,经过测试,使用Bootstrap,Pure等前端框架的网页都可以完美访问,性能方面一般般。(在QFramework的封装中我做了一点性能优化,有一点提升)我觉得要在性能方面做提升还是要在多线程处理这方面做优化,由于篇幅关系,就不把多线程版本的代码贴出来了。/// <summary>/// 是否开启PHP功能/// </summary>public bool PHP_CGI_Enabled = true;/// <summary>/// PHP执行文件路径/// </summary>public string PHP_CGI_Path = "php-cgi";
接下来在网页服务的核心代码里做PHP支持的处理。//PHP处理string phpCgiOutput = "";Action phpProc = new Action(() =>{ try { string argStr = ""; if (request.HttpMethod == "GET") { if (rawUrl.IndexOf('?') > -1) argStr = rawUrl.Substring(rawUrl.IndexOf('?')); } else if (request.HttpMethod == "POST") { using (StreamReader reader = new StreamReader(request.InputStream)) { argStr = reader.ReadToEnd(); } } Process p = new Process(); p.StartInfo.CreateNoWindow = false; //不显示窗口 p.StartInfo.RedirectStandardOutput = true; //重定向输出 p.StartInfo.RedirectStandardInput = false; //重定向输入 p.StartInfo.UseShellExecute = false; //是否指定操作系统外壳进程启动程序 p.StartInfo.FileName = PHP_CGI_Path; p.StartInfo.Arguments = string.Format("-q -f {0} {1}", fileName, argStr); p.Start(); StreamReader sr = p.StandardOutput; while (!sr.EndOfStream) { phpCgiOutput += sr.ReadLine() + Environment.NewLine; } responseByte = sr.CurrentEncoding.GetBytes(phpCgiOutput); } catch (Exception ex) { Qdb.Error(ex.Message, QDebugErrorType.Error, "onWebResponse->phpProc"); response.StatusCode = (int)HttpStatusCode.InternalServerError; }});if (fileExt == "php" && PHP_CGI_Enabled){ phpProc();}else{ if (!File.Exists(fileName)) { responseByte = Encoding.UTF8.GetBytes("404 Not Found!"); response.StatusCode = (int)HttpStatusCode.NotFound; } else { try { responseByte = File.ReadAllBytes(fileName); response.StatusCode = (int)HttpStatusCode.OK; } catch (Exception ex) { Qdb.Error(ex.Message, QDebugErrorType.Error, "onWebResponse"); response.StatusCode = (int)HttpStatusCode.InternalServerError; } }}
这样就实现了基于PHP-CGI的PHP支持了,经过测试,基本的php页面都可以支持,但是需要使用curl和xml这类扩展的暂时还没办法。需要做更多的工作。关键词:服务,网络,动手,实现