深入解析 TCP 和 UDP 类型的 socket 在网络游戏中的应用

棋牌游戏开发 8个月前 97浏览 0评论

“介绍”

你一定听说过套接字。有两种常见的类型:TCP 和 UDP。在编写在线游戏时,我们首先需要选择使用哪种类型的套接字。TCP、UDP 还是两者兼而有之。使用?

您选择的类型完全取决于您要编写的游戏类型。在以下文章中,我将假设您要编写一款“动作”在线游戏。例如:Halo 系列、战地 1942、Quake,这些游戏。

我们将仔细分析这两种插座类型的优缺点,并深入了解互联网的工作原理。一旦我们掌握了这些信息,就很容易做出正确的选择。

TCP/IP

TCP 代表“传输控制协议”,IP 代表“互联网协议”。您在互联网上所做的一切都基于这两个协议,例如浏览网页、发送和接收电子邮件等。

TCP

如果你用过 TCP socket,那你一定知道它是一种可靠连接协议,一种面向连接的传输协议。简单来说:两台机器先建立连接,然后两台机器互相发送数据,就像你们在一个(我是这样理解的:TCP socket 就像是连接的计算机之间共享一个‘文件’对象,两台计算机都通过读写这个‘文件’来进行数据传输)

这种连接是可靠有序的,也就是说发送的所有数据都保证能够到达传输的另一端,而另一端接收到的数据和发送的数据是一模一样的(可靠有序的。比如:A发送数据'abc'通过TCP socket传输给B,B接收到的数据一定是:'abc'。而不是'bca'或者'xueweihan'什么的!)。而传输的数据是以'数据流'(数据流:用来操作数据集的最小有序单位,就像操作本地文件中的流一样。所以TCP socket和文件对象很相似)的形式存在的,也就是说,TCP会把你的数据拆分成数据包,然后打包成数据包,然后通过网络发送出去。

注:就像读写文件一样,这个比较容易理解。

知识产权

“IP”协议在TCP协议之下(这涉及到七层互联网协议栈,我简单贴个图就不细说了)

“IP”协议没有连接的概念,它只是把上层(传输层)的数据包从一台计算机传递到另一台计算机。你可以把这个过程理解成一群人把纸条从一个人手里传到另一个人手里。它像一张纸条一样被传递了很多次,最终到达了纸条上标记的xxx的手中(纸条上写着“亲爱的xxx,偷看了3cm”)。

在投递过程中,没有任何保证能够保证这封信(信件)能够准确的送到收件人手中,寄件人发出一封信,却永远不知道这封信是否能够准确的到达收件人手中,除非收件人给他(寄件人)回信:“兄弟网络游戏开发,我收到信了!”(IP层只用于传输信息,并不进行信息验证等其他操作)

当然,信息传递的过程还是很复杂的,因为我们不知道具体的传输顺序,也就是不知道最优的传输路由(能让数据包快速到达目的地的最优路径),有时候“IP”协议会传输同一份数据的多份,这些副本通过不同的路由到达目的地,从而探索最优的传输路由。

这就是互联网的设计:自动优化和自动修复,解决连接问题。这真是一个很酷的设计。如果想更了解底层实现,可以看看TCP/IP方面的书。(推荐上野伸之的图解系列)

UDP

如果我们想直接发送和接收数据包,我们必须使用另一个套接字。

我们称之为UDP。UDP代表“用户数据报协议”,它是另一个建立在IP之上的协议,就像TCP一样,但没有TCP那么多的功能(例如建立连接,验证信息,数据流的拆分和合并等)

使用 UDP,我们可以向目标 IP 和端口(例如 80)发送数据包。数据包要么到达目标计算机,要么丢失。

对于接收方(目标计算机)来说,我们只需要监听特定的端口(例如:80),当收到任意一台计算机发来的数据包时(注意:UDP 不会建立连接),我们就知道这个数据包是发过来的。计算机地址(IP 地址)和端口,数据包的大小和内容。

UDP 是一种不可靠的协议。在实际使用中,大多数发送的数据包都会被接收,但通常会有 1-5% 的数据包丢失。偶尔,有时什么都收不到(所有数据包都丢失)。一个数据包都收不到。传输数据的计算机越多,出错的概率就越大。

UDP 协议中的数据包也是没有顺序的,比如你按照 1、2、3、4、5 的顺序发送了 5 个包,那么收到的包的顺序可能是 3、1、4、2、5。在实际使用中,大多数情况下,收到的数据的顺序是正确的,但也不总是这样。

最后,虽然UDP并不比“IP”协议先进多少,而且不可靠,你发送的数据要么完全到达,要么丢失。例如,如果你向另一台计算机发送一个256字节的数据包,那台计算机不会只收到100字节的数据包,它可能只收到256字节的数据包,也可能什么都收不到。这是UDP唯一能保证的,其他一切都由你决定。(据我了解,UDP协议只是一个简单的传输协议,它只保证数据包的完整性。注意是数据包,而不是信息。你需要自己做其他事情来完善这个协议,满足你自己的需求。)

TCP 与 UDP

我们如何选择使用TCP套接字还是UDP套接字?

我们先来看一下二者的特点:

TCP:

UDP:

从上面的描述我们不难发现,TCP 可以完成我们想要做的一切,并且非常容易使用。而 UDP 的使用难度非常大,我们需要自己编写和设计所有东西。显然,我们只需要使用 TCP 即可!

不,你以为这很简单(事实证明我太年轻了!)

当你开发像上面提到的那种 FPS(动作网游)时,使用 TCP 协议将是一个错误的决定,这个 TCP 协议将无法很好地发挥作用!为什么这么说呢?那你就需要知道 TCP 到底是干什么的,让它看起来非常简单。(我们继续往下看吧,这也是我最好奇的!!!你是不是心动了呢?)

TCP 内部工作原理

TCP 和 UDP 都是建立在“IP”协议之上,但是两者是完全不同的。UDP 和“IP”协议很相似,但是 TCP 隐藏了数据包中所有复杂和不可靠的部分,并将其抽象成类似文件的对象。

那么 TCP 是如何做到这一点的呢?

首先TCP是一个数据流协议,所以你只需要把输入的内容变成数据流,然后TCP协议会保证数据到达目的地,因为“IP”协议是通过数据包来传输信息的,TCP是建立在“IP”协议之上的,所以TCP必须把用户的输入数据流划分成数据包,TCP协议会把需要发送的数据进行排队,等排队的数据足够多时再把数据包发送给目标计算机。

在多人在线游戏中发送非常小的数据包时,这可能会出现问题。会发生什么情况?如果数据未达到缓冲区大小,数据包将不会被发送。这将导致一个问题:因为客户端用户输入请求后,需要尽快从服务器获得响应。如果 TCP 等待缓冲区满了再发送,就会有延迟,客户端的用户体验会非常糟糕!在线游戏几乎不能有任何延迟。我们希望看到的是“实时”和流畅。

TCP 有一个选项可以修复上面提到的缓冲区满了才发送的情况,那就是 TCP_NODELAY。这个选项允许 TCP 套接字在数据输入后立即发送数据,而不是等待缓冲区满了才发送。

不过,即使设置了TCP_NODELAY选项,在多人联机游戏中仍然会出现一系列问题。

这一切的根源在于 TCP 处理数据包丢失和无序数据包的方式,它给你一种有序和可靠的“错觉”。

TCP如何保证数据的可靠性?

本质上,TCP 所做的就是将数据流分解成数据包,通过不可靠的“IP”协议发送这些数据包,然后让数据包到达目标计算机并将它们重新组合成数据流。

但是我们如何处理数据包丢失?我们如何处理重复数据包和无序数据包?

这里就不详细说TCP是如何处理这些事情的了,因为都很复杂(想了解的可以参考上面我推荐的书单),但总的来说:TCP发送一个数据包,等待一段时间,检测到数据包丢失,因为没有收到它的ACK(一个传输类控制符号,用来确认已经正确接收),下一步就是把丢失的数据包重新发送给目的计算机,重复的数据包在接收端会被丢弃,乱序的数据包会被重新排序,这样数据包的可靠性和有序性就得到了保证。

如果我们用TCP来实现实时的数据传输,那么就会有一个问题:只要数据包有错,TCP就必须等待数据包重新发送,不管是什么情况。也就是说,即使最新的数据已经到了,但是仍然无法访问。这些数据包,新到的数据会被放到一个队列里,需要等待丢失的数据包重新发送,等到不丢失之后所有的数据都可以访问了。那么需要等多久才可以重新发送数据包呢?举个例子:如果延迟是125ms,那么最好的情况下,重新发送数据包需要250ms,但是最坏的情况下,需要500ms以上,比如网络拥塞。那就没指望了……

为什么在网络延迟要求极高的情况下不应该使用 TCP

如果FPS(第一人称射击)网络游戏使用TCP,肯定会有问题网络游戏开发,但是网页浏览器,电子邮件,以及大多数应用程序都不会有问题,因为多人网络游戏有实时性的要求。比如:玩家输入人物的位置,重要的不是前一秒发生了什么,而是最新的情况!TCP不考虑这样的要求,它本来就不是为这样的要求而设计的。

这里举一个简单的多人联机游戏的例子,比如射击类游戏,对网络的要求很简单,玩家通过客户端把各个场景(鼠标键盘输入的行走位置)发送到服务器,服务器处理各个场景,处理完用户发送过来的所有场景后返回给客户端,客户端解析响应,渲染最新的场景展示给玩家。

在上面多人游戏的例子中,如果丢失了一个数据包,那么一切都会停止,等待数据包重新发送。客户端会看起来像是在等待接收数据,因此玩家正在操作的任务会看起来像是停滞不前。当重新发送的数据包到来时,你收到了这个过期的数据包,但玩家并不关心过期的数据(在激烈的战斗中,卡住了!1秒,而当他们能够移动时,他们已经死了)

不幸的是,没有办法解决 TCP 的这个问题,这是 TCP 的固有问题,没有办法解决。这就是 TCP 使不可靠、无序的数据包看起来像有序、可靠的数据流的原因。

我不需要可靠、有序的数据流。我们想要的是客户端和服务器之间的延迟尽可能低,而不必等待丢失的数据包重新发送。

所以,这就是为什么当需要实时数据时我们不使用 TCP 的原因。

那么为什么不同时使用 UDP 和 TCP 呢?

比如玩家输入的实时游戏数据、状态变化,这些只和最新数据有关(这些数据强调实时性)。但其他数据,比如从一台计算机发送到另一台计算机的一系列指令(交易请求、聊天?),可靠有序的传输还是很重要的!

因此,使用 UDP 来传输用户输入和状态,使用 TCP 来传输可靠、有序的数据传输,似乎是个好主意。但问题是 TCP 和 UDP 都建立在“IP”协议之上,因此协议之间存在差异。协议之间的交互相当复杂,涉及 TCP 性能、可靠性和流量控制。简而言之,TCP 会导致 UDP 丢包,请参考这篇论文

另外,UDP和TCP混合使用实现起来非常复杂和痛苦。(这一段我就不翻译了,总之:不要混合使用UDP和TCP,很容易对传输的数据失去控制)

“总结”

我的建议是不要用UDP,但是做游戏还是用UDP协议吧,不要把TCP和UDP混用,你应该学习TCP实现的一些技巧,然后把这些技巧用在UDP上,这样就能实现一个适合你需求的协议(借鉴TCP的实现,改进UDP的功能,满足你的需求)。

本系列将会讲到:如何在UDP上建立虚拟连接(因为UDP本身没有连接的概念)、如何让UDP实现可靠性、流控、非阻塞。

參考

点击

立即阅读相关文章

核心设计分析

......

近期热门文章

分享:

支付宝

微信