【簡單Web伺服器搭建】基於Socket實現的最簡單的Web伺服器【ASP.NET原理分析】

来源:http://www.cnblogs.com/shouce/archive/2016/04/13/5385621.html
-Advertisement-
Play Games

通常,我們藉助瀏覽器(通常是IE,FireFox或者Chrome)瀏覽網頁,例如,我們在地址欄中輸入DebugLZQ的博客網址http://www.cnblogs.com/DebugLZQ/,回車之後,就會在瀏覽器的視窗中看到Debug的主頁,如下圖所示: 在這個簡單的操作背後影藏了巨大的複雜性。 ...


  通常,我們藉助瀏覽器(通常是IE,FireFox或者Chrome)瀏覽網頁,例如,我們在地址欄中輸入DebugLZQ的博客網址http://www.cnblogs.com/DebugLZQ/,回車之後,就會在瀏覽器的視窗中看到Debug的主頁,如下圖所示:

      在這個簡單的操作背後影藏了巨大的複雜性。

      我們在地址欄中輸入的內容稱為通用資源標記符(Universal Resource Identifier,URI)它有很多種樣式,在Web中我們通常稱為統一資源定位符(Uniform Resource Locator,URL)的形式,它的格式如下: 

      協議://主機[.埠號][絕對路徑[?參數]] 

      在http://www.cnblogs.com/DebugLZQ/中,http表示協議名稱;www.cnblogs.com表示主機的地址;可選的埠號沒有出現,那麼,將使用http協議預設的埠號80;絕對路徑為/DebugLZQ/;在這個例子中沒有參數出現。

      在.NET中,不管是URI還是URL,都使用定義在System命名空間中得URI類來進行處理。對應上面的介紹,這個類定義了5個屬性,分別對應5個組成部分,如下所示:

    Scheme:協議的名稱

    Host:取得URI地址中得主機部分

    Port:取得埠號

    AbsolutePath:絕對路徑部分

    Query:URI地址中得參數部分

      下麵的例子演示了地址中各個部分:

 

