简单模拟IIS服务器

最近在学习asp.net,在学习这部分之前,我觉得对iis的理解是很重要的,所以记录一下简单模拟IIS的过程和代码。

一. 模拟IIS流程:
首先模拟IIS服务器,最基本的就是对浏览器的请求进行处理,并将处理结果返还给浏览器,浏览器在对服务器响应的结果进行解析,并呈现在浏览器中,所以具体步骤如下所示:
1. 编写Socket,用于监听并接受浏览器发过来的请求,以及向浏览器发送响应内容。
2. 接受浏览器的请求报文,并进行解析,获取有用的报文信息。
3. 根据请求报文的信息进行响应,响应时,包括处理响应头和处理响应体。
3.1 响应头处理
3.2 响应体处理
4. 编写响应的网页,进行测试。

二. 代码和说明
编写Socket

        private void btnBind_Click(object sender, EventArgs e)
        {
            Socket bindSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress ip = IPAddress.Parse(txtIp.Text.Trim());
            IPEndPoint port = new IPEndPoint(ip, int.Parse(txtPort.Text));
            bindSocket.Bind(port);
            bindSocket.Listen(10);
            lblBind.Text = "正在监听...";
            lblBind.ForeColor = Color.Green;
            //创建线程池进行监听
            ThreadPool.QueueUserWorkItem(new WaitCallback(conSck =>
            {
                Socket acceptSocket = conSck as Socket;
                while (true)
                {
                    Socket recieveSocket = acceptSocket.Accept();
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    int r = recieveSocket.Receive(buffer);
                    string str = Encoding.UTF8.GetString(buffer, 0, r);
                    txtInfo.Text = str;
                    //处理请求后响应
                    ProcessRequest(str, recieveSocket);
                }
            }),bindSocket);
        }

这里的ProcessRequest是回发响应数据的函数。
解析请求报文
解析请求报文时,为了能更好的管理请求报文和响应报文,我们对这两部分进行了封装,并封装一个上下文类,对其进行管理。即这个上下文HttpContext类相当于一个中转站,它负责向浏览器分发响应体,以及向服务器分发请求体。
HttpContext:

    class HttpContext
    {
        public HttpContext(string str)
        {
            this.HttpRequest = new HttpRequest(str);
            this.HttpResponse = new HttpResponse(HttpRequest);
        }

        public HttpRequest HttpRequest{get;set;}
        public HttpResponse HttpResponse { get; set; }

    }

这样处理请求报文类HttpRequest将请求报文处理好后提交给HttpContext,处理响应体的类就可以从HttpContext获取相应的处理后的请求体了。下面是请求类对请求体的处理。
HttpRequest:

    class HttpRequest
    {
        private string str;

        public HttpRequest(string str)
        {
            string[] strSplit = str.Split(new string[] { "\r\n" }, StringSplitOptions.None);
            string[] strRequest = strSplit[0].Split(' ');
            this.RequestMethod = strRequest[0];
            this.RequestUrl = strRequest[1];
            this.HttpVersion = strRequest[2];
        }

        public string RequestMethod { get; set; }
        public string HttpVersion { get; set; }
        public string RequestUrl { get; set; }
    }

由于浏览器对服务器的请求内容比较多,我们只获取一下请求内容中的请求方法,请求地址,以及请求的http版本号。其实这里只用到了了请求的地址,即浏览器所请求的页面。
响应处理
响应类拿到请求类解析后的请求属性后,会对浏览器的请求进行处理(主要是响应体处理,即请求的页面处理),然后将处理结果与响应类的响应头中所必须的属性一起回发给HttpContext,再由Sockt将HttpContext中接受的响应发送给浏览器。那么如果将响应处理的内容写到响应类中会造成程序和层次的混乱,以及降低了程序的维护性。所以这里再封装一个类来对请求内容处理。

