ASP.net本質論之用控制檯應用程式建立Asp.net伺服器
主題 | 概要 |
---|---|
Asp.net | 應用程式域、HttpRunTime |
編輯 | 時間 |
新建 | 20170925 |
序號 | 參考資料 |
1 | Asp.net本質論 |
2 | C#高階程式設計(第七版) |
3 | http://blog.csdn.net/sh524555685/article/details/7454244(應用程式域解釋) |
4 | http://blog.csdn.net/lhc1105/article/details/47815971(程式集加到GAC方法) |
Web應用程式,歸根結底是一種網路處理程式,網路處理程式主要的關鍵就是監聽與處理。
監聽程式
Socket是最原始的網路監聽程式,下面的示例顯示了監聽本機9152埠並返回hello,world的html頁面。
Socket監聽程式
namespace SocketWeb
{
public class Socket
{
public void run()
{
IPAddress address = IPAddress.Loopback;
IPEndPoint endPoint = new IPEndPoint(address, 9152);
System.Net.Sockets.Socket socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(endPoint);
socket.Listen(10);
Console.WriteLine("開始監聽,埠號為{0}", endPoint.Port);
while (true)
{
System.Net.Sockets.Socket client = socket.Accept();
try
{
Console.WriteLine(client.RemoteEndPoint);
byte[] buffer = new byte[4096];
int length = client.Receive(buffer, 4096, SocketFlags.None);
System.Text.Encoding utf8 = System.Text.Encoding.UTF8;
string requestString = utf8.GetString(buffer, 0, length);
Console.WriteLine(requestString);
string statusLine = "HTTP/1.1 200 OK\r\n";
//string statusLine = "200 OK\r\n";
byte[] statusLineByte = utf8.GetBytes(statusLine);
string responseBody = @"<!DOCTYPE html><html lang = 'en'><head><meta charset = 'UTF-8'>" +
"<title> From Socket Server</title></head><body><h1>hello,world </h1></body></html> ";
//string responseBody = @"123456";
byte[] responseBodyByte = utf8.GetBytes(responseBody);
string responseHead = string.Format("Content-Type:text/html; charset=utf-8\r\nContent-Length:{0}\r\n",responseBodyByte.Length);
byte[] responseHeadByte = utf8.GetBytes(responseHead);
client.Send(statusLineByte);
client.Send(responseHeadByte);
//--頭部與內容的分隔行
client.Send(new byte[] { 13,10});
client.Send(responseBodyByte);
client.Close();
if (Console.KeyAvailable)
{
break;
}
}
catch (Exception ex)
{
var msg = ex.Message;
client.Close();
}
}
socket.Close();
}
}
}
在main函式中執行這個run方法進行監聽,在瀏覽器中輸入localhost:9152,能返回響應:
需要注意的是傳送響應內容時一定要把頭部和內容分隔開:
不然會被中止連線:
TCP監聽程式
.net為了簡化TCP協議的監聽程式,提供了一個TcpListener類,下面的程式碼與socket程式的效果相同:
namespace Aspnet.TcpListener
{
public class WebTcp
{
public void run()
{
IPAddress address = IPAddress.Loopback;
IPEndPoint endPoint = new IPEndPoint(address, 9152);
System.Net.Sockets.TcpListener tcpListener = new System.Net.Sockets.TcpListener(endPoint);
tcpListener.Start();
Console.WriteLine("開始監聽,埠號為{0}", endPoint.Port);
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
Console.WriteLine("已經建立聯接");
//--得到一個網路流
NetworkStream ns = tcpClient.GetStream();
try
{
byte[] buffer = new byte[4096];
int length = ns.Read(buffer,0,4096);
System.Text.Encoding utf8 = System.Text.Encoding.UTF8;
string requestString = utf8.GetString(buffer, 0, length);
Console.WriteLine(requestString);
string statusLine = "HTTP/1.1 200 OK\r\n";
//string statusLine = "200 OK\r\n";
byte[] statusLineByte = utf8.GetBytes(statusLine);
string responseBody = @"<!DOCTYPE html><html lang = 'en'><head><meta charset = 'UTF-8'>" +
"<title> From Socket Server</title></head><body><h1>hello,world </h1></body></html> ";
byte[] responseBodyByte = utf8.GetBytes(responseBody);
string responseHead = string.Format("Content-Type:text/html; charset=utf-8\r\nContent-Length:{0}\r\n", responseBodyByte.Length);
byte[] responseHeadByte = utf8.GetBytes(responseHead);
ns.Write(statusLineByte,0,statusLineByte.Length);
ns.Write(responseHeadByte, 0, responseHeadByte.Length);
ns.Write(new byte[] { 13, 10 }, 0, 2);
ns.Write(responseBodyByte, 0, responseBodyByte.Length);
ns.Close();
if (Console.KeyAvailable)
{
break;
}
}
catch (Exception ex)
{
var msg = ex.Message;
ns.Close();
}
}
tcpListener.Stop();
}
}
}
HTTP監聽程式
為了進一步簡化HTTP協議的監聽程式,.net提供了一個HttpListener類,通過字串的形式提供地址和監聽埠號。
public void run()
{
string prex = "http://localhost:9152/";
System.Net.HttpListener httpListener = new System.Net.HttpListener();
httpListener.Prefixes.Add(prex);
httpListener.Start();
Console.WriteLine("開始監聽.....");
while (true)
{
HttpListenerContext context = httpListener.GetContext();
Console.WriteLine("已經建立聯接");
try
{
HttpListenerRequest request = context.Request;
Console.WriteLine(request.ToString());
HttpListenerResponse response = context.Response;
string responseBody = @"<!DOCTYPE html><html lang = 'en'><head><meta charset = 'UTF-8'>" +
"<title> From Socket Server</title></head><body><h1>hello,world </h1></body></html> ";
response.ContentLength64 = System.Text.Encoding.UTF8.GetByteCount(responseBody);
response.ContentType = "text/html; charset=utf-8";
Stream output = response.OutputStream;
StreamWriter streamWriter = new StreamWriter(output);
streamWriter.Write(responseBody);
streamWriter.Close();
if (Console.KeyAvailable)
{
break;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
httpListener.Stop();
}
可以看到,上面的監聽和處理程式實際上是在一起的,只是通過監聽程式回發一些固定的內容。下面通過建立自定義的應用程式域,自定義請求處理函式來建立自己的Web伺服器。
自定義Web伺服器
所謂自定義Web伺服器,就是複用前面的http監聽程式,並呼叫自己的請求處理程式,完全拋棄IIS伺服器。
在建立之前,首先需要了解一個概念。
Web應用程式域
在.net之前的技術中,程式作為獨立的邊界來使用,每個程式都有其私有的虛擬記憶體。在.net體系結構中,應用程式有一個新的邊界:應用程式域。多個應用程式可以執行在一個程式的多個應用程式域中。
那為什麼要引入應用程式域?
我們知道所有.net應用程式都執行在託管環境中,但作業系統只提供程式供程式執行,而程式只是提供了基本的記憶體管理,它不瞭解什麼是託管程式碼。所以託管程式碼,也可以說是我們建立的.Net程式,是無法直接執行在作業系統程式中的。為了使託管程式碼能夠執行在非託管的程式之上,就需要有一箇中介者,這個中介者可以執行於非託管的程式之上,同時向託管程式碼提供執行的環境。這個中介者就是應用程式域(Application Domain,簡寫為App Domain)。所以我們的.Net程式,不管是Windows窗體、Web窗體、控制檯應用程式,又或者是一個程式集,總是執行在一個App Domain中
如果只有一個類庫程式集(.dll檔案),是無法啟動一個程式的(它並非可執行檔案)。所以,建立程式需要載入一個可執行程式集(Windows 窗體、控制檯應用程式等.exe檔案)。當可執行程式集載入完畢,.Net會在當前程式中建立一個新的應用程式域,稱為預設應用程式域。一個程式中只會建立一個預設應用程式域,這個應用程式域的名稱與程式集名稱相同。預設應用程式域不能被解除安裝,並且與其所在的程式同生共滅。
那為什麼引入了應用程式域,就能執行託管程式碼呢?也就是,應用程式域是如何提供託管環境的?
簡單來說,是因為應用程式域允許它所載入的程式集訪問由.net Runtime所提供的服務。這些服務包括託管堆(Managed Heap),垃圾回收器(Garbage collector),JIT 編譯器等.Net底層機制,這些服務本身(它們構成了.Net Runtime)是由非託管C++實現的。
可以看到,一個程式可以包含多個應用程式域,每個應用程式域都有自己的執行時環境。
建立自定義Web伺服器的應用程式域
一個程式可以包含多個應用程式域,我們建立一個控制檯exe程式來啟動程式,我們稱啟動程式建立的預設應用程式域叫控制檯應用程式域,把監聽以及資料回發的功能放在這個應用程式域中。另外建立一個應用程式域來進行請求的處理,假設叫作Web伺服器應用程式域。兩個應用程式域之間的通訊涉及到一個跨域訪問的問題,跨域訪問的類必須派生自System.MarshalByRefObject。
請求處理類
新增一個程式集Aspnet.WebServer,並定義一個WebServer類:
namespace Aspnet.WebServer
{
public class WebServer:System.MarshalByRefObject
{
public void ProcessRequest(string page,string query,System.IO.TextWriter writer)
{
Console.WriteLine("Web伺服器應用程式域ID={0}",AppDomain.CurrentDomain.Id);
System.Web.Hosting.SimpleWorkerRequest simpleRequest = new System.Web.Hosting.SimpleWorkerRequest(page,query,writer);
System.Web.HttpRuntime.ProcessRequest(simpleRequest);
}
}
}
這個類封裝了一個SimpleWorkerRequest物件,並呼叫該應執行時ProcessRequest方法。這就是我們自定義的Web處理程式,列印出當前應用程式域ID,並轉給執行時處理。
重寫HttpLinstener監聽程式
監聽程式,就使用上面的HttpLinstener,為它加上處理程式的委託,並把請求處理轉給此委託。
public delegate void ProcessRequestHandler(string page, string query, System.IO.TextWriter writer);
public ProcessRequestHandler processRequestHandler;
public void setProcessRequestHandler(ProcessRequestHandler processRequestHandler)
{
this.processRequestHandler = processRequestHandler;
}
新的監聽程式:
public void run()
{
string prex = "http://localhost:9152/";
System.Net.HttpListener httpListener = new System.Net.HttpListener();
httpListener.Prefixes.Add(prex);
httpListener.Start();
Console.WriteLine("控制檯應用程式域ID={0}",AppDomain.CurrentDomain.Id);
Console.WriteLine("開始監聽.....");
while (true)
{
HttpListenerContext context = httpListener.GetContext();
Console.WriteLine("已經建立聯接");
try
{
HttpListenerRequest request = context.Request;
Console.WriteLine(request.ToString());
HttpListenerResponse response = context.Response;
using (TextWriter streamWriter = new StreamWriter(response.OutputStream))
{
string path = Path.GetFileName(request.Url.AbsolutePath);
StringWriter sw = new StringWriter();
this.processRequestHandler(path, request.Url.Query, sw);
var code = sw.Encoding;
//--獲取處理結果
string content = sw.ToString();
sw.Close();
Console.WriteLine(content);
response.ContentLength64 = System.Text.Encoding.UTF8.GetByteCount(content);
response.ContentType = "text/html; charset=utf-8";
streamWriter.Write(content);
Console.WriteLine("Process OK");
}
if (Console.KeyAvailable)
{
break;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
httpListener.Stop();
}
自定義應用程式域
現在監聽程式和處理程式都準備完畢,需要自定義我們的Web應用程式域。使用CreateApplicationHost方法建立:
namespace Aspnet.SelfAppDomain
{
public class SelfAppDomain
{
public void build()
{
System.Type hostType = typeof(WebServer.WebServer);
WebServer.WebServer sefWebServer = System.Web.Hosting.ApplicationHost.CreateApplicationHost(hostType, "/",
System.Environment.CurrentDirectory) as WebServer.WebServer;
Console.WriteLine("CurrentDomain ID:{0}", AppDomain.CurrentDomain.Id);
WebHttp httpListener = new WebHttp();
httpListener.setProcessRequestHandler(sefWebServer.ProcessRequest);
httpListener.run();
}
}
}
要特別注意的是,使用CreateApplicationHost建立新的應用程式域,這個應用程式將重新載入hostType,按照:
1) GAC
2) 網站物理檔案目錄下的bin資料夾。
的順序尋找,不然會報未找到檔案錯誤。
但比較奇詭的是,我把程式集拷到bin目錄下,總是不成功。因此,為了順利執行,只好把它載入到GAC中。載入方法:
執行VS2015命令提示符
1) 通過sn –k 命令生成公鑰
2) 程式集屬性中使用此公鑰簽名
3)使用gacutil.exe –i 命令新增到GAC
如果一切順利完畢,執行此控制檯應用程式,就完成了我們自定義的Web伺服器:
在瀏覽器中輸入http://localhost:9152,控制檯上顯示確實有了兩個應用程式域:
相關文章
- Asp.net本質論之應用程式物件ASP.NET物件
- ASP.NET本質論ASP.NET
- ASP.NET Core 框架本質學習ASP.NET框架
- Docker中部署.NET CORE應用(控制檯應用程式篇)Docker
- 隱藏控制檯應用程式的視窗
- ASP.NET Core 應用程式狀態ASP.NET
- ASP.NET的模擬應用程式ASP.NET
- 控制檯程式使用ABP框架應用層服務框架
- 控制檯應用程式獲取計算機名計算機
- 【C#之控制檯與窗體應用程式】C#
- 構建安全的ASP.NET應用程式ASP.NET
- ASP.NET的應用程式域須知ASP.NET
- 開發安全的ASP.NET應用程式ASP.NET
- idea建立、執行、打包控制檯程式Idea
- 使用Angular.JS和ASP.NET建立單頁應用AngularJSASP.NET
- 建立ASP.NET WEB自定義控制元件(轉)ASP.NETWeb控制元件
- ASP.NET Web API 控制器建立過程ASP.NETWebAPI
- 【.NET】控制檯應用程式的各種互動玩法
- C# 控制檯應用程式中輸出彩色字型C#
- asp.net session的應用ASP.NETSession
- 開始使用ASP.NET Core - 建立第一個Web應用ASP.NETWeb
- 用ASP.Net編寫留言本 (轉)ASP.NET
- 七天學會ASP.NET MVC(七)——建立單頁應用ASP.NETMVC
- asp.net驗證碼應用ASP.NET
- ASP.NET MVC - PageData的應用ASP.NETMVC
- 建立模板化的ASP.NET 使用者控制元件ASP.NET控制元件
- 【原創】使用.NET Core 1.0建立一個Self-Contained控制檯應用AI
- ASP.NET MVC不可或缺的部分——DI及其本質工作分析ASP.NETMVC
- 控制程式碼的本質(整理-收藏)
- ASP.NET 伺服器控制元件的生命週期ASP.NET伺服器控制元件
- ASP.NET Web Forms – 伺服器控制元件簡介ASP.NETWebORM伺服器控制元件
- 在Docker容器中執行ASP.NET MVC應用程式DockerASP.NETMVC
- 【趙勳】在ASP.NET應用程式中上傳檔案ASP.NET
- 自帶打包工具打包Asp.Net Web應用程式ASP.NETWeb
- ASP.NET Web應用程式安全解決方案淺析ASP.NETWeb
- asp.net mvc控制器啟用全分析ASP.NETMVC
- .NET Core建立一個控制檯(Console)程式
- asp.net DataList控制元件分頁程式碼ASP.NET控制元件