剛剛接觸ASP.NET編程,爲了更好的屢清楚服務器的處理過程,就用Socket模擬服務器來處理請求。用Socket來模擬服務器的時候,一樣是本身來封裝一些對應的類文件。包括 HttpRequest、HttpResponse、HttpContext、HttpApplication、IHttpHandel。主要的執行流程是:先用Socket來建立一個簡單的服務器,進行監聽,當監聽到請求後將請求交給處理程序去處理,應用程序中根據請求的是靜態資源仍是動態資源作出不一樣的處理。而後經過Socket對象將響應類容給發送回去。這只是爲了更好的瞭解服務器的處理,進行了一個簡單的模擬,程序中有不少的bug,可是可以實現基本的功能javascript
主要包含幾個主要的屬性:請求方式,請求地址,請求協議的版本號css
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 }
包含響應頭和響應體,在網絡上傳輸的是二進制文件,故將這兩個屬性定義爲byte[],中間還包含其餘的一些屬性。其中響應長度是動態的,在處理程序裏面設置html
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 get
14 { 15
16 // -----響應報文------------ 17 //HTTP/1.1 200 OK 18 //Connection: keep-alive 19 //Date: Thu, 26 Jul 2007 14:00:02 GMT 20 //Server: Microsoft-IIS/6.0 21 //X-Powered-By: ASP.NET 22 //Content-Length: 190 23 //Content-Type: text/html 24 //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-type
48 { 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 }
在這個類裏面簡單的封裝了兩個成員對象,就是HttpRequest和HttpResponse兩個成員,其它的就從簡了java
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 }
因爲在客戶端請求的數據中可能請求的是一個動態網頁,這是就須要交給.NET Framework 來進行處理,爲了方便處理,故要求全部的動態網頁都須要實現一個接口,只有實現了這個接口的程序纔可以被瀏覽器給請求到編程
1 public interface IHttpHandel 2 { 3 void ProcessRequest (HttpContext context); 4 }
在這主要是啓動一個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 }
這個類須要傳遞一個參數,上下文對象。而後進行請求的解析,根據請求的是靜態資源仍是動態資源去進行不一樣的處理。若是是靜態資源就直接衝磁盤文件中讀取返回,若是是動態資源,就交給對應的類。固然前提是請求的資源名稱就對應了一個類文件。服務器
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:IHttpHandel 11
12 { 13 public void ProcessRequest (HttpContext context) 14 { 15 //讀取模板HTML文件
16 string html = File.ReadAllText("temp.html"); 17 string temp = "<table border='1'>{0}</table>"; 18 //讀取文本文件類容,拼接一個table表格
19 using (StreamReader reader = new StreamReader("1.txt")) 20 { 21 string str = ""; 22 List<string> list = new List<string>(); 23 while ((str = reader.ReadLine()) != null) 24 { 25 string[] strs = str.Split(':'); 26 list.Add("<tr><td>" + strs[0] + "</td><td>" + strs[1] + "</td></tr>"); 27
28 } 29 temp = string.Format(temp, string.Join("", list)); 30 } 31 //模板內容的替換
32 html = html.Replace("$body", temp); 33 byte[] by = Encoding.UTF8.GetBytes(html); 34 context.Response.Length = by.Length; 35 context.Response.Body = by; 36 } 37 } 38 }
附上b.cs中用到的一個HTML模板和一個txt文件app
1 <!DOCTYPE html>
2
3 <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
4 <head>
5 <meta charset="utf-8" />
6 <title></title>
7 </head>
8 <body>
9 $body 10 </body>
11 </html>
1 aaa:22 2 bbb:23 3 ccc:18
整個簡單的模擬就完成了,bug不少,這不重要。若是請求一個簡單的html頁面或者是jpg等圖片都可以成功,固然這些文件要存在代碼中所寫的目錄下面才行socket