友情推荐一位大神的一篇文章:抢占式和非抢占式的进程调度,搞调度比搞通讯协议有趣多了!

最近有个项目需要仿照QQ设计一个高性能即时通信应用,其中涉及到传输图片、视频、大块文字的需求。为了实现高性能的同时减少带宽占用,我们参考了QQ通信协议以及Google Protocol Buffers的精简设计理念设计了一套基于UDP的多媒体即时通信机制,达到低带宽、高性能的效果。

QQ通信协议

QQ在即时通信应用中并没有采用XMPP、HTTP等上层的协议进行消息通信,主要是基于效率的考虑:XMPP、HTTP等上层的传输协议为了保证易用性、通用性,包含了太多对应用无用的数据,从带宽、性能方面而言并不是特别优秀。QQ通信的数据结构和Protocol Buffers的机制类型,通过对数据进行序列化反序列化直接进行传输。但是QQ的通信仍然是基于TCP的,而我们项目的场景要求对流量使用有着变态的要求,因此我们尝试基于UDP的方式底层机制来实现。

目标与问题

项目要求传输的数据至少包括如下数据类型:文字、语音、图片、视频、文档、其他等。
由于UDP协议中每个包传输的数据大小有限并且UDP是不可靠的通信传输方式,会导致出现粘包和丢包的情况,设计不当的话其效率将比TCP/HTTP这类传统传输协议低效得多,因此在实现中既要处理好粘包的情况,也要处理好丢包的情况。

数据结构设计

在协议字段上,我们参考了QQ的TCP传输协议,将数据进行分片打包的方式进行流式处理。

序号 字段 类型 描述
1 tag byte[2] 传输协议包头固定值0xbebe,用于区分是否有效数据包、过滤部分恶意攻击
2 counter int 数据包唯一ID,单调递增以帮助接收端识别丢包
3 cmdType byte 消息类型,不同的消息ID对应不同的数据内容(如文字、语音、图片、视频、文档等等)
4 sliceID short int 分片序号,第一片数据序号要加上0xff用于区别首包数据,分片序号采用降序递减的方式发送(避免接收方区分EOF);不分片传输时分片序号为0xffff (节省一个byte)
5 sliceLength short int 待传输数据的整体长度或分片数据长度,取决于是否是首片数据。首片数据时传整体长度是为了接收方预准备缓存以避免多次数据拷贝操作
6 body byte[] 消息体具体内容。分片传输时首片数据的body提供配置信息,依次为:MTU值、丢包处理、超时时间,对应字段为:short int, byte, short int

代码如下:

public abstract class AbstractPacket {/** 协议包头固定值0xbebe,用于区分是否有效数据包,防止恶意攻击 **/protected byte[] tag;/**数据包唯一ID,不断递增,帮助识别丢包 */protected int counter;/**消息类型,不同的消息ID对应不同的数据内容(如文字、语音、图片、视频、文档等)*/protected byte cmdType;   /**消息分片序号,第一片数据的序号要加上0xff用于区别首包数据,序号采用降序递减的方式发送(避免区分EOF) */protected short int sliceID;   /**待传输数据的整体长度或分片数据的长度 */protected short int sliceLength;/**消息体*/protected byte[] body;}

以上数据结构是经过验证的较优结果,能够以有限的字段传递足够的信息(数据本身、丢包、超时、配置信息等)。

数据传输

数据传输有两种情形:

1、普通数据传输

要发送的数据较小时,不需要分片就可直接发送,这是最简单的一种情形。例如要传输一串文本(”QQ”)时,那么数据包的设置将为:tag=0xbebe, counter=<递增ID>,cmdType=<文字消息ID>,sliceID=0xffff, sliceLength=2, body=”QQ’。

2、大数据传输

如果要发送的数据很大,一次UDP发送无法发送完毕,那就需要对数据进行分片。例如要发送的一段大小为10KB的语音数据,那么

