剛剛接觸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 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 }
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 IHttpHandel 2 { 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: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文件

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等圖片都能夠成功,當然這些文件要存在代碼中所寫的目錄下面才行