在2017年10月深圳 Cocos 沙龙上,有幸结识了社区中大名顶顶的Colin,Shawn在论坛上第一次看到Colin的团队用CocosCreator制作的《热血暗黑》时就被深深地震撼到了!更为重要的是,Colin将他的技术心得和宝贵开发经验写成文字,每一篇分享都是满满的干货,而且幸运的是Shawn得到Colin的授权许可,与你一起欣赏一起成长!
从今日起 Colin 大神准备长期驻扎「Creator游戏开发社区」,为大家分享最为硬核的Linux C++ 游戏服务器开发相关知识与经验。
TCP/IP基础知识
网络由下向上可以分成:
数据链路层(Data-link layer)
网络层(Network layer)
传输层(Transport layer)
应用层(Application layer)
数据链路层
这一层和设备驱动以及网卡相关,它涉及到通过网络中的物理链路传输数据的技术。对于程序员来说通常可以忽略这一层,但有几个概念要了解:
Frame: 数据链路层将数据报封装成称为帧的单元。
MTU:最大传输单位,即在一帧中允许传输的最大有效数据(payload),
帧头信息+有效数据+帧尾信息
就形成1帧的全部内容,如果有效数据超过这个大小将被分割成多个单元。不同的数据链路层有不同的MTU。path MTU:当传输跨过多个不同的网络时,每条链路可能有不同大小的MTU,在整个网络路径上,最小的MTU被称为路径MTU。
IP层
IP层负责从源主机和目标主机传输数据包,它主要负责下面的事情:
将IP数据包分割成数据链路层的帧,交给链路层去传输。
通过互联网路由数据。
为上层的传输层提供服务。
一个IP数据报的组成大概是这样的:
[IP数据报] = [IP头] + [IP数据]
[IP头] = 源地址+目标地址+校验信息+...
关于IP协议还有一些要注意的地方:
IP协议是一个无连接和不可靠的协议,它不保证数据包会按序传输,或不会被复制,甚至不能保证数据包全部到达。
由于MTU的存在,IP数据包可能会被分片传输,比如以太网的MTU是1500,远小于IP数据包的最大值(65535),如果IP数据包大于MTU,那么数据链路层会把IP包分片,到目的地后再组装起来,这个过程对上层协议是透明的。
IP包分片传输的过程中,如果发生分片丢失或数据错误,那么整个数据包都将失效。上层协议比如UDP没有重传机制,这可能会加大丢包率,而TCP虽然有重传机制,也会降低传输效率。
TCP实现了path MTU发现算法,并相应的分解传给IP层的数据,这样IP包就不会超过MTU。但UDP没有提供这个机制,所以上层应用要自己控制包的大小,避免超过MTU造成IP碎片化。
IP地址分为IPv4和IPv6,关于IP地址有下面这些要点:
IPv4地址大小是32位,用可读的方式将4个字节用点分开,比如:204.152.189.116。
IP地址组成:网络ID+主机ID。用掩码可以得到各个部分,掩码中的二进制1表示网络ID,0表示主机ID,可读方式如:255.255.255.0,这表示前3个字节为网络ID,第4个字节为主机ID。由于左边的总是网络ID,可以简单表示为:204.152.189.0/24,24表示网络ID占24位。
回环地址:一般是127.0.0.1,主机名为localhost,表示主机自己,发送到这个地址的数据包并不会传输到网络,它只会传回本机。所以这个地址一般用于本机的进程间通讯,或在本机测试服务器程序。
通配符地址:一般是0.0.0.0,用于表示本机任意可用的IP地址,如果主机有多个IP地址,你想客户端通过任意一个IP地址都能连上服务器,那么可以使用这个地址。
IPv6地址大小是128位,用冒号分隔出8部分,每部分2个字节,如:F000:0:0:0:0:0:A:1,中间为0的部分可以用两个冒号省略,如:F000::A:1。
IPv6也有回环地址
::1
和通配符地址::
。一般带有端口的地址表示法,IPv4:
204.152.189.116:80
,IPv6:[F000::A:1]:80
。
传输层
最常用的两个传输层协议是TCP和UDP,传输层增加了端口的概念,它把目标从主机进一步细分到程序,IP地址关注的是网络中的主机,而端口则是关注主机中的程序。
1024以下端口被预留给特殊的应用协议,所以我们自己写的服务端程序一般端口要大于1023,比较常用的预留端口有:
FTP 21
SSH 22
telnet 23
DNS 53
HTTP 80
HTTPS 443
UDP协议
UDP在IP包之上仅仅增加了端口和数据校验,所以它的优点是速度很快,缺点也很明显,就是无连接和不可靠。
上面看到IP包如果超过MTU会产生碎片化,而UDP没有办法知道最小MTU是多少,所以在实践中UDP包的大小要做限制,尽可能不要引起IP包碎片化,许多基于UDP的程序选择512字节来限制UDP包的大小。
TCP协议
TCP协议提供了可靠的,面向连接的,双向的字节流通讯方式:
建立连接:在通讯开始之前,TCP需要在两端建立通讯通道,连接建立起来之后,两端就可以交换数据。
数据分段:数据被分成多个段,每段都包含用于检测传输错误的校验码,一个段用一个IP数据报传输。
数据段重传:当一个TCP数据段正确到达接收端时,接收端向发送端发送一个确认消息,表示数据段已成功到达;如果数据段出错,它会被丢弃,并且接收端不发送确认给发送端。发送端为了处理这种情况,它在发送数据端时会启动一个定时器,时间到达仍没有收到接收端的确认,它会重新发送数据段。
数据段序号:通讯时每一个数据段都有一个序号,下个段的序号等于上个段的序号加上段长度,序号的意义在于:
接收端可以正常处理数据段的顺序。
接收端向发送端发送一个确认消息并带上数据段的序号,这可以让发送端知道这个段已经成功发送。
接收端能正确消除重复的段,这些重复的段可能是TCP重传导致,也可能是IP数据报重复。
流量控制:这是了防止发送端发送数据过快,接收端来不及接收导致丢失数据,TCP使用一个叫滑动窗口的算法来控制流量。接收端在回发确认包时,告诉发送端我这边缓存还有多少可用(窗口大小),发送端根据这个窗口大小调整发包的速度,如果窗口为0,表示接收端缓冲区满了,此时发送端停止发送。停了之后,发送端会启动一个定时器,定时向接收端发探测段,接收端回应窗口大小,这样发送端又可以开始发送数据。
拥塞控制:流量控制与接收方的缓存状态相关,而拥塞控制则与网络的拥堵情况相关,拥塞控制是为了防止发送数据过快使网络拥堵,这可能会导致比较高的丢包率,而TCP有传重机制,会继续向网络重传数据,这又进一步使网络拥堵。拥塞控制主要是结合了两种算法:慢启动和拥塞避免,感兴趣的可以进一步阅读相关文档,这里就略过了。
从上面描述可以看出,TCP的确是一个非常复杂的协议,我们只是简单的描述,也许有些细节不完全正确,如果想更深入了解TCP协议,请阅读TCP/IP详解。