本文基于.net core 的控制台程序作为服务端

main函数:

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Console.WriteLine("The server is starting......");
  6.  
  7. new Server().StartServer();
  8.  
  9. Console.ReadLine();
  10. }
  11. }

其中核心代码在Server这个类上面:

  1. public class Server
  2. {
  3. private Socket socketWatch = null;
  4. private Thread threadWatch = null;
  5. private string ipAddress = "127.0.0.1";
  6. private string port = "";
  7.  
  8. public Server()
  9. {
  10. socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  11. socketWatch.Bind(new IPEndPoint(IPAddress.Parse(ipAddress), int.Parse(port)));
  12. socketWatch.Listen();
  13. // 创建Thread->后台执行
  14. threadWatch = new Thread(ListenClientConnect);
  15. threadWatch.IsBackground = true;
  16. }
  17.  
  18. public void StartServer()
  19. {
  20. threadWatch.Start(socketWatch);
  21. }
  22.  
  23. private void ListenClientConnect(object objSocket)
  24. {
  25. Socket socketListen = objSocket as Socket;
  26.  
  27. while (true)
  28. {
  29. Socket proxSocket = socketListen.Accept();
  30. byte[] data = new byte[ * * ];
  31. int length = proxSocket.Receive(data, , data.Length, SocketFlags.None);
  32. // Step1:接收HTTP请求
  33. string requestText = Encoding.Default.GetString(data, , length);
  34. HttpContext context = new HttpContext(requestText);
  35. // Step2:处理HTTP请求
  36. HttpApplication application = new HttpApplication();
  37. application.ProcessRequest(context);
  38. // Step3:响应HTTP请求
  39. Console.WriteLine(string.Format("{0} {1} from {2}", context.Request.HttpMethod, context.Request.Url, proxSocket.RemoteEndPoint.ToString()));
  40. proxSocket.Send(context.Response.GetResponseHeader());
  41. proxSocket.Send(context.Response.Body);
  42. // Step4:即时关闭Socket连接
  43. proxSocket.Shutdown(SocketShutdown.Both);
  44. proxSocket.Close();
  45. }
  46. }
  47. }

上面代码中,主要是基于Socket和线程。在构造函数中初始化了服务器端Socket,还初始化了Thread,并且设置为后台线程。ListenClientConnect函数主要做的事情是接受浏览器请求,并且转化为HttpContext和HttpApplication,最后输出响应并且关闭socket。

这里面有几个比较重要的类,主要如下:

  1. public class HttpContext
  2. {
  3. public HttpRequest Request { get; set; }
  4. public HttpResponse Response { get; set; }
  5.  
  6. public HttpContext(string requestText)
  7. {
  8. Request = new HttpRequest(requestText);
  9. Response = new HttpResponse();
  10. }
  11. }

