咱們在地址欄中輸入的內容稱爲通用資源標記符(Universal Resource Identifier,URI)它有不少種樣式,在Web中咱們一般稱爲統一資源定位符(Uniform Resource Locator,URL)的形式,它的格式以下: html
協議://主機[.端口號][絕對路徑[?參數]] 程序員
在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地址中得參數部分socket
下面的例子演示了地址中各個部分:oop
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace URI說明 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 System.Uri DebugLZQAddress = new Uri("http://www.cnblogs.com/DebugLZQ/"); 13 Console.WriteLine("Scheme: {0}",DebugLZQAddress.Scheme ); 14 Console.WriteLine("Host: {0}", DebugLZQAddress.Host ); 15 Console.WriteLine("Port: {0}", DebugLZQAddress.Port ); 16 Console.WriteLine("AbsolutePath: {0}", DebugLZQAddress.AbsolutePath ); 17 Console.WriteLine("Query: {0}", DebugLZQAddress.Query ); 18 } 19 } 20 }
輸出結果以下:
其中絕對路徑部分使用相似於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傳輸層返回給瀏覽器。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Net;// 6 using System.Net.Sockets;// 7 8 namespace 基於Socket的最簡單Web服務器 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 IPAddress address = IPAddress.Loopback;//取得本機的loopback網絡地址,即127.0.0.1 15 IPEndPoint endPoint = new IPEndPoint(address, 49152);//建立可訪問的端點,49152表示端口號,若是設置爲0,表示使用一個空閒的端口號 16 Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//建立socket,使用IPv4地址,數據通訊類型爲字節流,TCP協議 17 socket.Bind(endPoint);//將socket綁定到一個端點上 18 socket.Listen(10);//設置鏈接隊列的長度 19 Console.WriteLine("開始監聽,端口號:{0}",endPoint.Port ); 20 while (true) 21 { 22 Socket client = socket.Accept();//開始監聽,這個方法會阻塞線程的執行,直到接受到一個客戶端的請求鏈接 23 Console.WriteLine(client.RemoteEndPoint);//輸出客戶端的地址 24 byte[] buffer = new byte[4096];//準備讀取客戶端請求的數據,讀取的數據將保存在一個數組中 25 int length = client.Receive(buffer, 4096, SocketFlags.None);//接受數據 26 //將請求數據翻譯爲UTF-8 27 System.Text.Encoding utf8 = System.Text.Encoding.UTF8; 28 string requestString = utf8.GetString(buffer, 0, length); 29 Console.WriteLine(requestString);//顯示請求 30 //迴應的狀態行 31 string statusLine = "HTTP/1.1 200 OK\r\n"; 32 byte[] statusLineBytes = utf8.GetBytes(statusLine); 33 //準備發送回客戶端的網頁 34 string responseBody = "<html><head><title>From Socket Server</title></head><body><h1>Hello world.<h1></body></html>"; 35 byte[] responseBodyBytes = utf8.GetBytes(responseBody); 36 //迴應的頭部 37 string responseHeader = string.Format("Content-Type:text/html;charset=UTF-8\r\nContent-Length:{0}\r\n",responseBody.Length ); 38 byte[] responseHeaderBytes = utf8.GetBytes(responseHeader); 39 40 //向客戶端發送狀態信息 41 client.Send(statusLineBytes); 42 //向客戶端發送迴應頭 43 client.Send(responseHeaderBytes); 44 //頭部與內容的分隔行 45 client.Send(new byte[]{13,10}); 46 //向客戶端發送內容部分 47 client.Send(responseBodyBytes); 48 49 //斷開與客戶端的鏈接 50 client.Close(); 51 if (Console.KeyAvailable) 52 break; 53 } 54 socket.Close(); 55 } 56 } 57 }
運行後,在瀏覽器的窗口中輸入:http://localhost:49152/,瀏覽器中能夠看到以下的顯示結果。
在命令行中看到以下輸出:
參考原文:http://www.cnblogs.com/DebugLZQ/archive/2011/12/06/2278234.html