using System; using System.Collections.Generic; using System.Linq; using System.Text;   namespace URI說明 {     class Program     {         static void Main(string[] args)         {             System.Uri DebugLZQAddress = new Uri("http://www.cnblogs.com/DebugLZQ/");             Console.WriteLine("Scheme: {0}",DebugLZQAddress.Scheme );             Console.WriteLine("Host: {0}", DebugLZQAddress.Host );             Console.WriteLine("Port: {0}", DebugLZQAddress.Port );             Console.WriteLine("AbsolutePath: {0}", DebugLZQAddress.AbsolutePath );             Console.WriteLine("Query: {0}", DebugLZQAddress.Query );         }     } }

  輸出結果如下:

      其中絕對路徑部分使用類似於Unix的文件目錄的形式來描述伺服器中得資源,這個絕對路徑唄傳送到伺服器之後,在Web伺服器上通常被稱為虛擬路徑。

      我們在地址欄輸入URL後,如何找到伺服器呢?互聯網上的主機千千萬,我們要訪問的伺服器是互聯網上數千萬台伺服器中得一臺,它很可能遠在地球的另一邊。瀏覽器要找到伺服器,需要提供伺服器的網路地址。

      在當前的TCP/IP協議下,所謂伺服器的網路地址,就是一個IP地址,目前我們使用IPv4的地址,即IP協議第4個版本規定的地址,每個地址由四個位元組共32位組成。理論上將,可以表示4G個網路地址。通常我們用遠點分隔四個數字來表示一個地址,每個數字對應地址的一個位元組,例如,微軟的IP地址為:207.46.19.254,直接在地址欄中輸入http://207.46.19.254也可以訪問網頁。

      但是,這些數字實在很難讓人記憶,人們更願意通過一個有意義的名字來找到一臺主機。在經歷了短暫得互聯網初期階段之後,1983年,保羅·莫卡派(Paul Mockapetris)發明瞭功能變數名稱系統,這樣,在互聯網上,我們可以為IP地址起一個有意義的名字以方便找尋主機,這個名字成為功能變數名稱。比如,微軟Web伺服器的功能變數名稱為www.microsoft.com,這個名字對應實際IP地址為207.46.19.254。

      雖然這個名字很好記,但是只有這個名字並不能直接找到微軟的Web伺服器,必須建立起名字和IP地址之間的對應關係。這個工作由功能變數名稱伺服器DNS(即Domain Name Server)完成。DNS伺服器提供一個列私語分層的通訊錄,允許用戶通過功能變數名稱來查找對應的地址,或者完成通過地址來查找對應的功能變數名稱。通常情況下,互聯網服務商已經為我們自動設置了DNS伺服器,因此可以簡單地通過www.microsoft.com功能變數名稱找到微軟的Web伺服器。

      找到伺服器之後,需要將請求從我們的客戶端傳輸到伺服器,那麼,兩台電腦是如何通信的呢?他們如何才能理解彼此發送的數據呢?這就需要提到協議。

      當瀏覽器尋找到Web伺服器的地址之後,瀏覽器幫助我們把對伺服器的請求轉換為一系列參數發送給Web伺服器。伺服器受到瀏覽器發來的請求參數之後,將會分析這些數據,併進行處理。然後向瀏覽器回應處理的結果,也就是一些新的數據;這些數據通常是HTML網頁或者圖片。瀏覽器收到之後,解析這些數據,將它們呈現在瀏覽器的視窗中,這就是我們看到的網頁。

      在瀏覽器與Web伺服器的對話中,需要使用雙方都能夠理解的語法規範進行通信,這種程式之間進行通信的語法規範,我們稱之為協議。協議有許多種,根據國際標準化組織ISO的網路參考模型,程式與程式之間的通信可分為7層,從低到高依次為:物理層、數據鏈路層、網路層、傳輸層、會話層、表示層、應用層。每層都有自己對應的協議。比如,應用層之間的協議我們稱之為應用層協議。不同的應用程式可能有著不同的應用層協議。同一層的協議也可能有很多種。

     

      瀏覽器與Web伺服器之間的協議是應用層協議,當前,我們主要遵循的協議為HTTP/1.1。HTTP協議是Web開發的基礎,這是一個無狀態的協議,客戶機與伺服器之間通過請求和相應完成一次會話(Session)。每次會話中,通信雙方發送的數據稱為消息(Message),消息分兩種:請求消息和回應消息。

      消息的格式如圖所示。

      圖DebugLZQ用繪圖畫的,不太美觀。吼吼。。。 博友心聲:真醜。。。    

      每個消息可能由三部分組成,第一部分為請求行或者回應的狀態行,第二部分為消息的頭部,第三部分為消息體部分。消息頭部分和消息體部分使用一個空行進行分隔。

      通常情況下,我們在客戶端使用瀏覽器來訪問伺服器,瀏覽器軟體幫助我們構造所有的請求消息。使用Fiddler軟體,可以幫助我們檢測到瀏覽器與伺服器之間的通信內容,如圖所示。

