tcp为我们做了什么事情?

总得来说,tcp做了这几件事:

  1. 通过序列号和基于确认的超时重传机制,为上层提供了可靠的字节流服务;
  2. 通过滑动窗口、拥塞窗口提供了流量控制;
  3. 默认情况下,为了有效利用带宽,tcp的报文一次会尽量携带更多的数据。但与此同时,为了避免IP层的分片,又不会发送超过MTU大小的数据包。

udp为我们做了什么事情?

首先应该清楚的是,一个udp数据包仅仅是在IP数据包之上加了一个udp协议头。这个协议头十分精简,仅有的四个字段是:目的端口号、源端口号、数据包长度、校验和。通过sendto这个syscall发送一个udp数据包时,实际上就是包装成一个IP数据包然后直接发送出去。这里并不会像tcp那样在本地缓存数据,直到达到一个长度限制后再发送出去。另外由于有检验和,所以我们不需要在udp之上再做校验的事情了。

一个可靠的udp协议

在清楚tcp做了什么事后,我们就有了实现一个可靠udp的思路了。首先需要说明的是,在udp之上实现一个tcp是没有意义的。实现一个可靠的udp协议往往是,根据业务需要,牺牲掉一部分的tcp的功能,来换取更高的性能。但是,在动手造轮子之前,我们应该分清的是,哪些功能是tcp已经为我们实现了的,而哪些功能是要通过自己定制一个可靠udp来实现的。比如说,上述的第3条,这个特性有时候会造成tcp的延迟性,但是通常可以通过设置TCP_NODELAY来解决。

实现一个最基础的可靠udp通讯协议,我们只需要提供一个重传机制即可。在这我实现了一个简单的可靠udp协议,这个协议为每一个发送出去的udp数据包分配一个包id,每次接收方收到一个数据包时,都要回应发送方一个ack对应这个包id。协议通过这种确认机制来保证接收方能收到发送方发出的udp数据包,在发出的时候,发送方应该设置一个计时器,超时的话会重传数据包。

这个协议做的就是这么多,它仅能确保接收方能收到发送方的udp包,并没有做其它的事情。

具体来说它没做这些事情:

  1. 它没有保证包的有序性。发送方连续发送几个udp数据包,接收方可以以任何顺序收到这几个数据包。如果想要做到有序性,必须由应用层来完成。
  2. 它没做流量控制。发送方连续大量发送数据包会导致网络性能变差,丢包次数增大。
  3. 它没对数据包大小做控制。为了避免IP层对数据包进行分片,应用层应该要保证每个数据包的大小不超过MTU。如果这个数据包会经过广域网(一般情况下)这个值应该不超过576。考虑到IP头的20字节,udp头的8个字节,以及这个协议头的字节。最好每次发送的数据大小在512以内。

这个协议这么简陋,那要它何用?

首先,对于互相独立的数据包,没有必要保证包的有序性。以游戏开发为例子,玩家在客户端发起一个开宝箱请求的同时,又迅速的切换界面,打开了角色面板又发起一个查看角色数据的请求。保证这两个请求的先后顺序真的有意义吗?

其次,对于大部分的小数据包,没有必要考虑IP分片。玩家发起一个开宝箱的请求,可能整个数据包的内容就是一个请求id和一个宝箱id。假设这两个id都是64位,也就是16字节,整个IP包的大小并不足以让IP层进行分片处理。如果你的系统绝大部分都是这种小包,或许你能不考虑IP分片。

再来谈谈tcp的字节流服务。正如大家所知道的,tcp是提供可靠的字节流服务的。但是,一般情况下我们真的需要传输层提供这种服务吗?以游戏服务器为例子,在用tcp与客户端进行网络通讯上,我们现在的做法是在字节流上对请求、响应进行封包解包,也就是在字节流之上,模拟一个数据包的传输。并且我相信很多其它游戏也是这么做的。数据在IP层上本来就是通过包的形式进行传输,tcp对此做了抽象实现了字节流,应用层又在字节流之上模拟出数据包。是否可以用udp去减少这些不必要的抽象呢?或许我们仅需要的是一个有序可靠的数据包服务,又或许我们连有序性都不需要。。。

总结

最后,我想说的是并没有一套解决方案能适用与所有情况。tcp作为传输层给出的是一套通用性的解决方案,并致力于满足大多数的需求,但是总会有其不适合的地方。这个时候我们就可以使用udp来自己定制一套时候自己业务的协议。这套协议不一定是很复杂,其实实现一个可靠的udp也并不是很复杂,关键要看你需要协议提供什么功能!

参考资料 

  1. 可靠UDP传输。地址:http://blog.codingnow.com/2016/03/reliable_udp.html
  2. KCP。地址:https://github.com/skywind3000/kcp

可靠UDP的更多相关文章

  1. Unity跨平台C/CPP动态库编译---可靠UDP网络库kcp基于CMake的各平台构建实践

    1.为什么需要动态库 a)提供原生代码(native code)的支持,也叫原生插件,但是我实践的是c/cpp跨平台动态库,这里不具体涉及安卓平台java库和ios平台的objectc库构建. b)某 ...

  2. TCP与UDP,可靠UDP如何实现

    两种协议都是传输层协议,为应用层提供信息载体. TCP协议是基于连接的字节流的可靠协议,有三次握手,四次挥手,超时重传,流量控制(滑动窗口),拥塞控制和差错控制,也正因为有可靠性的保证和控制手段,所以 ...

  3. 可靠UDP设计

    最近加入了一个用帧同步的项目,帧同步方案对网络有着极大的影响,于是采用了RUDP(可靠UDP),那么为什么要摒弃TCP,而费尽心思去采用UDP呢?要搞明白这个问题,首先要了解TCP和UDP的区别 , ...

  4. 开源基于asio的网络通信框架asio2,支持TCP,UDP,HTTP,RPC,SSL,跨平台,支持可靠UDP,支持TCP自动拆包,TCP数据报模式等

    开源基于asio的网络通信框架asio2,支持TCP,UDP,HTTP,RPC,SSL,跨平台,支持可靠UDP,支持TCP自动拆包,TCP数据报模式等 C++开发网络通信程序时用asio是个不错的选择 ...

  5. 可靠UDP,KCP协议快在哪?

    WeTest 导读 云真机已经支持手机端的画面投影.云真机实时操作,对延迟的要求比远程视频对话的要求更高(100ms以内).在无线网络下,如何更实时.更可靠的传输视频流就成了一个挑战.通过websoc ...

  6. 云风的BLOG❳可靠 UDP 传输

    http://mp.weixin.qq.com/s?__biz=MzA3NjYxOTA0MQ==&mid=405432715&idx=1&sn=2e40ceafd4b298e1 ...

  7. UDP可靠传输那些事

    有空来论坛走走,发现讨论udp可靠传输又热了起来,有人认为udp高效率,有人认为udp丢包重传机制容易控制,还有朋友搞极限测试,当然也有人推销自己的东西,这里写一点我个人的看法. udp可靠传输其实非 ...

  8. UDP如何实现可靠传输

    概述 UDP不属于连接协议,具有资源消耗少,处理速度快的优点,所以通常音频,视频和普通数据在传送时,使用UDP较多,因为即使丢失少量的包,也不会对接受结果产生较大的影响. 传输层无法保证数据的可靠传输 ...

  9. 【Network】高性能 UDP 服务应该怎么搞?

    参考资料: Netty系列之Netty高性能之道 C++高性能服务框架revover:rudp总体介绍(可靠UDP传输) - zerok的专栏 - 博客频道 - CSDN.NET 高性能异步Socke ...

随机推荐

  1. [osx] android studio下修改avd的hosts文件

    1. 启动avd 安装/启动avd就不说啦,可以直接在android studio里面操作的 2. 进入adb目录 当然是打开终端来敲命令啦. cd /Users/birdylee/Library/A ...

  2. Alwayson 与 mirror

    --将主副本改为同步模式和自动故障转移,将其中一个辅助副本改为同步辅助副本和自动故障转移 USE [master]GOALTER AVAILABILITY GROUP [TESTDB5AG]MODIF ...

  3. HTTPS原理详解

         HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版.即HTT ...

  4. 跳转 linQ

    <a href="../Book/BookDetail?book_id=@book.book_id">@book.book_name</a> query + ...

  5. 170105、MySQL 性能优化的最佳 20+ 条经验

    今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我 们程序员需要去关注的事情.当我们去设计数据库表结构,对操作数 ...

  6. svn版本搭建

    安装步骤如下: 1.yum install subversion   2.输入rpm -ql subversion查看安装位置,如下图:   我们知道svn在bin目录下生成了几个二进制文件. 输入 ...

  7. C++模拟实现JDK中的ArrayList和LinkedList

    Java实现ArrayList和LinkedList的方式采用的是数组和链表.以下是用C++代码的模拟: 声明Collection接口: #ifndef COLLECTION_H_ #define C ...

  8. 用python在linux下获取网卡地址

    使用一个第三方库,名为psutil 代码如下: #!/usr/bin/python # -*- coding:utf-8 -*- import psutil def get_netcard(): ne ...

  9. js数组中indexOf/filter/forEach/map/reduce详解

    今天在网上看到一篇帖子,如题: 出处:前端开发博客 (http://caibaojian.com/5-array-methods.html) 在ES5中一共有9个Array方法,分别是: Array. ...

  10. 前端使用js读取文件

    最近同时问我js能不能读取本地文件: 想起以前看到js读取本地文件的文章,然后自己写了个demo. ps:这有点想Java的IO流,但是又有差别. 首先我们定义一个input标签type=" ...