HttpContext模拟asp的HttpContext,里面有两个看起来很熟悉的类,HttpRequest和HttpResponse

  1. public class HttpRequest
  2. {
  3. public HttpRequest(string requestText)
  4. {
  5. string[] lines = requestText.Replace("\r\n", "\r").Split('\r');
  6. string[] requestLines = lines[].Split(' ');
  7. // 获取HTTP请求方式、请求的URL地址、HTTP协议版本
  8. if(requestLines.Length >= )
  9. {
  10. HttpMethod = requestLines[];
  11. Url = requestLines[];
  12. HttpVersion = requestLines[];
  13. }
  14. }
  15. // 请求方式:GET or POST?
  16. public string HttpMethod { get; set; }
  17. // 请求URL
  18. public string Url { get; set; }
  19. // Http协议版本
  20. public string HttpVersion { get; set; }
  21. // 请求头
  22. public Dictionary<string, string> HeaderDictionary { get; set; }
  23. // 请求体
  24. public Dictionary<string, string> BodyDictionary { get; set; }
  25. }
  1. public class HttpResponse
  2. {
  3. // 响应状态码
  4. public string StateCode { get; set; }
  5. // 响应状态描述
  6. public string StateDescription { get; set; }
  7. // 响应内容类型
  8. public string ContentType { get; set; }
  9. //响应报文的正文内容
  10. public byte[] Body { get; set; }
  11.  
  12. // 生成响应头信息
  13. public byte[] GetResponseHeader()
  14. {
  15. string strRequestHeader = string.Format(@"HTTP/1.1 {0} {1}
  16. Content-Type: {2}
  17. Accept-Ranges: bytes
  18. Server: Microsoft-IIS/7.5
  19. X-Powered-By: ASP.NET
  20. Date: {3}
  21. Content-Length: {4}
  22.  
  23. ", StateCode, StateDescription, ContentType, string.Format("{0:R}", DateTime.Now), Body.Length);
  24.  
  25. return Encoding.UTF8.GetBytes(strRequestHeader);
  26. }
  27. }

这两个核心类是关于请求和响应的。

IHttpHandler是另外一个很熟悉的接口,一般处理程序中都会实例化它

  1. public interface IHttpHandler
  2. {
  3. void ProcessRequest(HttpContext context);
  4. }

我们处理请求的时候就是依靠实例化这个接口了,在我们的实例上面就是HttpApplication

  1. public class HttpApplication : IHttpHandler
  2. {
  3. // 对请求上下文进行处理
  4. public void ProcessRequest(HttpContext context)
  5. {
  6. // 1.获取网站根路径
  7. if(string.IsNullOrEmpty(context.Request.Url))
  8. {
  9. return;
  10. }
  11. string bastPath = AppDomain.CurrentDomain.BaseDirectory;
  12. string fileName = Path.Combine(bastPath, "LZZWebSite", context.Request.Url.TrimStart('/'));
  13. string fileExtension = Path.GetExtension(context.Request.Url);
  14. // 2.处理动态文件请求
  15. if (fileExtension.Equals(".aspx") || fileExtension.Equals(".ashx"))
  16. {
  17. string className = Path.GetFileNameWithoutExtension(context.Request.Url);
  18. IHttpHandler handler = Assembly.GetExecutingAssembly().CreateInstance($"lzzWebServerDemo.Page.{className}", true) as IHttpHandler;
  19. handler.ProcessRequest(context);
  20. return;
  21. }
  22. // 3.处理静态文件请求
  23. if (!File.Exists(fileName))
  24. {
  25. context.Response.StateCode = "";
  26. context.Response.StateDescription = "Not Found";
  27. context.Response.ContentType = "text/html";
  28. string notExistHtml = Path.Combine(bastPath, @"LZZWebSite\notfound.html");
  29. context.Response.Body = File.ReadAllBytes(notExistHtml);
  30. }
  31. else
  32. {
  33. context.Response.StateCode = "";
  34. context.Response.StateDescription = "OK";
  35. context.Response.ContentType = GetContenType(Path.GetExtension(context.Request.Url));
  36. context.Response.Body = File.ReadAllBytes(fileName);
  37. }
  38. }
  39.  
  40. // 根据文件扩展名获取内容类型
  41. public string GetContenType(string fileExtension)
  42. {
  43. string type = "text/html; charset=UTF-8";
  44. switch (fileExtension)
  45. {
  46. case ".aspx":
  47. case ".html":
  48. case ".htm":
  49. type = "text/html; charset=UTF-8";
  50. break;
  51. case ".png":
  52. type = "image/png";
  53. break;
  54. case ".gif":
  55. type = "image/gif";
  56. break;
  57. case ".jpg":
  58. case ".jpeg":
  59. type = "image/jpeg";
  60. break;
  61. case ".css":
  62. type = "text/css";
  63. break;
  64. case ".js":
  65. type = "application/x-javascript";
  66. break;
  67. default:
  68. type = "text/plain; charset=gbk";
  69. break;
  70. }
  71. return type;
  72. }

上面的业务比较清晰,如果是静态资源,就直接响应返回。如果是动态资源,例如aspx、ashx的话就通过反射实例化对应的处理类。我们例子上是这样模拟的:

  1. public class LzzPage: IHttpHandler
  2. {
  3. public void ProcessRequest(HttpContext context)
  4. {
  5. StringBuilder sbText = new StringBuilder();
  6. sbText.Append("<html>");
  7. sbText.Append("<head></head>");
  8. sbText.Append("<body>");
  9. sbText.Append("<h1>demo</h1>");
  10. sbText.Append("lzzdemolzzdemo");
  11. sbText.Append(string.Format("<h3>time:{0}</h3>", DateTime.Now.ToString()));
  12. sbText.Append("</body>");
  13. sbText.Append("</html>");
  14. context.Response.Body = Encoding.UTF8.GetBytes(sbText.ToString());
  15. context.Response.StateCode = "";
  16. context.Response.ContentType = "text/html";
  17. context.Response.StateDescription = "OK";
  18. }
  19. }

最后来一张整个体统的结构图

运行图:

开发属于自己的Web服务器的更多相关文章

  1. 自己动手模拟开发一个简单的Web服务器

    开篇:每当我们将开发好的ASP.NET网站部署到IIS服务器中,在浏览器正常浏览页面时,可曾想过Web服务器是怎么工作的,其原理是什么?“纸上得来终觉浅,绝知此事要躬行”,于是我们自己模拟一个简单的W ...

  2. Java开发环境配置(5)--Web 服务器--Tomcat--安装过程遇到的问题

    1.参考例子:--- 怎样安装配置tomcat 8_百度经验https://jingyan.baidu.com/article/ff42efa91132a0c19e220208.html 安装与配置T ...

  3. 【安富莱专题教程第3期】开发板搭建Web服务器,利用花生壳让电脑和手机可以外网远程监控

    说明:1.  开发板Web服务器的设计可以看我们之前发布的史诗级网络教程:链接.2.  需要复杂些的Web设计模板,可以使用我们V6开发板发布的综合Demo:链接.3.  教程中使用的是花生壳免费版, ...

  4. [思路]为什么要做一个Web服务器

    对于.net开发者而言,提到Web服务器最容易想到的就是IIS了. IIS功能强大,配置繁多,但不免对普通用户而言过于复杂,另外在云时代的今天,同时维护多个IIS或远程维护IIS还是有诸多不便的,有很 ...

  5. 04-HTTP协议和静态Web服务器

    一.HTTP协议(HyperText Transfer Protocol)     超文本传输协议,超文本是超级文本的缩写,是指超越文本限制或者超链接,比如:图片.音乐.视频.超链接等等都属于超文本. ...

  6. 嵌入式web服务器BOA的移植及应用

    嵌入式web服务器子系统 一.嵌入式web服务器的控制流程 如下图所示,嵌入式web服务器可实现通过网络远程控制嵌入式开发板,便捷实用. 控制流程:浏览器 --->>>嵌入式开发板 ...

  7. Nginx 外的另一选择,轻量级开源 Web 服务器 Tengine 发布新版本

    新版发布 近日,轻量级开源 Web 服务器 Tengine 发布了2.3.0版本,新增如下特性: ngx_http_proxy_connect_module,该模块让 Tengine 可以用于正向代理 ...

  8. ASP.NET 开发必备知识点(1):如何让Asp.net网站运行在自定义的Web服务器上

    一.前言 大家都知道,在之前,我们Asp.net 的网站都只能部署在IIS上,并且IIS也只存在于Windows上,这样Asp.net开发的网站就难以做到跨平台.由于微软的各项技术的开源,所以微软自然 ...

  9. Web服务器具体开发流程

    下面是我个人对Web服务器开发流程的一点理解,下面做出了大概的模型,实现了基本的功能,下面也有所有的代码可以提供参考: 一开始学的时候不要把网站想的太复杂了,把网站的流程和大概的原理框架搞清楚,在通过 ...

随机推荐

  1. Eclipse 安装 AmaterasUML 插件

    网上很多Eclipse 安装UML插件教程,可能对高版本Eclipse都无法安装成功,本文提供的安装方式,亲测可用. 一.安装GEF插件 1.打开eclipse官网 https://www.eclip ...

  2. centos7 防火墙相关命令

    启动:systemctl start firewalld禁用:systemctl stop firewalld重新载入规则:firewall-cmd --reload查看所有打开的端口:firewal ...

  3. springcloud+zuul+swagger 分布式接口文档

    https://gitee.com/didispace/swagger-butler 1.引用上面项目中的swagger 工具包 2.zuul 网关配置 zuul.routes.api-apiserv ...

  4. webpack踩坑--webpack 2.x升级至4.x

    一.安装webpack-cli,webpack@4.26.1 1.npm install webpack-cli -D 2.npm install webpack@4.26.1 -D 二.踩坑 执行n ...

  5. 如何在已安装Python解释器的Linux上更新Python

    在Linux环境下升级Python (附:解决pip报错 subprocess.CalledProcessError: Command '('lsb_release', '-a')' returned ...

  6. php倒计时

    <form name="form1"> <div align="center" align="center"> &l ...

  7. Equal 路由类

    1.Route 原型 class Route { /* 获取请求路径和查询字符串 */ /* 获取模块.控制器.动作名称 */ /* 获取 URI 参数 */ }

  8. Java 异常与反射 总结

    1.异常 异常,简单来说,就是一个程序执行过程中发生的不正常情况的事件.它发生在程序的运行期间,干扰了正常的指令流程.如果没有处理异常,那么出现异常之后,程序会停止运行.异常分为运行异常和非运行异常. ...

  9. xx系统属性分析

    在本周的课程学习当中,我们简单了解到系统的一些属性,同时在课下也对<大型网站技术架构:核心原理与案例分析>进行了初步的阅读. 在书籍中我看到了许多其他的知识,也对课堂学习的知识有了巩固,现 ...

  10. 在Windows平台下Qt的exe报错问题排查步骤

    在Windows平台下Qt的exe报错问题排查步骤 工具介绍: 1. Dependency Worker Dependency Worker是一个免费的用具用来扫描任何的32bit 或者64bit 的 ...