为什么要对请求内容处理呢,因为浏览器请求的页面分为静态页面和动态页面,那么如果是静态页面,我们需要从服务器本地获取到浏览器所请求的页面地址,如果是动态页面,我们需根据请求的URL动态获取.cs等动态页面文件。下面是HttpApplication类,用于处理静态页面和动态页面。
HttpApplication:

 class HttpApplication 
    {

        public void ProcessRequest(HttpContext context)
        {
            string extention=Path.GetExtension(context.HttpRequest.RequestUrl);
            if (extention == ".aspx")
            {
                HandleDynamicPage(context);
            }
            else
            {
                HandleStaticPage(context);
            }
        }

        private void HandleStaticPage(HttpContext context)
        {
            string namepace = MethodBase.GetCurrentMethod().DeclaringType.Namespace;
            string path1 = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            string path = Path.Combine(path1, "website", context.HttpRequest.RequestUrl.TrimStart('/'));
            if (File.Exists(path))
            {
                context.HttpResponse.ResponseBody = File.ReadAllBytes(path);
            }
            else
            {
                context.HttpResponse.ResponseBody = new byte[] { 0 };
            }
        }

        private void HandleDynamicPage(HttpContext context)
        {
            string className=Path.GetFileNameWithoutExtension(context.HttpRequest.RequestUrl);
            string ObjectName = MethodBase.GetCurrentMethod().DeclaringType.Namespace + "." + className;
            IDynamicPages dynamicPage =(IDynamicPages) Assembly.GetExecutingAssembly().CreateInstance(ObjectName);
            if (dynamicPage != null)
            {
                dynamicPage.ProcessRequest(context);
            }
        }

响应类
处理完毕后,结合响应类就可以将响应内容回发了。
HttpResponse:

    class HttpResponse
    {
        private HttpRequest httpRequest;

        public HttpRequest HttpRequest
        {
            get { return httpRequest; }
            set { httpRequest = value; }
        }

        public HttpResponse(HttpRequest httpRequest)
        {
            this.HttpRequest = httpRequest;
        }
        public byte[] ResponseHeader 
        {
            get 
            {
                return GetResponseHeader(); 
            }
        }
        public byte[] ResponseBody { get; set; }
        public string ContentLenth
        {
            get
            {
                if (ResponseBody != null)
                {
                    return ResponseBody.Length.ToString();
                }
                else return string.Empty;
            }
            set { ContentLenth = value; }
        }
        public string ContentType
        {
            get { return GetContentType(HttpRequest.RequestUrl); }
            set { ContentType = value; }
        }
        private byte[] GetResponseHeader()
        {
             //HTTP/1.1 200 OK
             //Content-Type: text/html
             //Content-Length: 913
            StringBuilder strHeader = new StringBuilder();
            strHeader.AppendLine("HTTP/1.1 200 OK");
            strHeader.AppendLine("Content-Type: " + ContentType);
            strHeader.AppendLine("Content-Length: " + ContentLenth);
            strHeader.AppendLine();
            return Encoding.UTF8.GetBytes(strHeader.ToString());
        }
        private string GetContentType(string p)
        {
            string urlExtension = Path.GetExtension(p);
            string content_Type = string.Empty;
            switch (urlExtension)
            {
                case ".aspx":
                case ".html":
                case ".htm":
                    content_Type = "text/html; charset=utf-8";
                    break;
                case ".png":
                    content_Type = "image/png";
                    break;
                case ".gif":
                    content_Type = "image/gif";
                    break;
                case ".jpg":
                case ".jpeg":
                    content_Type = "image/jpeg";
                    break;
                case ".css":
                    content_Type = "text/css";
                    break;
                case ".js":
                    content_Type = "application/x-javascript";
                    break;
                default:
                    content_Type = "text/plain";
                    break;
            }
            return content_Type;
        }      
    }

响应类中包括响应头和响应体,为了简单,我们同样只响应一些必要的内容,响应头包括响应是否成功,响应页面类型(html,aspx,css,jpg等),以及响应体字节长度。响应体自然是响应页面的内容了。这里交给了HttpApplication处理了。

获取动态页面内容
获取静态页面的内容在HttpApplication中有,我们只需建好相关的页面就可以了。那么怎么处理动态页面呢,这里以.aspx为例,.aspx页面在程序中可以是一个.cs文件,那么我们就要建立相应的.cs文件了。但是建立好了.cs文件,程序怎样获取呢,因为项目中有很多.cs文件,我们怎么判断哪个是相应的.aspx呢,怎样获取相应的类对象呢。这里可以让所有的只要是.aspx的类都继承一个IDynamicPages的接口,并实现接口中的ProcessRequest方法,然后可以通过反射动态页面名称获取相应的类,并将其强转成IDynamicPages类型。这样就获取到了该对象,并执行该对象中的ProcessRequest(用于获取页面内容)方法。下面是.aspx页面类:

    class IndexPage : IDynamicPages
    {

        public void ProcessRequest(HttpContext context)
        {
            StringBuilder str = new StringBuilder();
            str.Append("<html><head><title>hello, world</title></head><body><p>hello, world!</p></body></html>");
            context.HttpResponse.ResponseBody = Encoding.UTF8.GetBytes(str.ToString());
        }
    }

这个类中的ProcessRequest中只写了一个hello, world的html,并将其传给HttpResponse的响应体属性。
上面HttpApplication类中的HandleDynamicPage方法,就是用于获取请求动态页面所对应的的类对象,并调用该对象中的ProcessRequest方法,从而得到响应体。
Socket回发响应内容
这样Socket就可以通过HttpContext获取响应内容,并发送给浏览器了,其余的事情就交给浏览器了.

        private void ProcessRequest(string str, Socket recieveSocket)
        {
            HttpContext context = new HttpContext(str);
            HttpApplication application=new HttpApplication();
            application.ProcessRequest(context);
            recieveSocket.Send(context.HttpResponse.ResponseHeader);
            recieveSocket.Send(context.HttpResponse.ResponseBody);
            recieveSocket.Shutdown(SocketShutdown.Both);
            recieveSocket.Close();
        }

简单测试
打开简单的IIS服务器,然后点击里监听,在浏览器中输入http://192.168.1.100:9991/index.htm ,这里192.168.1.100是我本机的ip,9991是端口号,index.html是一个静态网页.
这里写图片描述
文本框中的内容就是浏览器的请求内容。
浏览器显示index.html
这里写图片描述
请求一个动态页面:http://192.168.1.100:9991/IndexPage.aspx
这里写图片描述
对于简单的页面可以进行测试成功,由于这是一个简单的模拟,主在理解浏览器的请求和服务器的响应方式,所以程序还是有不少需要改进的。

资源下载地址:http://download.csdn.net/detail/u012058778/9371404

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

非正经程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值