本文基於.net core 的控制檯程序做爲服務端javascript
main函數:css
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Console.WriteLine("The server is starting......"); 6 7 new Server().StartServer(); 8 9 Console.ReadLine(); 10 } 11 }
其中核心代碼在Server這個類上面:html
1 public class Server 2 { 3 private Socket socketWatch = null; 4 private Thread threadWatch = null; 5 private string ipAddress = "127.0.0.1"; 6 private string port = "11111"; 7 8 public Server() 9 { 10 socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 11 socketWatch.Bind(new IPEndPoint(IPAddress.Parse(ipAddress), int.Parse(port))); 12 socketWatch.Listen(100); 13 // 建立Thread->後臺執行 14 threadWatch = new Thread(ListenClientConnect); 15 threadWatch.IsBackground = true; 16 } 17 18 public void StartServer() 19 { 20 threadWatch.Start(socketWatch); 21 } 22 23 private void ListenClientConnect(object objSocket) 24 { 25 Socket socketListen = objSocket as Socket; 26 27 while (true) 28 { 29 Socket proxSocket = socketListen.Accept(); 30 byte[] data = new byte[1024 * 1024 * 2]; 31 int length = proxSocket.Receive(data, 0, data.Length, SocketFlags.None); 32 // Step1:接收HTTP請求 33 string requestText = Encoding.Default.GetString(data, 0, length); 34 HttpContext context = new HttpContext(requestText); 35 // Step2:處理HTTP請求 36 HttpApplication application = new HttpApplication(); 37 application.ProcessRequest(context); 38 // Step3:響應HTTP請求 39 Console.WriteLine(string.Format("{0} {1} from {2}", context.Request.HttpMethod, context.Request.Url, proxSocket.RemoteEndPoint.ToString())); 40 proxSocket.Send(context.Response.GetResponseHeader()); 41 proxSocket.Send(context.Response.Body); 42 // Step4:即時關閉Socket鏈接 43 proxSocket.Shutdown(SocketShutdown.Both); 44 proxSocket.Close(); 45 } 46 } 47 }
上面代碼中,主要是基於Socket和線程。在構造函數中初始化了服務器端Socket,還初始化了Thread,而且設置爲後臺線程。ListenClientConnect函數主要作的事情是接受瀏覽器請求,而且轉化爲HttpContext和HttpApplication,最後輸出響應而且關閉socket。java
這裏面有幾個比較重要的類,主要以下:瀏覽器
1 public class HttpContext 2 { 3 public HttpRequest Request { get; set; } 4 public HttpResponse Response { get; set; } 5 6 public HttpContext(string requestText) 7 { 8 Request = new HttpRequest(requestText); 9 Response = new HttpResponse(); 10 } 11 }
HttpContext模擬asp的HttpContext,裏面有兩個看起來很熟悉的類,HttpRequest和HttpResponse服務器
1 public class HttpRequest 2 { 3 public HttpRequest(string requestText) 4 { 5 string[] lines = requestText.Replace("\r\n", "\r").Split('\r'); 6 string[] requestLines = lines[0].Split(' '); 7 // 獲取HTTP請求方式、請求的URL地址、HTTP協議版本 8 if(requestLines.Length >= 2) 9 { 10 HttpMethod = requestLines[0]; 11 Url = requestLines[1]; 12 HttpVersion = requestLines[2]; 13 } 14 } 15 // 請求方式:GET or POST? 16 public string HttpMethod { get; set; } 17 // 請求URL 18 public string Url { get; set; } 19 // Http協議版本 20 public string HttpVersion { get; set; } 21 // 請求頭 22 public Dictionary<string, string> HeaderDictionary { get; set; } 23 // 請求體 24 public Dictionary<string, string> BodyDictionary { get; set; } 25 }
1 public class HttpResponse 2 { 3 // 響應狀態碼 4 public string StateCode { get; set; } 5 // 響應狀態描述 6 public string StateDescription { get; set; } 7 // 響應內容類型 8 public string ContentType { get; set; } 9 //響應報文的正文內容 10 public byte[] Body { get; set; } 11 12 // 生成響應頭信息 13 public byte[] GetResponseHeader() 14 { 15 string strRequestHeader = string.Format(@"HTTP/1.1 {0} {1} 16 Content-Type: {2} 17 Accept-Ranges: bytes 18 Server: Microsoft-IIS/7.5 19 X-Powered-By: ASP.NET 20 Date: {3} 21 Content-Length: {4} 22 23 ", StateCode, StateDescription, ContentType, string.Format("{0:R}", DateTime.Now), Body.Length); 24 25 return Encoding.UTF8.GetBytes(strRequestHeader); 26 } 27 }
這兩個核心類是關於請求和響應的。app
IHttpHandler是另一個很熟悉的接口,通常處理程序中都會實例化它socket
1 public interface IHttpHandler 2 { 3 void ProcessRequest(HttpContext context); 4 }
咱們處理請求的時候就是依靠實例化這個接口了,在咱們的實例上面就是HttpApplication函數
1 public class HttpApplication : IHttpHandler 2 { 3 // 對請求上下文進行處理 4 public void ProcessRequest(HttpContext context) 5 { 6 // 1.獲取網站根路徑 7 if(string.IsNullOrEmpty(context.Request.Url)) 8 { 9 return; 10 } 11 string bastPath = AppDomain.CurrentDomain.BaseDirectory; 12 string fileName = Path.Combine(bastPath, "LZZWebSite", context.Request.Url.TrimStart('/')); 13 string fileExtension = Path.GetExtension(context.Request.Url); 14 // 2.處理動態文件請求 15 if (fileExtension.Equals(".aspx") || fileExtension.Equals(".ashx")) 16 { 17 string className = Path.GetFileNameWithoutExtension(context.Request.Url); 18 IHttpHandler handler = Assembly.GetExecutingAssembly().CreateInstance($"lzzWebServerDemo.Page.{className}", true) as IHttpHandler; 19 handler.ProcessRequest(context); 20 return; 21 } 22 // 3.處理靜態文件請求 23 if (!File.Exists(fileName)) 24 { 25 context.Response.StateCode = "404"; 26 context.Response.StateDescription = "Not Found"; 27 context.Response.ContentType = "text/html"; 28 string notExistHtml = Path.Combine(bastPath, @"LZZWebSite\notfound.html"); 29 context.Response.Body = File.ReadAllBytes(notExistHtml); 30 } 31 else 32 { 33 context.Response.StateCode = "200"; 34 context.Response.StateDescription = "OK"; 35 context.Response.ContentType = GetContenType(Path.GetExtension(context.Request.Url)); 36 context.Response.Body = File.ReadAllBytes(fileName); 37 } 38 } 39 40 // 根據文件擴展名獲取內容類型 41 public string GetContenType(string fileExtension) 42 { 43 string type = "text/html; charset=UTF-8"; 44 switch (fileExtension) 45 { 46 case ".aspx": 47 case ".html": 48 case ".htm": 49 type = "text/html; charset=UTF-8"; 50 break; 51 case ".png": 52 type = "image/png"; 53 break; 54 case ".gif": 55 type = "image/gif"; 56 break; 57 case ".jpg": 58 case ".jpeg": 59 type = "image/jpeg"; 60 break; 61 case ".css": 62 type = "text/css"; 63 break; 64 case ".js": 65 type = "application/x-javascript"; 66 break; 67 default: 68 type = "text/plain; charset=gbk"; 69 break; 70 } 71 return type; 72 }
上面的業務比較清晰,若是是靜態資源,就直接響應返回。若是是動態資源,例如aspx、ashx的話就經過反射實例化對應的處理類。咱們例子上是這樣模擬的:網站
1 public class LzzPage: IHttpHandler 2 { 3 public void ProcessRequest(HttpContext context) 4 { 5 StringBuilder sbText = new StringBuilder(); 6 sbText.Append("<html>"); 7 sbText.Append("<head></head>"); 8 sbText.Append("<body>"); 9 sbText.Append("<h1>demo</h1>"); 10 sbText.Append("lzzdemolzzdemo"); 11 sbText.Append(string.Format("<h3>time:{0}</h3>", DateTime.Now.ToString())); 12 sbText.Append("</body>"); 13 sbText.Append("</html>"); 14 context.Response.Body = Encoding.UTF8.GetBytes(sbText.ToString()); 15 context.Response.StateCode = "200"; 16 context.Response.ContentType = "text/html"; 17 context.Response.StateDescription = "OK"; 18 } 19 }
最後來一張整個體統的結構圖
運行圖: