IIS或者其他Web服务器究竟做了哪些工作,让浏览器请求一个URL地址后显示一个漂亮的网页?要想弄清这个疑问,我想我们可以自己写一个简单的web服务器。

思路:

  1. 创建socket监听浏览器请求。
  2. 连接成功,接受浏览器的请求数据。
  3. 响应浏览器的请求。(我们只响应静态文件) 。

好的,思路很清晰。下面就跟着我动手一步步用代码实现。

1、创建socket监听浏览器请求。

当浏览器请求域名,DNS将域名解析为IP地址。带着缺省的端口80访问我们的服务器。所以Socket的监听,也需要绑定IP和端口。

代码:

  1. // 窗体加载
  2. private void Form1_Load(object sender, EventArgs e)
  3. {
  4. // 关闭线程操作检测
  5. Control.CheckForIllegalCrossThreadCalls = false;
  6. Log("服务器启动中 >>>");
  7. IPAddress ip = IPAddress.Parse(txtIP.Text);
  8. IPEndPoint p = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
  9. socketWatch.Bind(p);
  10. socketWatch.Listen();
  11. // 为了不阻塞主线程,开启后台线程,监听浏览器请求。
  12. Thread t = new Thread(Listen);
  13. t.IsBackground = true;
  14. t.Start();
  15. Log("服务器启动成功。");
  16. }

Listen 进程只需要无限循环接受浏览器发送过来的请求。

private void Listen()

{

   while (true)

   {

     Socket sender=socketWatch.Accept();

     Log(sender.RemoteEndPoint.ToString() + "连接成功");

}

}

辅助方法,显示提示信息。(不重要)

  1. // 显示提示信息
  2. private void Log(string msg)
  3. {
  4. txtMsg.AppendText(msg + "\r\n");
  5. }

执行结果

打开我们的winform程序。

打开浏览器访问: http://192.168.95.1:3099/  地址

就能看到以下结果:

2、连接成功,接受浏览器的请求数据。

分析:

浏览器的请求到达服务器后,我们要接待他,就要问他是过来干嘛的。

要和浏览器交流,那么服务器和浏览器就应该说一种语言。这个语言就是http协议。

