• Asp.net学习第一天OurHttpServer


    今天开始学习ASP.NET,这个对于我来说是个非常陌生的玩意,当然要从基本的开始学起了.晚上把今天的学习知识整理了下.

    --我们到底是怎么看到网站的?

    当我们在浏览器的地址栏里输入地址,敲回车后,浏览器到底做什么一些什么事情了?用户在浏览器地址栏输入网址www.baidu.com,浏览器先看当前电脑上是否保存了www.baidu.com对应的IP地址如果有,就直接请求了;如果没有,则是先到dns服务器查询,dns返回查询到的IP地址给浏览器,浏览器在本机保存,并发送请求到对应的IP地址.当我们的请求到达服务器时,服务器会对我们的请求做出响应.给我们的响应中可以是一个静态网页,也可以是动态页面.浏览器只负责解释执行html+css+javascript代码,服务器可以执行的服务端语言:c#,java...分别由不同的运行环境执行代码(framework,jvm).

    -静态页面:在服务器就相当于直接读取文件字符串然后返回客户端浏览器

    -动态页面:在服务器是先交给某语言环境虚拟机编译运行的,按照语法生成代码返回给客户端

    --服务器与浏览器之间的交互用什么方式了?

    浏览器跟服务器本质上就是两个使用socket进行基于http协议通信的两个应用程序(就相当于两个用电话说中文进行交流).

    --写一个简单的httpserver

    自己做个服务器程序要解决很多问题:

    1. 怎么接收浏览器的请求?接收以后怎么理解?怎么向浏览器发送响应,发送后浏览器怎么理解
    2. 如何对于静态页面(html/css/javascript)的请求做出响应
    3. 如何对动态页面(ashx/aspx)的请求做出响应,动态文件可是有很多的,使用什么方式来调用呢?
    4. 如何对图片文件(jpg/gif)的请求做出响应?

    解决思路:

    1. 使用套接字进行数据交换。
    2. 使用HTTP协议(到底什么是HTTP协议?)的数据,双方按照此协议解释理解接收到的数据。
    3. 直接读取静态文件的数据并通过套接字发送回浏览器。
    4. 对于动态文件的请求,可以通过分析请求页面,并反射所对应的类的对象,并通过接口调用类的方法。

    下面开始模拟一个IIS服务器吧(只是单纯模拟,帮助自己学习asp.net的)

    • 首先我们先监听浏览器的连接,这里服务器我们就用winform吧
    View Code
     1  private void btnStart_Click(object sender, EventArgs e)
     2         {
     3             //服务端的IP
     4             IPAddress ipAddress = IPAddress.Parse(this.txtIP.Text);
     5             //端口
     6             IPEndPoint endPoint = new IPEndPoint(ipAddress,int.Parse(txtPort.Text));
     7             //开启一个监听的socket
     8             Socket socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
     9             //绑定
    10             socket.Bind(endPoint);
    11             //开启监听
    12             socket.Listen(10);
    13             CurrentServierSocket = socket;
    14             this.lbStatus.Text = "已经启动..";
    15             //由于接收用户请求会阻塞当前的进程,所以我们开启另外一个线程帮我们接收用户,由于要不断的接收,我们就用死循环
    16             ThreadPool.QueueUserWorkItem(a =>
    17             {
    18                 
    19                 Socket serverSocket = (Socket) a;
    20                 while (true)
    21                 {
    22                     //接收用户
    23                     var proxSocket = socket.Accept();
    24                     //开启另外一个线程不断的接收用户的请求,由于也要不断请求,不断回应(请求后),我们这里放在死循环里,这么写会出现很多问题,只是单纯的模拟也就不用try住了
    25 
    26                     ThreadPool.QueueUserWorkItem(s =>
    27                     {
    28                         Socket pSocket = (Socket)s;
    29                         byte[] data = new byte[1024 * 1024];
    30                         int realLength = pSocket.Receive(data, 0, data.Length, 0);
    31                         string requestStr = Encoding.UTF8.GetString(data, 0, realLength);
    32                         //响应用户的请求
    33                         ProcessRequest(requestStr,pSocket);
    34                     }, proxSocket); 
    35                 }
    36             }, socket);
    37         }
    38         /// <summary>
    39         /// 响应请求
    40         /// </summary>
    41         /// <param name="requestStr"></param>
    42         /// <param name="socket"></param>
    43         public void ProcessRequest(string requestStr,Socket socket)
    44         {
    45             if(string.IsNullOrEmpty(requestStr))
    46             {
    47                 return;
    48             }
    49             this.richTextBox1.Text +="\r\n" + requestStr;
    50             HttpApplication application = new HttpApplication();
    51 
    52             HttpContext context = new HttpContext(requestStr);
    53             
    54             application.ProcessRequest(context);
    55 
    56             //CurrentServierSocket.Send()
    57             byte[] header = context.Response.GetHeader();
    58             socket.Send(header, 0, header.Length, 0);
    59 
    60             byte[] responseBody = context.Response.GetData();
    61             socket.Send(responseBody, 0, responseBody.Length, 0);
    62 
    63             socket.Shutdown(SocketShutdown.Both);
    64 
    65             socket.Close();
    66         }
    67     }
    •  ok,当我们开始监听客户端发回来的请求后,我们下面就要对发过来的请求做出响应,首先我们需要一个HttpContext类来帮我们存放请求报文跟响应报文

    我们添加一个HttpContext和httpresponse跟httprequest类来:

    View Code
     public class HttpContext
        {
            public HttpRequest Request { get; set; }
            public HttpResponse Response { get; set; }
    
            public HttpContext(string requestTxt)
            {
                this.Request = new HttpRequest(requestTxt);
                this.Response = new HttpResponse(this.Request);
            }
        }

    在httprequest类中我们需要对请求的报文进行解析:具体怎么做如下(对于请求的报文,我只处理了一部分,只是模拟)

    View Code
    public class HttpRequest
        {
            //请求页面地址
            public string RequestURL { get; set; }
            //请求行
            public string RawRequestTxt { get; set; }
            //请求方法:post,get等
            public string Method { get; set; }
            public HttpRequest(string requestStr)
            {
                //处理 设置当前的请求的url
                string temp = requestStr.Replace("\r\n", "\r");
                string[] allLines = temp.Split('\r');
                string requestLine = allLines[0];
                RequestURL = requestLine.Split(' ')[1];
                //设置当前的RawRequestText
                this.RawRequestTxt = requestStr;
                //设置Method
                Method = requestLine.Split(' ')[0];
            }
        }

    在httpresponse类中我们需要根据httprequest来做出响应体的构造,具体如下:

    View Code
       public class HttpResponse
        {
            public byte[] ResponseData { get; set; }
            public string StatusCode { get; set; }
            public Dictionary<string,string> StatusDic { get; set; }
            public string RequestURL = string.Empty;
           /* 
            HTTP/1.1 200 OK -------------------------statuscode statusdic
            Content-Type: text/html
            Last-Modified: Tue, 17 Jul 2012 07:36:38 GMT
            Accept-Ranges: bytes
            ETag: "0741e6ee63cd1:0"
            Server: Microsoft-IIS/7.5
            X-Powered-By: ASP.NET
            Date: Tue, 17 Jul 2012 14:45:55 GMT
            Content-Length: 70*/
            public byte[] GetHeader()
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendFormat("HTTP/1.1 200 OK\r\n");
                sb.AppendFormat("Content-Type: {0}\r\n", GetContentType(RequestURL));
                if (this.ResponseData != null)
                {
                    sb.AppendFormat("Content-Length: {0}\r\n\r\n", this.ResponseData.Length);
                }
                return Encoding.UTF8.GetBytes(sb.ToString());
                
            }
            /// <summary>
            /// 根据请求的url定义contentype
            /// </summary>
            /// <param name="RequestURL"></param>
            /// <returns></returns>
            public string GetContentType(string RequestURL)
            {
                string ext = Path.GetExtension(RequestURL);
                string type = "text/html";
                switch (ext)
                {
                    case ".jpg" :
                        type = "image/jpeg";
                        break;
                    case ".png":
                        type = "image/png";
                        break;
                    case ".gif":
                        type = "image/gif";
                        break;
                    case ".js":
                        type = "application/x-javascript";
                        break;
                    case ".css":
                        type = "text/css";
                        break;
                    case ".htm":
                    case ".html":
                        type = "text/html";
                        break;
                    case ".aspx":
                        type = "text/html";
                        break;
                        
                    default:
                        type = "text/plain";
                        break;
    
                }
                return type;
            }
            public  byte[] GetData()
            {
                return ResponseData;
            }
            /// <summary>
            /// ctor
            /// </summary>
            /// <param name="request"></param>
            public HttpResponse(HttpRequest request)
            {
                StatusDic = new Dictionary<string, string>();
                StatusDic.Add("200","Ok");
                StatusDic.Add("404", "Object Not Found");
                RequestURL = request.RequestURL;
            }
        }

    我们还需要一个HttpApplication类来帮我们获得数据,里面包含三个方法,一个处理上下文httpcontext,一个用来处理静态页面请求,一个是动态.

    View Code
     public class HttpApplication :IHttpHandler
        {
            public void ProcessRequest(HttpContext context)
            {
                //获取基目录
                string currentExcutePath = AppDomain.CurrentDomain.BaseDirectory;
                //获取全路径
                string fileName = Path.Combine(currentExcutePath, context.Request.RequestURL.TrimStart('/'));
                //获取后缀名
                string strFileExt = Path.GetExtension(fileName);
                switch (strFileExt)
                {
                    case ".html":
                    case ".htm":
                    case ".jpg":
                    case ".gif":
                    case ".png":
                    case ".js":
                    case ".css":
                        //处理静态页面
                        ProcessRequestStaticFile(fileName,context);
                        break;
                    case ".aspx":
                        //处理动态页面
                        ProcessDnamicPage(fileName,context);
                        break;
                    default:
                        //默认
                        ProcessRequestStaticFile(fileName,context);
                        break;
                }
            }
            /// <summary>
            /// 动态
            /// </summary>
            /// <param name="fileName"></param>
            /// <param name="context"></param>
            private void ProcessDnamicPage(string fileName,HttpContext context)
            {
                //获取除去扩展名的文件名
                string className = Path.GetFileNameWithoutExtension(fileName);
                //获取当前执行改方法的程序集
                var assembly = MethodBase.GetCurrentMethod().DeclaringType.Assembly;
                //获取命名控件加类名的全名称
                string fullName = MethodBase.GetCurrentMethod().DeclaringType.Namespace + "." + className;
                //因为可能请求动态的页面不只是一种,这里我们用接口来统一调用,利用反射穿件一个实例
                IHttpHandler handler = (IHttpHandler)assembly.CreateInstance(fullName, true);
                if(handler == null)
                {
                    string path = AppDomain.CurrentDomain.BaseDirectory + "web/404.htm";
                    context.Response.StatusCode = "404";
                    context.Response.ResponseData = File.ReadAllBytes(path);
                }
                else
                {
                    handler.ProcessRequest(context);
                    context.Response.StatusCode = "200";
                }
                //return context.Response.ResponseData;
            }
            private void ProcessRequestStaticFile(string fileName,HttpContext context)
            {
                if(File.Exists(fileName) )
                {
                    context.Response.StatusCode = "200";
                    context.Response.ResponseData = File.ReadAllBytes(fileName);
                }
                else
                {
                    string path = AppDomain.CurrentDomain.BaseDirectory + "web/404.htm";
                    context.Response.StatusCode = "404";
                    //如果是静态的直接读取了字符串
                    context.Response.ResponseData = File.ReadAllBytes(path);
                }
            }
        }

    接口和一个动态类(我也写死了,并没求通过用户传值过来,然后去数据库取值等操作,同样是模拟,少了很多操作)

    View Code
    public class Login : IHttpHandler
        {
            public string PageName { get; set; }
    
    
            public void ProcessRequest(HttpContext context)
            {
                string str = @"
                                <html>
                                    <head></head>
                                    <body>
                                        <h1>hello the shit! 你大爷的...</h1>
                                    </body>
                                </html>";
                context.Response.ResponseData = Encoding.UTF8.GetBytes(str);
                
            }
        }
    View Code
    public interface IHttpHandler
        {
            void ProcessRequest(HttpContext context);
        }

    总结:这是个小小的demo 目的是帮助我了解一个初步的asp.net运行机制,但是asp.net的运行机制可比这个要复杂的多了,明天继续学习.第一次写,写的很烂,大家给点意见!

  • 相关阅读:
    第一百二十七节,JavaScript,JSON数据类型转换,数据转换成字符串,字符串转换成数据
    第一百二十六节,JavaScript,XPath操作xml节点
    第一百二十五节,JavaScript,XML
    第一百二十四节,JavaScriptCookie与存储
    in_array严格模式和普通模式的区别
    thinkphp解决表单令牌问题
    php操作Memcache示例
    flash引入
    自己制作简单的可编辑并添加表情的文本编辑器
    php中mysqli 处理查询结果集的几个方法
  • 原文地址:https://www.cnblogs.com/wings/p/2743030.html
Copyright © 2020-2023  润新知