刚刚接触ASP.NET编程,为了更好的屡清楚服务器的处理过程,就用Socket模拟服务器来处理请求。用Socket来模拟服务器的时候,同样是自己来封装一些对应的类文件。包括 HttpRequest、HttpResponse、HttpContext、HttpApplication、IHttpHandel。主要的执行流程是:先用Socket来创建一个简单的服务器,进行监听,当监听到请求后将请求交给处理程序去处理,应用程序中根据请求的是静态资源还是动态资源做出不同的处理。然后通过Socket对象将响应类容给发送回去。这只是为了更好的了解服务器的处理,进行了一个简单的模拟,程序中有很多的bug,但是能够实现基本的功能
HttpRequest类
主要包含几个主要的属性:请求方式,请求地址,请求协议的版本号
1 public class HttpRequest 2 { 3 public HttpRequest (string str) 4 { 5 if (string.IsNullOrEmpty(str)) 6 { 7 return; 8 } 9 string head = str.Replace("\r\n", "$").Split('$')[0];10 string[] heads = head.Split(' ');11 Method = heads.Length>1? heads[0]:"";12 //得到请求的绝对路径13 Url = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), heads.Length>1? heads[1].Substring(1):heads[0]);14 Protocol = heads.Length > 1 ? heads[2] : "";15 16 }17 18 public string Method { get; set; }19 public string Url { get; set; }20 public string Protocol { get; set; }21 }
HttpResponse类
包含响应头和响应体,在网络上传输的是二进制文件,故将这两个属性定义为byte[],中间还包含其他的一些属性。其中响应长度是动态的,在处理程序里面设置
1 public class HttpResponse 2 { 3 public HttpResponse (HttpRequest request) 4 { 5 this.request = request; 6 Type = GetType(Path.GetExtension(request.Url)); 7 } 8 9 private HttpRequest request;10 private string Type { get; set; }11 public int Length { get; set; }12 public byte[] Head {13 get14 {15 16 // -----响应报文------------17 //HTTP/1.1 200 OK18 //Connection: keep-alive19 //Date: Thu, 26 Jul 2007 14:00:02 GMT20 //Server: Microsoft-IIS/6.021 //X-Powered-By: ASP.NET22 //Content-Length: 19023 //Content-Type: text/html24 //Set-Cookie: ASPSESSIONIDSAATTCSQ=JOPPKDCAMHHBEOICJPGPBJOB; path=/25 //Cache-control: private 26 ////--空行--27 //响应体(正文)28 29 StringBuilder sb = new StringBuilder();30 sb.AppendFormat("{0} {1}\r\n", request.Protocol, "200 OK");31 sb.AppendLine("Date:" + DateTime.Now.ToString());32 sb.AppendLine("Server:QIGANG-PC");33 sb.AppendLine("Content-Length:" + Length);34 sb.AppendLine("Content-Type:" + Type);35 sb.AppendLine();36 return Encoding.UTF8.GetBytes(sb.ToString());37 38 39 }40 }41 public byte[] Body { get; set; }42 43 ///根据请求来得到响应的类型44 private string GetType (string ext)45 {46 string type1 = "text/html;charset=utf-8";47 switch (ext)//mime-type48 {49 case ".aspx":50 case ".html":51 case ".htm":52 type1 = "text/html;charset=utf-8";53 break;54 case ".png":55 type1 = "image/png";56 break;57 case ".gif":58 type1 = "image/gif";59 break;60 case ".jpg":61 case ".jpeg":62 type1 = "image/jpeg";63 break;64 case ".css":65 type1 = "text/css";66 break;67 case ".js":68 type1 = "application/x-javascript";69 break;70 default:71 type1 = "text/plain;charset=gbk";72 break;73 }74 return type1;75 }76 }
HttpContext类
在这个类里面简单的封装了两个成员对象,就是HttpRequest和HttpResponse两个成员,其它的就从简了
1 public class HttpContext 2 { 3 public HttpContext (string str) 4 { 5 Request = new HttpRequest(str); 6 Response = new HttpResponse(Request); 7 } 8 public HttpRequest Request { get; set; } 9 public HttpResponse Response { get; set; }10 }
IHttpHandel接口
由于在客户端请求的数据中可能请求的是一个动态网页,这是就需要交给.NET Framework 来进行处理,为了方便处理,故要求所有的动态网页都需要实现一个接口,只有实现了这个接口的程序才能够被浏览器给请求到
1 public interface IHttpHandel2 {3 void ProcessRequest (HttpContext context);4 }
服务器端Socket程序
在这主要是启动一个Socket对象,来进行连接的监听,然后把监听到的对象交给处理程序 HttpApplication进行处理
1 private void button1_Click (object sender, EventArgs e) 2 { 3 socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 4 socket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 22822)); 5 socket.Listen(10); 6 Thread thread = new Thread((obj) => 7 { 8 Socket server = obj as Socket; 9 while (true)10 {11 Socket client = server.Accept();12 Thread cth = new Thread((obj2) =>13 {14 Socket cSocket = obj2 as Socket;15 16 byte[] by = new byte[cSocket.Available];17 cSocket.Receive(by, 0, by.Length, SocketFlags.None);18 //拿到请求头文件19 string str = Encoding.UTF8.GetString(by);20 21 /*--------- 调用请求处理函数进行处理 ---------*/22 HttpContext context = new HttpContext(str);23 HttpApplication app = new HttpApplication(context, cSocket);24 });25 cth.IsBackground = true;26 cth.Start(client);27 }28 });29 thread.IsBackground = true;30 thread.Start(socket);31 }
HttpApplication类
这个类需要传递一个参数,上下文对象。然后进行请求的解析,根据请求的是静态资源还是动态资源去进行不同的处理。如果是静态资源就直接冲磁盘文件中读取返回,如果是动态资源,就交给对应的类。当然前提是请求的资源名称就对应了一个类文件。
1 public class HttpApplication 2 { 3 //构造函数 4 public HttpApplication (HttpContext context,Socket socket) 5 { 6 string url =context.Request.Url; 7 if(string.IsNullOrEmpty(url)){ 8 return; 9 }10 string ext = Path.GetExtension(url);11 //请求的是动态资源文件12 if (ext == ".aspx")13 {14 //下面的代码中也就没有进行错误的处理了,主要是模拟,没有考虑其他的情况15 //拿到请求资源的文件名(不包含后缀)16 string cs = Path.GetFileNameWithoutExtension(url);17 //得到当前程序的程序集18 Assembly ass = Assembly.GetExecutingAssembly();19 //拿到请求的文件对应的类20 Type type = ass.GetType(ass.GetName().Name + "." + cs, true, true);21 //创建对象,进行调用22 IHttpHandel handel = Activator.CreateInstance(type) as IHttpHandel;23 handel.ProcessRequest(context);24 //上面几句话可以合并到一起,拿到程序集后直接CreateInstance();25 }26 else if (ext == ".ashx")27 {28 }29 else//访问静态资源30 {31 //直接从磁盘中读取请求的资源文件32 byte[] by = File.ReadAllBytes(url);33 context.Response.Length = by.Length;34 context.Response.Body = by;35 36 }37 socket.Send(context.Response.Head);38 socket.Send(context.Response.Body);39 }40 }
整个请求的模型基本上就差不多完成了,如果是请求的动态文件b.aspx,那么这个b.cs文件需要实现IHttpHandel接口,同时在规定的方法里面进行处理,代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace IIS_Two 9 {10 public class b:IHttpHandel11 12 {13 public void ProcessRequest (HttpContext context)14 {15 //读取模板HTML文件16 string html = File.ReadAllText("temp.html");17 string temp = "
附上b.cs中用到的一个HTML模板和一个txt文件
1 2 3 4 5 67 8 9 $body10 11
1 aaa:222 bbb:233 ccc:18
整个简单的模拟就完成了,bug很多,这不重要。如果请求一个简单的html页面或者是jpg等图片都能够成功,当然这些文件要存在代码中所写的目录下面才行