(对HTTP协议还不太了解的园友可以参考:http协议知识整理

根据HTTP协议,浏览器请求服务器的格式都是规定好的,

所以我们只需要根据格式进行拆分,获取我们感兴趣的数据。

这件事情,已经不属于form1.cs这个类的能力范围了。所以我们找一个管家HttpManager来帮我们处理。至于怎么处理form1不用管,他只需要告诉管家就可以了。

代码:

在form1.cs的Listen中创建管家

  1. HttpManager manager = new HttpManager(sender, Log);

管家接收基础的数据后,再交给专门处理请求数据的对象HttpRequest。

  1. class HttpManager
  2. {
  3. private Socket socket;
  4. private Action<string> Log;
  5. public HttpManager(Socket socket, Action<string> act)
  6. {
  7. this.socket = socket;
  8. this.Log = act;
  9. // 接收消息
  10. string msg = ReceiveMsg();
  11. Log(msg);
  12.  
  13. try
  14. {
  15. // 去找请求对象,拿到我们需要的数据。
  16. HttpRequest request = new HttpRequest(msg);
  17. }
  18. catch (Exception ex)
  19. {
  20.  
  21. Log(ex.Message);
  22. }
  23. }
  24.  
  25. // 接受浏览器请求报文
  26. private string ReceiveMsg()
  27. {
  28. byte[] date = new byte[ * ];
  29. int count = socket.Receive(date);
  30. return Encoding.UTF8.GetString(date, , count);
  31. }
  32. }

核心的HttpResponse:

  1. class HttpRequest
  2. {
  3. // 请求原始地址 /xxx/xxx.xxxx
  4. public string RawUrl { get; set; }
  5. // 请求类型 Post/Get
  6. public string Method;
  7. public HttpRequest(string msg)
  8. {
  9. string[] lines = msg.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
  10. string[] arrays = lines[].Split(' ');
  11. Method = arrays[];
  12. RawUrl = arrays[];
  13. }
  14. }

3、响应浏览器的请求。

  响应过程其实也非常简单,IO读取浏览器需要请求的数据,拼接响应报文返回给浏览器。

在HttpManager类接收完数据后,调用响应对象。来帮我们处理响应。

  1. // 构造响应报文,响应请求。
  2. HttpResponse response = new HttpResponse(request.RawUrl, socket, Log);

核心HttpResponse代码

  1. class HttpResponse
  2. {
  3. public int StatusCode { get; set; }
  4. public Dictionary<int, string> Dic = new Dictionary<int, string>();
  5. public string ContentType { get; set; }
  6. public int ContentLength { get; set; }
  7. public byte[] Data { get; set; }
  8. public Socket Socket { get; set; }
  9. public Action<string> Log { get; set; }
  10. public HttpResponse(string rawUrl, Socket socket, Action<string> log)
  11. {
  12. this.Socket = socket;
  13. FillDic();
  14. this.Log = log;
  15. // 判断是否为静态资源
  16. if (Judge(rawUrl))
  17. {
  18. // IO 读取资源
  19. Data = ReadFile(rawUrl);
  20. if (Data == null)
  21. {
  22. // 404 没有这个文件
  23. StatusCode = ;
  24. string msg = "没有找到这个文件。";
  25. ContentType = "text/html";
  26. Data = Encoding.Default.GetBytes(msg);
  27. ContentLength = Data.Length;
  28. ContentType = "text/html";
  29. SendData();
  30. }
  31. else
  32. {
  33. ContentLength = Data.Length;
  34. StatusCode = ;
  35. ContentType = GetContentType(rawUrl);
  36. // 响应请求
  37. SendData();
  38. }
  39. }
  40. else
  41. {
  42. // 不处理
  43. string msg = "只处理静态文件。";
  44. Data = Encoding.Default.GetBytes(msg);
  45. ContentLength = Data.Length;
  46. StatusCode = ;
  47. ContentType = "text/html";
  48. // 响应请求
  49. SendData();
  50. }
  51.  
  52. }
  53.  
  54. // 判断文件是否为静态资源
  55. private bool Judge(string rawUrl)
  56. {
  57. bool isStatic = false;
  58. string ext = Path.GetExtension(rawUrl);
  59. switch (ext)
  60. {
  61. case ".html":
  62. case ".htm":
  63. case ".jpg":
  64. case ".png":
  65. case ".gif":
  66. case ".js":
  67. case ".css":
  68. case ".ico": isStatic = true; break;
  69. }
  70. return isStatic;
  71. }
  72.  
  73. // 响应
  74. private void SendData()
  75. {
  76. StringBuilder sb = new System.Text.StringBuilder();
  77. sb.Append("HTTP/1.1 " + StatusCode + " " + Dic[StatusCode] + "\r\n");
  78. sb.Append("Server: ksn/1.0\r\n");
  79. sb.Append("Content-Type: " + ContentType + "\r\n");
  80. sb.Append("Content-Length: " + ContentLength + "\r\n");
  81. sb.Append("\r\n");
  82. Log("响应报文: \r\n" + sb.ToString());
  83.  
  84. byte[] head = Encoding.UTF8.GetBytes(sb.ToString());
  85. List<byte> res = new List<byte>();
  86. res.AddRange(head);
  87. if (this.Data != null)
  88. {
  89. res.AddRange(this.Data);
  90. }
  91. Socket.Send(res.ToArray());
  92.  
  93. }
  94.  
  95. private string GetContentType(string rawUrl)
  96. {
  97. string ct = "text/html";
  98. string ext = Path.GetExtension(rawUrl);
  99. switch (ext)
  100. {
  101. case ".js": ct = "text/javascript";
  102. break;
  103. case ".css": ct = "text/css";
  104. break;
  105. case ".jpg": ct = "image/jpeg";
  106. break;
  107. case ".png": ct = "image/png";
  108. break;
  109. case ".gif": ct = "image/gif";
  110. break;
  111. case ".ico": ct = "image/x-ico";
  112. break;
  113. }
  114. return ct;
  115. }
  116.  
  117. // 初始化状态码
  118. private void FillDic()
  119. {
  120. Dic.Add(, "Ok");
  121. Dic.Add(, "Not Found");
  122. Dic.Add(, "Internal Error");
  123. }
  124.  
  125. // 读取请求的资源
  126. private byte[] ReadFile(string rawUrl)
  127. {
  128. string path = AppDomain.CurrentDomain.BaseDirectory;
  129. path = path + "web" + rawUrl;
  130. if (File.Exists(path))
  131. {
  132. using (FileStream fs = new FileStream(path, FileMode.Open))
  133. {
  134. byte[] data = new byte[fs.Length];
  135. fs.Read(data, , data.Length);
  136. return data;
  137. }
  138. }
  139. else
  140. {
  141. return null;
  142. }
  143.  
  144. }
  145. }

  这个类的代码比较多,但是不难,都是按照顺序一步步往下走的。其实web服务器大致就做了这些事情。只不过封装的东西更多,功能更完善。

资源:

项目源码下载地址

一步步搭建自己的web服务器的更多相关文章

  1. linux系统下搭建自己的web服务器

    之前在windows 2008 server上搭建了一个用于测试的web服务器,但是在打开网站的时候特别的慢,尤其是图片的加载都会失败,当时以为是路径的问题,但是在服务器上自己打开都特别慢,自己实在找 ...

  2. [阿里云部署] Ubuntu+Flask+Nginx+uWSGI+Mysql搭建阿里云Web服务器

    部署地址:123.56.7.181 Ubuntu+Flask+Nginx+uWSGI+Mysql搭建阿里云Web服务器 这个标题就比之前的"ECS服务器配置Web环境的全过程及参考资料&qu ...

  3. 基于python2【重要】怎么自行搭建简单的web服务器

    基本流程:1.需要的支持     1)python本身有SimpleHTTPServer     2)ForkStaticServer.py支持,该文件放在python7目录下     3)将希望共享 ...

  4. nginx搭建前端项目web服务器以及利用反向代理调试远程后台接口

    前端同学用nginx搭建自己的web服务器,后台程序专门部署在一台服务器上(我们之前公司就有三套环境,开发/测试/生产),这样做的好处是 1.前端代码基本都是静态文件,重启一次很快,也就几秒钟时间. ...

  5. 使用 Nginx 搭建静态资源 web 服务器

    在搭建网站的时候,往往会加载很多的图片,如果都从 Tomcat 服务器来获取静态资源,这样会增加服务器的负载,使得服务器运行 速度非常慢,这时可以使用 Nginx 服务器来加载这些静态资源,这样就可以 ...

  6. 用go-module作为包管理器搭建go的web服务器

    本篇博客主要介绍了如何从零开始,使用Go Module作为依赖管理,基于Gin来一步一步搭建Go的Web服务器.并使用Endless来使服务器平滑重启,使用Swagger来自动生成Api文档. 源码在 ...

  7. 使用 Nodejs 搭建简单的Web服务器

    使用Nodejs搭建Web服务器是学习Node.js比较全面的入门教程,因为要完成一个简单的Web服务器,你需要学习Nodejs中几个比较重要的模块,比如:http协议模块.文件系统.url解析模块. ...

  8. qingshow “不积跬步无以至千里,不积小流无以成江海”。--荀子《劝学篇》 用tomcat+花生壳搭建自己的web服务器+域名(参考)

    链接地址:http://www.blogjava.net/qingshow/archive/2010/01/17/309846.html 用tomcat搭建web服务器 目标:免费拥有自己的网站及域名 ...

  9. 【小姿势】如何搭建ipa下载web服务器(直接在手机打开浏览器安装)

    前提: 1) 有个一个现成的web服务器,我用是nodejs. 2) 有个能在用你手机安装的ipa 3) 有个github账号 开搞: 1.用http://plist.iosdev.top/plist ...

