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的运行机制可比这个要复杂的多了,明天继续学习.第一次写,写的很烂,大家给点意见!

 

 

 

 

 

 

 

 

 

posted @ 2012-10-27 23:34  BigRocWings  阅读(1672)  评论(2编辑  收藏  举报