詳談socket請求Web服務器過程


最開始我們需要明白一件事情,因為這是這篇文章的前提:

HTTP協議只是一個應用層協議,它底層是通過TCP進行傳輸數據的。因此,瀏覽器訪問Web服務器的過程必須先有“連接建立”的發生。

而有人或許會問:眾所周知,HTTP協議有兩大特性,一個是“無連接”性,一個是“無狀態”性。這里的“無連接”豈不是跟上面的說法有沖突?其實這里並沒有矛盾,只是人們對“連接”這個詞的理解有差異。首先我們來看一下瀏覽器向Web服務器發出Http請求以及Web服務器給瀏覽器回復的過程:

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

3)瀏覽器等待服務器處理並返回數據;

4)Web服務器端使用Socket.Accept()、Socket.BeginAccept()等方法偵聽到瀏覽器的連接后,便開始接收瀏覽器發送的數據。接收到請求數據后,依據HTTP協議規范解析數據,然后處理,最終將處理結果(如html文檔)發回給瀏覽器,這里可能用到類似Socket.Send()、Socket.BeginSend()等方法;

5)Web服務器發送完處理結果后,關閉Socket;

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM