HTTP是一種應用層的協議,基礎是TCP/IP協議。TCP/IP協議其實並不是一個協議,而是一組協議,包含了TCP、IP、UDP等多個傳輸層和網路層的協議。
五層網路協議模型
一次典型的HTTP請求,包含幾個過程:客戶端建立連線→客戶端傳送請求→伺服器處理請求並返回資料→關閉連線。通常在流程完成之後連線就會關閉掉,伺服器也不會儲存連線的任何狀態。雖然HTTP協議也支援keep-alive機制,可以不需要重新建立TCP連線,但是在網路遊戲中通常不會用到這個屬性。
而Socket其實並不是一種協議,而是指一種通訊的方式。Socket這個詞通常翻譯為“套接字”,然而我很不喜歡這種生造出來的翻譯。這詞兒的原始含義是插座、插口、插槽,我覺得無論哪個都比不知所云的“套接字”要好。
Socket大致上可以理解為一種點對點的通訊方式,作業系統為應用程式提供了一系列Socket相關的介面,而應用程式呼叫相關介面可以更方便快捷地操作資料,與另一個Socket埠進行通訊。雖然在網路遊戲中Socket通常使用的是TCP協議,實際上Socket也支援UDP,甚至支援自己寫協議(Raw模式)。只要是透過作業系統提供的Socket介面進行點對點的資料通訊,就叫做Socket。(所以其實Socket翻譯成插口插槽還更形象一些)
那麼問題來了,HTTP難道不是點對點的嗎?那麼HTTP和Socket有什麼區別呢?答案是這兩者本來就沒有什麼衝突關係,它們並不是一個維度上的說法,就像一個人喜歡吃火鍋和這個人是程式設計師這兩者之間沒有什麼聯絡。HTTP是網路協議層的東西,而Socket是一種通訊方式。
之所以在遊戲領域大家通常將HTTP和Socket放在一起說,其實主要指的是短連線和長連線這兩種模式。短連線一般使用HTTP協議,只要完成一次資料交換就關閉連線,伺服器並不儲存連線狀態和資料,也沒有辦法主動向客戶端傳送訊息。而長連線一般使用TCP協議,直接呼叫Socket介面進行資料通訊,在雙向流式傳輸的Socket通訊中,雙方的地位是對等的,既可以由客戶端向服務端發起訊息,也可以由服務端向客戶端傳送訊息。
首先來探討一個問題:客戶端如何知道自己斷線了?
通常來說,斷線有兩種方式,一是主動,二是被動。
Socket連線是可以主動關閉的,伺服器或客戶端任何一方主動關閉連線對方都會受到相應的訊息。
當對方主動關閉連線或者發生其他異常時,另一端的recv介面的返回值會小於等於0,這個返回值是即時生效沒有延遲的,這時候就知道,對方關閉了連線,在這裡可以做相應的處理。比如說客戶端主動關閉,伺服器收到訊息後,可以記錄一下使用者的離線時間;伺服器主動關閉,客戶端收到訊息可以重新連線或者給使用者彈出一些提示“你被踢下線了”之類的。大部分情況下,瀏覽器的網頁重新整理、瀏覽器或app關閉時,都會立刻收到這個訊息。當然事無絕對,這一般跟你的應用程式載體(即作業系統或瀏覽器)有關,也有部分載體沒有這個功能,如果沒這個功能就得看下面的處理方式了。
另一種情況相對複雜一點,那就是被動斷線,一般情況就是斷網。碰到斷網,Socket是無法馬上得知的。那麼如何來判斷到底有沒有斷網呢?
大家可以回想一下,在日常使用電腦的時候,是如何判斷自己有沒有斷網的。
相信很多人的第一反應是,開啟瀏覽器,輸入baidu.com,如果等了半天瀏覽器一直是一片空白,那麼ok,確定自己斷網了。
很多事情的本質其實是一樣的,在Socket連線中,碰到斷網也是類似的處理方式。最常規的方法就是用心跳包來檢測。心跳包的基本原理是,每隔一段時間,一方就向另一方打個招呼,傳送一段很小的資料,如果對方有迴應,那就OK,如果對方沒有迴應,那就是斷網了。由於這種隔一會兒就來一下判斷是不是還活著的方式很像是心跳,所以通常稱之為“心跳檢測”。
如果Socket使用的是TCP協議,現在的主流作業系統都支援TCP的Keep Alive模式。Keep Alive就是系統幫你封裝好的心跳檢測,每隔一段時間系統幫你發個空包(注意:空包不代表沒有任何資料傳輸,只是沒有任何業務層資料而已,在TCP通訊中,即使是空包也是有資料傳輸的),對面回了說明這條Socket連線還活著,對面沒回說明連線已完蛋。
Keep Alive有這麼幾個引數:
tcp_keepalive_time:系統的心跳包並不是一開始就會發的,而是當TCP連線長時間都沒有資料通訊時才會傳送。打個比方大概就是,如果對方剛跟你說過話,就不必再去檢測他是否還活著,當對方好久都沒有反應了,你才會去捅捅他問哎兄弟你還活著嗎。這個引數就是設定多久沒說話才會去詢問對方。
tcp_keepalive_intvl:每隔多久傳送一次心跳包。前面說過,心跳包是每隔一段時間傳送一個,這個引數就是控制間隔的。
tcp_keepalive_probes:重試次數。假如有一次心跳對方沒有回覆,可能這時候並不能確認連線已經斷開,而是會反覆嘗試幾次,幾次都沒有回覆才會確信這條連線已經完蛋。這個引數是控制重試次數的。
系統提供的Keep Alive功能從機制上來說是比較完善的,不過,在實際應用中,一般並不建議使用系統提供的TCP Keep Alive來做心跳檢測。原因主要有幾個,首先keep alive只有TCP協議才支援,UDP和Raw(自定義協議)模式都是無法支援的;更重要的原因是,整個過程對應用層來說全部是不可控的,對業務來說不夠靈活。實際上,瞭解Keep Alive的原理,自己寫一個也是很簡單的事情,所以一般建議自己在應用中實現心跳檢測的功能。
在網路遊戲中,心跳檢測和斷線重連一般都由客戶端發起。客戶端每隔一段時間發一個心跳訊息,同時去監聽伺服器回覆的訊息,如果客戶端連續傳送了N的訊息都沒有收到回覆,就做相應的處理,比如說在使用者無感的情況下默默地重新建立連線,又或者是給使用者彈出一個提示告知斷網,都是很常見的處理方式。