首片数据的设置将为:tag=0xbebe, counter=<递增ID>,cmdType=<语音消息ID>,sliceID=0xff+10KB/1024+1=0x10A, sliceLength=5, body=”MTU=1024,Relay=1,Timeout=120″,

第二片数据的设置为:tag=0xbebe, counter=<递增ID+1>,cmdType=<语音消息ID>,sliceID=10KB/1024=0xA, sliceLength=1024, body=”<语音消息前1024个字节>”,

最后一片数据的设置为:tag=0xbebe, counter=<递增ID+11>,cmdType=<语音消息ID>,sliceID=1, sliceLength=1024, body=”<语音消息最后1024个字节>”

丢包处理

由于UDP传输存在不可靠、乱序等问题,数据传输的接收方需要有效的识别丢包并反馈给发送方以请求重传。丢包处理有两种情况:第一种情况,针对不需要分片的数据,只要过了超时时间即可要求发送方重传。

第二种情况是针对分片的大数据,一种最简单的策略(Relay=1)是一旦发现分片数据丢失即要求所有分片数据重新传输,这种方式对接收方而言最简单,但是效率很低。实测发现其效率远低于使用TCP等传统的传输方式。另一种优化的策略(Relay=2)是只要求发送方重传丢失的分片数据,已收到的分片数据不影响,这种策略也是大多数丢包处理方案所采用的的方式。两种方式进行结合也是可以的,主要看分片数量的大小:如果分片数量很少,第一种策略效果没问题;如果分片数量很大,则应采用第二种。

除了以上两种情况,丢包处理还可以进一步进行优化,具体的实现方法可以参考TCP的sliding window的重传方式,但由于实现相对复杂,以后再行实现。

命令回复

数据传输的回复结果理论上可以复用数据发送的流程,但由于命令的回复内容都较短,一般不会出现需要分片的情景,只需要处理丢包的情景,因此可以针对数据发送的流程进行优化来实现命令回复。具体如下所示:

序号 字段 类型 描述
1 tag byte[2] 传输协议包头固定值0xbeef,用于区分是否有效数据包、过滤部分恶意攻击
2 counter int 回复数据包唯一ID,单调递增以帮助识别丢包
3 cmdType byte 消息类型,不同的消息ID对应不同的数据内容(如文字、语音、图片、视频、文档等等)
4 result short int 信息传输的返回码,具体可根据需要随便定义
5 body byte[] 信息传输的成功反馈或错误解释

确认MTU的值

选好MTU的值对于传输效率的影响十分巨大,因此在协商过程中应该把MTU的值设为多少很有讲究。MTU选得过大,则丢包率严重;MTU选得过小,则传输次数太多,影响传输速度(因为重传机制存在)。在我们大量的测试验证中发现,MTU在500字节左右是最合适的。如果网络环境较差,丢包率较高,可以适当减少MTU的值,但不要低于200;如果网络环境较好、丢包率低,可以适当增大MTU,但不要大于1500。

方案效果

虽然我们花了一些时间另辟蹊径地在UDP的基础上设计了这个大数据传输方案,期望得到相对更优的方案。上线后发现整体优化结果不是非常明显,甚至在一些情况下效率反而很低,可能是丢包重传机制没有优化的原因。重新发明轮子果真是一件吃力不讨好的事情。

参考附件:基于UDP实现多媒体即时通信机制方案

