C#中使用Socket請求Web伺服器過程

周見智發表於2014-08-14

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

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/riky/archive/2007/04/09/705848.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七層中每層都很多種協議。那麼協議到底本質上是個什麼東西呢?單就通訊中的協議來講,協議的本質其實就是一種資料結構,類似程式碼中的結構體,說得再底層一點,就是一個位元組流,規定好了第一個位元組代表什麼、第二個位元組代表什麼等等。

協議的作用跟我們平時所說的“契約”、“約定”類似,一個團隊合作的任務,合作各方必須同時遵守事先的約定,最後工作才能正常進行下去。網路通訊中也一樣,通訊雙方收/發資料時必須按照實現規定好了的結構去傳送/接收,一方不遵守該規範,通訊就不能成功。這裡說的結構規範其實就是“協議”。協議有以下作用:

  • 既然是規範,那麼按照規範做事,自己做的別人更容易理解,便於交流;
  • 將規範寫成文件,提供給其他人,方便後期他人擴充套件。因為只要知道了通訊規範,那麼很容易就可以編寫出擴充套件模組與原有系統協調工作。
  • 計算機網路通訊中,有些因素決定了我們必須按照規定的格式收發資料,比如TCP通訊中,由於資料是按照“流”式傳輸的,如果我們事先不定義資料傳輸規範,那麼很難判斷TCP傳輸的資料邊界。

就網路通訊協議來講,應用層協議與我們程式開發最為密切(至少對我們使用c#、java的人來講),其他向tcp、udp等傳輸層協議幾乎用不到。我們開發的通訊程式,必須遵守實現定義好了的應用層協議,比如瀏覽器和Web伺服器都遵守了HTTP應用層協議,只有這樣,它們才能正常互動。倘若我們自己開發一個程式,正確地遵守了HTTP協議,那麼我們的程式也能夠像chrome、IE等瀏覽器一樣,去訪問Web伺服器。

文章末尾有一個使用socket模擬瀏覽器請求Web伺服器的demo,實現的功能我們完全可以使用類似WebClient、WebRequest等型別去實現。demo功能如下:

  • 使用Socket連線Web伺服器(任意);
  • 按照HTTP協議格式傳送HTTP請求(使用Socket.Send方法);
  • 按照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         }
View Code

下面是效果圖:

圖2

原始碼下載:https://files.cnblogs.com/xiaozhi_5638/socket_browser.rar

相關文章