最開始咱們須要明白一件事情,由於這是這篇文章的前提:html
HTTP協議只是一個應用層協議,它底層是經過TCP進行傳輸數據的。所以,瀏覽器訪問Web服務器的過程必須先有「鏈接創建」的發生。java
而有人或許會問:衆所周知,HTTP協議有兩大特性,一個是「無鏈接」性,一個是「無狀態」性。這裏的「無鏈接」豈不是跟上面的說法有衝突?其實這裏並無矛盾,只是人們對「鏈接」這個詞的理解有差別。首先咱們來看一下瀏覽器向Web服務器發出Http請求以及Web服務器給瀏覽器回覆的過程:chrome
1)瀏覽器建立Socket,按給定IP(域名)和端口(默認爲80)鏈接服務器。好比使用相似Socket.Connect()、Socket.BeginConnect()等方法;編程
2)鏈接成功後,瀏覽器依據HTTP協議規範(關於協議,後面有講到),向Web服務器發送請求數據。好比「請求行」、「請求頭標」以及「請求數據」等,這裏可能使用相似Socket.Send()、Socket.BeginSend()等方法。【關於HTTP協議中的請求行、請求頭標等請參見http://www.cnblogs.com/visec479/diary/2014/09/15/3972715.html】c#
3)瀏覽器等待服務器處理並返回數據;瀏覽器
4)Web服務器端使用Socket.Accept()、Socket.BeginAccept()等方法偵聽到瀏覽器的鏈接後,便開始接收瀏覽器發送的數據。接收到請求數據後,依據HTTP協議規範解析數據,而後處理,最終將處理結果(如html文檔)發回給瀏覽器,這裏可能用到相似Socket.Send()、Socket.BeginSend()等方法;服務器
5)Web服務器發送完處理結果後,關閉Socket;cookie
6)瀏覽器接收Web服務器發回的數據(如html),將其顯示在瀏覽器UI界面。關閉socket;網絡
7)一次「瀏覽器到Web服務器」的http請求結束;數據結構
8)下一次瀏覽器須要請求Web服務器,跳轉到第1)步循環開始。
用圖表示以上過程:
圖1
如上圖1所示。瀏覽器向Web服務器發送http請求以前,須要先創建鏈接。沒錯,它們間創建鏈接的過程跟咱們平時開發socket程序相似。由此可知,HTTP協議的「無鏈接」特性並非指:瀏覽器與Web服務器進行數據交換時,不須要創建鏈接。那麼「無鏈接」特性到底指什麼呢?咱們再看圖1會發現,瀏覽器每次請求完畢後都會與服務器處於「斷開」狀態,下一次請求時再從新與服務器創建鏈接。HTTP的無鏈接特性偏偏就是指瀏覽器的每次請求都必須從新與服務器創建鏈接,正常狀況下,瀏覽器不會與Web服務器保持長時間的鏈接狀態。現將HTTP協議的兩大特性歸結以下:
無鏈接:
服務器與瀏覽器之間的一次鏈接只處理一個http請求,請求處理結束後,鏈接斷開。下一次請求再從新創建鏈接。
無狀態:
服務器不會保存瀏覽器信息。也就是說,在服務器端,第一次http請求處理的結果不會保留到第二次請求。若是第二次請求處理時,須要用到第一次請求處理的結果,瀏覽器在第二次請求時,必須將第一次處理結果從新傳回給Web服務器(好比使用cookie)。
關於「協議」:
這個話題有點大,不是我能掌控得了的。不過對於今天這篇文章,我仍是盡最大可能說一點。計算機中協議範疇普遍,單就網絡通訊中的協議,就不可勝數,OSI七層中每層都不少種協議。那麼協議到底本質上是個什麼東西呢?單就通訊中的協議來說,協議的本質其實就是一種數據結構,相似代碼中的結構體,說得再底層一點,就是一個字節流,規定好了第一個字節表明什麼、第二個字節表明什麼等等。
協議的做用跟咱們平時所說的「契約」、「約定」相似,一個團隊合做的任務,合做各方必須同時遵照事先的約定,最後工做才能正常進行下去。網絡通訊中也同樣,通訊雙方收/發數據時必須按照實現規定好了的結構去發送/接收,一方不遵照該規範,通訊就不能成功。這裏說的結構規範其實就是「協議」。協議有如下做用:
1)既然是規範,那麼按照規範作事,本身作的別人更容易理解,便於交流;
2)將規範寫成文檔,提供給其餘人,方便後期他人擴展。由於只要知道了通訊規範,那麼很容易就能夠編寫出擴展模塊與原有系統協調工做。
3)計算機網絡通訊中,有些因素決定了咱們必須按照規定的格式收發數據,好比TCP通訊中,因爲數據是按照「流」式傳輸的,若是咱們事先不定義數據傳輸規範,那麼很難判斷TCP傳輸的數據邊界。
就網絡通訊協議來說,應用層協議與咱們程序開發最爲密切(至少對咱們使用c#、java的人來說),其餘向tcp、udp等傳輸層協議幾乎用不到。咱們開發的通訊程序,必須遵照實現定義好了的應用層協議,好比瀏覽器和Web服務器都遵照了HTTP應用層協議,只有這樣,它們才能正常交互。假若咱們本身開發一個程序,正確地遵照了HTTP協議,那麼咱們的程序也可以像chrome、IE等瀏覽器同樣,去訪問Web服務器。
文章末尾有一個使用socket模擬瀏覽器請求Web服務器的demo,實現的功能咱們徹底可使用相似WebClient、WebRequest等類型去實現。demo功能以下:
1)使用Socket鏈接Web服務器(任意);
2)按照HTTP協議格式發送HTTP請求(使用Socket.Send方法);
3)按照HTTP協議格式解析Web服務器返回的數據(其實就顯示在了UI界面)
(開發這樣的程序須要咱們充分熟悉socket編程、HTTP協議格式)
如下是發送HTTP請求的代碼:
1 /// <summary>
2 /// 發送請求 3 /// </summary>
4 /// <param name="socket"></param>
5 private void SendRequest(Socket socket) 6 { 7 string h1 = "GET " + _path + " HTTP/1.1\r\n"; 8 string h2 = "Accept: */*\r\n"; 9 string h3 = "Accept-Language: zh-cn\r\n"; 10 string h4 = "Host: " + _host + "\r\n"; 11 string h5 = "User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36\r\n"; 12 string h7 = "Connection: close\r\n\r\n"; 13
14 byte[] send_buffer = Encoding.UTF8.GetBytes(h1 + h2 + h3 + h4 + h5 + h7); 15 socket.Send(send_buffer); 16 Print("請求發送完畢,等待Web Server回覆..."); 17 socket.BeginReceive(_buffer, 0, 640 * 1024, SocketFlags.None, new AsyncCallback(OnReceive), socket); 18 }
主程序:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Windows.Forms; 5 6 namespace socket_browser 7 { 8 static class Program 9 { 10 /// <summary> 11 /// 應用程序的主入口點。 12 /// </summary> 13 [STAThread] 14 static void Main() 15 { 16 Application.EnableVisualStyles(); 17 Application.SetCompatibleTextRenderingDefault(false); 18 Application.Run(new Form1()); 19 } 20 } 21 }
Demo下載:
http://yunpan.cn/Q75bZUrw8n5nb 訪問密碼 c961