基于UDP实现多媒体信息通信机制相关推荐

  1. 虚拟机无法接受组播消息_基于UDP的组播通信

    基于UDP的组播通信 在Java实现基于UDP协议的发送端与接收端通信中,我们可以知道它的一些主要操作: 在发送端:1,创建绑定指定端口的发送接口:DatagramSocket(port) 2,创建绑 ...

  2. Android基于UDP的局域网聊天通信

    代码地址如下: http://www.demodashi.com/demo/12057.html 记得把这几点描述好咯:代码实现过程 + 项目文件结构截图 + 演示效果 1. 开发环境 1.1 开发工 ...

  3. Java 实现基于 UDP 的简单 socket 通信

    UDPServer.java package com.learn;import java.io.IOException; import java.net.DatagramPacket; import ...

  4. LINUX下UDP实现消息镜像通信,linux环境下基于udp socket简单聊天通信

    客户端代码:client.c /* * File: main.c * Author: guanyy * * Created on 20161202 * * 主要实现:客户端和服务端相互通信 */ #i ...

  5. 基于UDP协议的Socket通信

    服务端实现步骤: Step 1:创建DatagramSocket,指定端口号 Step 2:创建DatagramPacket Step 3:接收客户端发送的数据信息 Step 4:读取数据 示例代码: ...

  6. Java实例练习——基于UDP协议的多客户端通信

    昨天学习了UDP协议通信,然后就想着做一个基于UDP的多客户端通信(一对多),但是半天没做出来,今天早上在参考了很多代码以后,修改了自己的代码,然后运行成功,在这里分享以下代码,也说一下自己的认识误区 ...

  7. Java+利用UDP实现简单双机通信

    计算机网络实验报告,给了很多课题,然后自己就对实现双机通信比较感兴趣,然后就希望做一个能够实现双机双机通信的小聊天程序,本来还想用MFC做一个的,然后就在那扣了将近一天的MFC结果就自己做了一个最简单 ...

  8. java: java mina ——基于TCP/IP、UDP/IP协议栈的通信框架

    Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于TCP/IP.UDP/IP协议栈的通信框架(当然,也可以提供JAVA 对象的序列化服务.虚拟机管道通信服务等),M ...

  9. android udp定时发送,Android Socket基于UDP协议通信

    首先我们要知道UDP通信协议是Socket通信的一种实现方式,Socket通信一般有两种通信方式:基于TCP协议.基于UDP协议.这两者的差别和优缺点就不说了,这里主要讲一下基于UDP协议的实现. 基 ...

最新文章

  1. [iOS翻译]《The Swift Programming Language》系列:Welcome to Swift-01
  2. 2018-11-25-今日总结
  3. 如何将hive与mysql连接_hive连接mysql配置
  4. QT计算器功能的实现
  5. jdbc,mybatis,hibernate各自优缺点及区别
  6. 数字图像处理与Python实现笔记
  7. 【Modern OpenGL】前言
  8. python获取路径下所有文件_Python获取路径下所有文件名
  9. android 时间戳 转日期格式,在Android中转换为简单日期格式或Unix时间戳日期?
  10. java 小程序--杨辉三角
  11. 中国顶级程序员有多牛?一个弄哭韩国,一个堪称阿里“扫地僧”
  12. C#笔记 使用自定义事件(含参)
  13. AutoCad 二次开发(一) 开发环境搭建
  14. 如何复制百度文库内容?
  15. 轴承后缀ce和ca_轴承cc和ca与cde4有什么区别
  16. java hook api 实现封包截取_hook 封包 截取WSASend实例
  17. 从人工智能到人机智能
  18. 期货反跟单-千万不要盲目开始反跟单交易
  19. Excel MATCH函数判断两列包含关系
  20. 【翻译】Visual Place Recognition_ A Survey视觉场景识别综述【四】

热门文章

  1. Unity接入AWS S3过程,AWS SDK for Unity 踩坑记录
  2. 光源控制器拨码开关使用说明
  3. 逆向入门学习路线及渠道
  4. Linux SSHFS挂载验证-海思Linux系统
  5. DELL升级bios方式
  6. 电子科技大学计算机调剂要求,2016电子科技大学考研调剂信息发布(3.16更新)
  7. 张泉灵转学了,罗胖是交学费的人!
  8. Photoshop CS6下载及安装教程a
  9. java我的世界1.7.2怎么下载模组,我的世界1.7.2模组大全整合包游戏
  10. [渝粤教育] 中国地质大学 劳动与社会保障法 复习题