談談網路遊戲中的延遲解決方案

2022-02-03 01:18:29 字數 2876 閱讀 7145

我們平常玩的很多網路遊戲,比如英雄聯盟/王者榮耀/pubg等,你感覺到卡頓往往不是因為你的網速問題,而是因為網路延時導致的,比如說lol美服的遊戲伺服器在美國,而你在中國的華中地區玩著美服lol,那麼你的延遲可能會在300ms左右,因為網路請求從美國到中國華中地區需要經過很多的路由,這裡面會消耗掉很多時間,如果發生了丟包,那麼重發需要的延遲更是會加倍增長,而延遲往往在150ms以上時往往就會影響到你的遊戲體驗了。

市面上會有一些遊戲加速器,它們會在國外安置伺服器,搭建一條線路,來保證你的請求能夠迅速的被處理,來降低遊戲的延遲。

現在很多的遊戲以及直播等低延遲需求的應用,一般都不會再使用原生的tcp或者udp來進行傳輸,而是在兩者的基礎上進行擴充套件修改,取其優異,比如tcp的傳輸可靠,udp的傳輸速度。

仔細想想以前在計算機網路課程中學習tcp/udp時,就對tcp的所謂可靠傳輸感覺很怪異,真的是可靠到太過慎重了,說到慎重就不得不提這個月的一部新番《這個勇者明明超強卻過分慎重 》。

昨天看了第一話,吹爆!

說回tcp,當時覺得它的超時重傳rto時間每次都會翻倍,如果乙個包多次超時,那下次重發這個包不是需要很久,延遲這不就上來了? 還有它的重傳,丟了乙個包就需要重傳之後所有的包,過分的慎重,雖然說可以保證可靠性,但是這對於我們毫秒級即時通訊之類的應用確實不太友好。

kcp是乙個快速可靠協議,能以比 tcp浪費10%-20%的頻寬的代價,換取平均延遲降低 30%-40%,且最大延遲降低三倍的傳輸效果。

純演算法實現,並不負責底層協議(如udp)的收發,需要使用者自己定義下層資料報的傳送方式,以 callback的方式提供給 kcp。

連時鐘都需要外部傳遞進來,內部不會有任何一次系統呼叫。

有一種叫kcptun的實現,可以把我們的tcp請求轉化成kcp+udp在公網上傳輸。

tcp是為流量設計的(每秒內可以傳輸多少kb的資料),講究的是充分利用頻寬。而 kcp是為流速設計的(單個資料報從一端傳送到一端需要多少時間),以10%-20%頻寬浪費的代價換取了比 tcp快30%-40%的傳輸速度。

tcp通道是一條流速很慢,但每秒流量很大的大運河,而kcp是水流湍急的小激流。

kcp有正常模式和快速模式兩種,通過以下策略達到提高流速的結果:

tcp超時計算是rtox2,這樣連續丟三次包就變成rtox8了,十分恐怖,而kcp啟動快速模式後不x2,只是x1.5(實驗證明1.5這個值相對比較好),提高了傳輸速度。

tcp丟包時會全部重傳從丟的那個包開始以後的資料,kcp是選擇性重傳,只重傳真正丟失的資料報。

傳送端傳送了1,2,3,4,5幾個包,然後收到遠端的ack: 1, 3, 4, 5,當收到ack3時,kcp知道2被跳過1次,收到ack4時,知道2被跳過了2次,此時可以認為2號丟失,不用等超時,直接重傳2號包,大大改善了丟包時的傳輸速度。

tcp為了充分利用頻寬,延遲傳送ack(nodelay都沒用),這樣超時計算會算出較大 rtt時間,延長了丟包時的判斷過程。kcp的ack是否延遲傳送可以調節。

arq模型響應有兩種,una(此編號前所有包已收到,如tcp)和ack(該編號包已收到),光用una將導致全部重傳,光用ack則丟失成本太高,以往協議都是二選其一,而 kcp協議中,除去單獨的 ack包外,所有包都有una資訊。

