• 一步步搭建自己的web服务器


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

    思路:

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

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

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

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

    代码:

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

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

    private void Listen()

    {

       while (true)

       {

         Socket sender=socketWatch.Accept();

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

           }

    }

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

    // 显示提示信息
    private void Log(string msg)
    {
        txtMsg.AppendText(msg + "
    ");
    }

    执行结果

    打开我们的winform程序。

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

    就能看到以下结果:

     

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

    分析:

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

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

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

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

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

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

    代码:

    在form1.cs的Listen中创建管家

    HttpManager manager = new HttpManager(sender, Log);

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

    class HttpManager
    {
        private Socket socket;
        private Action<string> Log;
        public HttpManager(Socket socket, Action<string> act)
        {
            this.socket = socket;
            this.Log = act;
            // 接收消息
            string msg = ReceiveMsg();
            Log(msg);
    
            try
            {
              // 去找请求对象,拿到我们需要的数据。
                HttpRequest request = new HttpRequest(msg);
            }
            catch (Exception ex)
            {
    
                Log(ex.Message);
            }
        }
    
        // 接受浏览器请求报文
        private string ReceiveMsg()
        {
            byte[] date = new byte[1024 * 1024];
            int count = socket.Receive(date);
            return Encoding.UTF8.GetString(date, 0, count);
        }
    }

    核心的HttpResponse:

    class HttpRequest
    {
        // 请求原始地址 /xxx/xxx.xxxx
        public string RawUrl { get; set; }       
        // 请求类型 Post/Get
        public string Method;
        public HttpRequest(string msg)
        {            
            string[] lines = msg.Split(new string[] { "
    " }, StringSplitOptions.RemoveEmptyEntries);
            string[] arrays = lines[0].Split(' ');
            Method = arrays[0];
            RawUrl = arrays[1];
        }
    }

    3、响应浏览器的请求。

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

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

    // 构造响应报文,响应请求。
    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 = 404;
     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 = 200;
     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 = 500;
     47             ContentType = "text/html";
     48             // 响应请求
     49             SendData();
     50         }
     51 
     52 
     53     }
     54 
     55 
     56     // 判断文件是否为静态资源
     57     private bool Judge(string rawUrl)
     58     {
     59         bool isStatic = false;
     60         string ext = Path.GetExtension(rawUrl);
     61         switch (ext)
     62         {
     63             case ".html":
     64             case ".htm":
     65             case ".jpg":
     66             case ".png":
     67             case ".gif":
     68             case ".js":
     69             case ".css":
     70             case ".ico": isStatic = true; break;
     71         }
     72         return isStatic;
     73     }
     74 
     75     // 响应
     76     private void SendData()
     77     {
     78         StringBuilder sb = new System.Text.StringBuilder();
     79         sb.Append("HTTP/1.1 " + StatusCode + " " + Dic[StatusCode] + "
    ");
     80         sb.Append("Server: ksn/1.0
    ");
     81         sb.Append("Content-Type: " + ContentType + "
    ");
     82         sb.Append("Content-Length: " + ContentLength + "
    ");
     83         sb.Append("
    ");
     84         Log("响应报文: 
    " + sb.ToString());
     85 
     86         byte[] head = Encoding.UTF8.GetBytes(sb.ToString());
     87         List<byte> res = new List<byte>();
     88         res.AddRange(head);
     89         if (this.Data != null)
     90         {
     91             res.AddRange(this.Data);
     92         }
     93         Socket.Send(res.ToArray());
     94 
     95     }
     96 
     97 
     98     private string GetContentType(string rawUrl)
     99     {
    100         string ct = "text/html";
    101         string ext = Path.GetExtension(rawUrl);
    102         switch (ext)
    103         {
    104             case ".js": ct = "text/javascript";
    105                 break;
    106             case ".css": ct = "text/css";
    107                 break;
    108             case ".jpg": ct = "image/jpeg";
    109                 break;
    110             case ".png": ct = "image/png";
    111                 break;
    112             case ".gif": ct = "image/gif";
    113                 break;
    114             case ".ico": ct = "image/x-ico";
    115                 break;
    116         }
    117         return ct;
    118     }
    119 
    120     // 初始化状态码
    121     private void FillDic()
    122     {
    123         Dic.Add(200, "Ok");
    124         Dic.Add(404, "Not Found");
    125         Dic.Add(500, "Internal Error");
    126     }
    127 
    128     // 读取请求的资源
    129     private byte[] ReadFile(string rawUrl)
    130     {
    131         string path = AppDomain.CurrentDomain.BaseDirectory;
    132         path = path + "web" + rawUrl;
    133         if (File.Exists(path))
    134         {
    135             using (FileStream fs = new FileStream(path, FileMode.Open))
    136             {
    137                 byte[] data = new byte[fs.Length];
    138                 fs.Read(data, 0, data.Length);
    139                 return data;
    140             }
    141         }
    142         else
    143         {
    144             return null;
    145         }
    146 
    147 
    148     }
    149 }
    View Code

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

    资源:

    项目源码下载地址

  • 相关阅读:
    leetcode950
    leetcode938
    leetcode953
    推荐系统那点事儿
    极大似然估计的理解与应用
    吴恩达机器学习笔记 —— 1 绪论:初识机器学习
    吴恩达机器学习笔记 —— 3 线性回归回顾
    吴恩达机器学习笔记 —— 5 多变量线性回归
    吴恩达机器学习笔记 —— 7 Logistic回归
    吴恩达机器学习笔记 —— 8 正则化
  • 原文地址:https://www.cnblogs.com/simple-blog/p/4386989.html
Copyright © 2020-2023  润新知