随机推荐

  1. Servlet3.0异步请求

    在Servlet3.0之前,Servlet采用Thread-Per-Request的方式处理请求 即每次Http请求都有一个线程从头到尾负责处理 如果一个请求需要进行IO操作,比如访问数据库.调用第三 ...

  2. Axis.Labels.CustomSize

    tChart1.Axes.Bottom.Labels.CustomSize = ; //Changes spacing occupied by the axis labels between the ...

  3. window_onload和body_onload

    onload 事件 Event 对象 定义和用法 onload 事件会在页面或图像加载完成后立即发生. 语法 onload="SomeJavaScriptCode" 参数 描述 S ...

  4. matplotlib 初步学习

    author:pprp Matplotlib数据可视化 [TOC] 安装 conda install matplotlib sudo apt-get install python-matplotlib ...

  5. vc 导出函数/调用

    loader(exe): #include "stdafx.h" #include <Windows.h> #include <stdio.h> #defi ...

  6. 从Github上轻松安装R包—githubinstall包--转载

    1.综述 越来越多的R包正在由世界上不同的人所创建,其中一部分原因是devtools包使得开发R包1变得更加简单.devtools包不仅让开发R包变得简单,而且用于分发R包. 当开发者发布一个R包的时 ...

  7. Android显示框架:自定义View实践之绘制篇

    文章目录 一 View 二 Paint 2.1 颜色处理 2.2 文字处理 2.3 特殊处理 三 Canvas 3.1 界面绘制 3.2 范围裁切 3.3 集合变换 四 Path 4.1 添加图形 4 ...

  8. Unity使用Win10语音

    1.    引入头文件 using UnityEngine.Windows.Speech; 2.    设置识别词 public string[] keywords = new string[] { ...

  9. 如何成为 Python 高手

    这篇文章主要是对我收集的一些文章的摘要.因为已经有很多比我有才华的人写出了大量关于如何成为优秀Python程序员的好文章. 我的总结主要集中在四个基本题目上:函数式编程,性能,测试,编码规范.如果一个 ...

  10. UVALive - 6709树套树

    题意:给你一个矩阵,q次操作,每次查询长宽l的矩阵最大值a和最小值b,然后把中间点换成floor((a+b)/2), 解法:暴力可过,建n颗线段树暴力更新,但是正解应该是树套树,树套树需要注意的是当建 ...