kcp正常模式同tcp一樣使用公平退讓法則,即傳送視窗大小由:傳送快取大小、接收端剩餘接收快取大小、丟包退讓及慢啟動這四要素決定。但傳送及時性要求很高的小資料時,可選擇通過配置跳過後兩步,僅用前兩項來控制傳送頻率。以犧牲部分公平性及頻寬利用率之代價,換取了開著bt都能流暢傳輸的效果。

如果網路永遠不卡,那 kcp/tcp 表現類似,但是網路本身就是不可靠的,丟包和抖動無法避免(否則還要各種可靠協議幹嘛)。在內網這種幾乎理想的環境裡直接比較,大家都差不多,但是放到公網上,放到3g/4g網路情況下,或者使用內網丟包模擬,差距就很明顯了。公網在高峰期有平均接近10%的丟包,wifi/3g/4g下更糟糕,這些都會讓傳輸變卡。

可能你玩的很多遊戲,或者說使用的加速器,都是利用了kcp來降低延遲。

下面是kcp在github上的位址,只需要將其中ikcp.c,ikcp.h兩個檔案匯入你的協議棧中就可以使用了。

建立 kcp物件:

// 初始化 kcp物件,conv為乙個表示會話編號的整數,和tcp的 conv一樣,通訊雙

// 方需保證 conv相同,相互的資料報才能夠被認可,user是乙個給**函式的指標

ikcpcb *kcp = ikcp_create(conv, user);

設定**函式:

// kcp的下層協議輸出函式,kcp需要傳送資料時會呼叫它

// buf/len 表示快取和長度

// user指標為 kcp物件建立時傳入的值,用於區別多個 kcp物件

int udp_output(const char *buf, int len, ikcpcb *kcp, void *user)

// 設定**函式

kcp->output = udp_output;

迴圈呼叫 update:

// 以一定頻率呼叫 ikcp_update來更新 kcp狀態,並且傳入當前時鐘(毫秒單位)

// 如 10ms呼叫一次,或用 ikcp_check確定下次呼叫 update的時間不必每次呼叫

ikcp_update(kcp, millisec);

輸入乙個下層資料報:

// 收到乙個下層資料報(比如udp包)時需要呼叫:

ikcp_input(kcp, received_udp_packet, received_udp_size);

處理了下層協議的輸出/輸入後 kcp協議就可以正常工作了,使用 ikcp_send 來向 遠端傳送資料。而另一端使用 ikcp_recv(kcp, ptr, size)來接收資料。

網路遊戲中的網路程式設計

談談網路遊戲中的 網路 程式設計 近段時間總是有不少人問我關於完成埠模型的一些資料,很多時候其實我很鬱悶,為什麼大家會選擇使用完成埠呢?或者說很多時候他們竟為了使用完成埠,而使用windows作為網路遊戲伺服器平台,還一開口羅列出一堆使用windows平台的網路遊戲案例。有些兄弟跟我一直爭論 win...

網路遊戲中的網路程式設計 效能

談談網路遊戲中的 網路 程式設計 近段時間總是有不少人問我關於完成埠模型的一些資料,很多時候其實我很鬱悶,為什麼大家會選擇使用完成埠呢?或者說很多時候他們竟為了使用完成埠,而使用windows作為網路遊戲伺服器平台,還一開口羅列出一堆使用windows平台的網路遊戲案例。有些兄弟跟我一直爭論 win...

Unity網路遊戲中的登入註冊以及Web安全

前言 現在網路遊戲越來越多,絕大部分網路遊戲都是需要登入註冊這一功能的,登入註冊一般使用弱聯網,即http來實現,由於只需要請求返回資訊而不需要實時互動,所以我們選擇了弱聯網。流程 我們會通過客戶端來請求伺服器登入註冊,伺服器根據客戶端請求的型別來對資料庫進行增刪改查,並返回資料給客戶端,客戶端拿到...