上圖右上部為瀏覽器請求的內容,可以看到,第一行為請求行,請求的內容為:

      GET http://www.microsoft.com/en-us/default.aspx HTTP/1.1

      下麵的連續N行為請求頭部分,然後是一個空行,由於是GET請求,所以沒有請求體部分。

      圖右下部為伺服器回應的內容,第一行為回應的狀態行,HTTP/1.1 200 OK表示請求的內容可以找到,但是需要到另外的地址去取。下麵的15行為回應的頭部。一個空行分隔了回應的頭部和回應體部分,回應體中為一個簡單的HTML網頁。

      HTTP協議定義了內容的格式,這是一個應用層的協議,應用層協議的內容需要通過傳輸層在瀏覽器和伺服器之間傳送,TCP/IP協議是ISO網路參考模型的一種實現。在TCP/IP協議中,與網路程式員相關的主要有兩層:傳輸層和應用層。

      傳輸層協議負責解決數據傳輸問題,包括數據通行的可靠性問題。傳輸層依賴更底層的網路層來完成實際的數據傳輸,在TCP/IP網路協議中,負責可靠通信的傳輸層協議為TCP協議。而網路層一般用網路驅動來實現,普通的程式員不會涉及;在TCP/IP協議中,網路層的協議為IP協議。

      應用層用於在特定的應用程式之間傳輸數據。HTTP協議就是TCP/IP協議中專門用於瀏覽器與Web伺服器之間通信的應用層協議。應用層協議依賴於傳輸層協議完成數據傳輸,傳輸層協議依賴於網路層協議王城數據傳輸,他們之間的關係如下圖(瀏覽器與伺服器之間網路通信的傳輸過程):

      到這裡,我們的準備理論超不讀了,哦,還得再認識下Socket。

      在遙遠的Unix時代,為瞭解決傳輸層的編程問題,從4.2BSD Unix開始,Unix提供了類似於文件操作的網路操作方式----Socket。通過Socket,程式員可以像文件一樣通過打開、寫入、讀取、關閉等操作完成網路編程。這使得網路編程可以統一到文件操作之下。通過Socket幫助程式員解決網路傳輸層的問題,而系統中得網路系統負責處理網路內部的複雜操作,這樣程式員就可以比較容易地編寫網路應用程式。需要註意的是應用層的協議需要針對網路程式專門處理,Socket不負責應用層的協議,僅僅負責傳輸層的協議。

      當然網路畢竟不是簡單的文件,所以,在使用Socket的時候,程式員還是需要設置一些網路相關的細節問題參數。

      當通過Socket開髮網絡應用程式的時候,首先需要考慮所使用的網路類型,主要包括以下三個方面:

    1)Socket類型,使用網路協議的類別,如IPv4的類型為PF_INET。

    2)數據通信的類型,常見的數據報(SOCK_DGRAM)、數據流(SOCK_STREAM)。

    3)使用的網路協議,比如:TCP協議。

      在同一個網路地址上,為了區分使用相同協議的不同應用程式,可以為不同的應用程式分配一個數字編號,這個編號稱為網路埠號(port)。埠號是一個兩位元組的證書,取值範圍從0~65535。IANA(Internet Assigned Number Authority,互聯網地址分配機構)維護了一個埠分配列表,這些埠分三類,第一類的範圍是0~1023,稱為眾所周知的埠,由IANA進行控制和分配,由特定的網路程式使用,例如,TCP協議使用80號埠來完成HTTP協議的傳輸。第二類的範圍是1024~49151,稱為登記埠,這些埠不由IANA控制,但是IANA委會了一個登記的列表,如果沒有在IANA登記的話,也不應該在程式中使用。但是大多數的系統中,在沒有衝突的情況下,也可以有用戶程式使用。第三類的範圍是49152~65535,稱為動態或者似有埠號,這些埠可以由普通用戶程式使用。

      對於一個網路應用程式來說,通過地址、協議和埠號可以唯一地確定網路上的一個應用程式。其中地址和埠的組合稱為端點(EndPoint)。每個Socket需要綁定到一個端點上與其他端點進行通信。

      在.NET中,System.Net命名空間提供了網路編程的大多數數據類型以及常用操作,其中常用的類型如下:

    1)IPAddress類用來表示一個IP地址。

    2)IPEndPoint類用來表示一個IP地址和一個埠號的組合,稱為網路的端點。

    3)System.Net.Sockets命名空間中提供了基於Socket編程的數據類型。

    4)Socket類封裝了Socket的操作。

      常用的操作如下:

    1)Listen:設置基於連接通信的Socket進入堅挺狀態,並設置等待隊列的長度。

    2)Accept:等待一個新的連接,當新連接到達的時候,返回一個指針對新連接的Socket對象。通過新的Socket對象,可以與新連接通信。

    3)Receive:通過Socket接受位元組數據,保存到一個位元組數組中,返回實際接受的位元組數。

    4)Send:通過Socket發送預先保存在位元組數組中得數據。

      博友聲音:夠了,說了這麼多,DebugLZQ真是不嫌麻煩。。。快!!!!!

      DebugLZQ:吼吼,有了上面的基礎,下麵用代碼演示如何通過Socket編程創建一個簡單的Web伺服器。必要說明:這個伺服器通過49152號埠提供訪問,向瀏覽器返回一個固定的靜態網頁。在這個解決方案中,請求的消息由瀏覽器生成,併發送到伺服器,這個程式將簡單地顯示請求信息。回應的消息由伺服器程式生成,通過Socket傳輸層返回給瀏覽器。

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net;// using System.Net.Sockets;//   namespace 基於Socket的最簡單Web伺服器 {     class Program     {         static void Main(string[] args)         {             IPAddress address = IPAddress.Loopback;//取得本機的loopback網路地址,即127.0.0.1             IPEndPoint endPoint = new IPEndPoint(address, 49152);//創建可訪問的端點,49152表示埠號,如果設置為0,表示使用一個空閑的埠號             Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//創建socket,使用IPv4地址,數據通信類型為位元組流,TCP協議             socket.Bind(endPoint);//將socket綁定到一個端點上             socket.Listen(10);//設置連接隊列的長度             Console.WriteLine("開始監聽,埠號:{0}",endPoint.Port );             while (true)             {                 Socket client = socket.Accept();//開始監聽,這個方法會阻塞線程的執行,直到接受到一個客戶端的請求連接                 Console.WriteLine(client.RemoteEndPoint);//輸出客戶端的地址                 byte[] buffer = new byte[4096];//準備讀取客戶端請求的數據,讀取的數據將保存在一個數組中                 int length = client.Receive(buffer, 4096, SocketFlags.None);//接受數據                 //將請求數據翻譯為UTF-8                 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";                 byte[] statusLineBytes = utf8.GetBytes(statusLine);                 //準備發送回客戶端的網頁                 string responseBody = "<html><head><title>From Socket Server</title></head><body><h1>Hello world.<h1></body></html>";                 byte[] responseBodyBytes = utf8.GetBytes(responseBody);                 //回應的頭部                 string responseHeader = string.Format("Content-Type:text/html;charset=UTF-8\r\nContent-Length:{0}\r\n",responseBody.Length );                 byte[] responseHeaderBytes = utf8.GetBytes(responseHeader);                   //向客戶端發送狀態信息                 client.Send(statusLineBytes);                 //向客戶端發送回應頭                 client.Send(responseHeaderBytes);                 //頭部與內容的分隔行                 client.Send(new byte[]{13,10});                 //向客戶端發送內容部分                 client.Send(responseBodyBytes);                   //斷開與客戶端的連接                 client.Close();                 if (Console.KeyAvailable)                     break;             }             socket.Close();         }     } }

  運行後,在瀏覽器的視窗中輸入:http://localhost:49152/,瀏覽器中可以看到如下的顯示結果。

      在命令行中看到如下輸出:


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.介面 介面代表一種能力,實現該介面的類和介面沒有繼承關係 介面是用來實現的,類是用來繼承的。 C#中的類成員可以是任意類型,包括數組和集合。當一個類包含了數組和集合成員時,索引器將大大簡化對數組或集合成員的存取操作。 定義索引器的方式與定義屬性有些類似,其一般形式如下: [修飾符] 數據類型 t ...
  • 本質:實現了一個IEnumerable介面, 01.為什麼數組和集合可以使用foreach遍歷? 解析:因為數組和集合都實現了IEnumerable介面,該介面中只有一個方法,GetEnumerator() 02.數組是一種數據結構,它包含若幹相同類型的變數。數組是使用類型聲明的:type[] ar ...
  • 方法All返回布爾值bool,判斷集合中是否所有元素都滿足某一條件,通俗一點說,就是每一個元素,均符合同一個條件,它才返回真,不然返回假。舉列,創建一個model: source code: namespace Insus.NET.Models { public class Book { publi ...
  • 1.開篇之前先說一說NISI是什麼。 NSIS(Nullsoft Scriptable Install System)是一個開源的 Windows 系統下安裝程式製作程式。它提供了安裝、卸載、系統設置、文件解壓縮等功能。這如其名字所指出的那樣,NSIS 是通過它的腳本語言來描述安裝程式的行為和邏輯的 ...
  • 轉載自乎聲 前 一天準備下載VS2015預覽版,到VisualStudio官網一看,發現微軟發佈了VisualStudio2013的插件——Visual Studio Tools for Apache Cordova,實現跨平臺的開發。官網下載地址:http://www.microsoft.com/ ...
  • 朋友們,還記得我們在C#語言開發中用到過索引器嗎? 記得在獲得DataGridView控制項的某列值時:dgvlist.SelectedRows[0].Cells[0].Value; 記得在獲得ListView控制項的某列值時:listView1.SelectedItems[0].SubItems[0] ...
  • 1,工資計算公式 每一個企業都一定會用到工資計算,發工資是一件非常神聖的事情,而計算工資就是一項非常重要的工作。Excel有非常強大的公式功能,幫助了很多財務人員計算工資,但如果企業的人數比較多,而且工資的計算公式比較複雜,那使用Excel的人員必須是一個超高手了,但Excel維護起來也是非常困難的 ...
  • AjaxControlToolkit是一組控制項的集合,可以實現自動補充文本框,點擊文本框彈出日曆,加水印等Ajax效果,包含40多個控制項,具體實現效果如:http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/Default.aspx ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...