本文参考并引用了部分腾讯游戏学院的相关技术文章内容,感谢原作者的分享。
1、前言
以现在主流的即时通讯应用形态来讲,一个完整的即时通讯IM应用其实是即时通信(英文简写:IM=Instant messaging)和实时通信(英文简写:RTC=Real-time communication)2种技术组合在一起的一整套网络通信系统。之所以以IM这个简写代称整个即时通讯软件,其实是历史原因了(因为早期的诸如ICQ这样的即时通讯工具,也就是文字聊天,并没有加入实时音视频这样的实时通信技术),对这个话题有兴趣的可以到网上查一查IM的发展历史。
以微信、QQ这样的完整即时通讯应用来说,回归到工具的本质,它主要包含了两种应用和技术:
1)广义的文字聊天:也就是我最常理解的各种聊天消息的传递,这部分的技术实现就是众所周之的IM通信(即Instant messaging);
2)实时音视频聊天:包括语音电话、视频聊天,这部分的技术实现,从网络通信的角度讲,就是实时通信(即Real-time communication)。
我们回忆一下:早几年前市面上主流的移动端IM——比如微信、QQ、以及现在满屏广告的网易易信、半死不活的小米米聊、已经入土的阿里来往、打擦边球的陌陌等,基本都没有或者很晚才加入实时音视频聊天功能(我们抛开技术因素之外的原因不议),原因不是不想做,而是实时音视频这种实时通信技术确实是有相当的门槛,并不容易做。
所以:对于即时通讯网社区内众多的IM应用开发者来说,实时通信技术如此重要,深入研究和理解实时通信技术的原理、技术实践,对于自已IM产品的开发来说,都是大有裨益的。
本文将尝试从开发者角度:梳理开发网游服务端的网络接入层的过程中面临的各种技术挑战,并针对性地提供相应的实时通信网络接入层解决思路,希望对于即时通讯应用的开发者来说,可以从中得到些许启发。
学习交流:
- 即时通讯开发交流3群:185926912 [推荐]
- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM》
(本文同步发布于:http://www.52im.net/thread-1915-1-1.html)
2、相关文章
《高性能网络编程(一):单台服务器并发TCP连接数到底可以有多少》
《高性能网络编程(二):上一个10年,著名的C10K并发连接问题》
《高性能网络编程(三):下一个10年,是时候考虑C10M并发问题了》
《高性能网络编程(四):从C10K到C10M高性能网络应用的理论探索》
《不为人知的网络编程(七):如何让不可靠的UDP变的可靠?》
《网络编程懒人入门(五):快速理解为什么说UDP有时比TCP更有优势》
《技术扫盲:新一代基于UDP的低延时网络传输层协议——QUIC详解》
《脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手》
《脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?》
3、主流网游的网络通信架构原理
维基百科关于网络游戏的定义:
即通过计算机网络,将专用服务器和用户的客户端设备(手机、PC、游戏主机等)相连,让多名玩家同时联机进行游戏的娱乐形式。
由此可知网络游戏涉及三个角色:
1)客户端;
2)网络;
3)服务器。
从网络架构上来讲,网游可分为:
1)C/S 架构:这个最好理解;
2)P2P架构:特指客户端间直连通信;
3)C/M架构:在实际开发中这是一种C/S和P2P架构的混合体。
典型的网络架构如下图所示:
P2P架构不在本文讨论范围。
C/M架构和C/S架构相似,跟经典的LAMP网站架构类似,一般C/S架构的游戏后台也可划分为如下三层:
1)网络接入层;
2)游戏逻辑层;
3) 数据存储层。
一般C/S架构的游戏后台分层,如下图所示:
网络接入、游戏逻辑、数据存储层各自所面临的问题域及对应技术栈都大为不同,做此划分不仅有助于模块解耦、技术分工、组件复用,也可方便服务的运维部署。本文要讨论的就是这个网络接入层。
4、题外话:该如何理解C/M架构?
可能有人对上节中的C/M架构有疑问,在网游中这个架构到底是怎么用的?
其实,网络游戏中是通过同步机制来保证各个客户端游戏世界的一致性。
主流的网游数据同步机制有两种:
1)状态同步:即由客户端负责将玩家的操作发往中心节点 (服务器或master客户端),由中心节点来负责游戏逻辑计算并将计算结果广播给客户端,再由客户端负责渲染游戏结果
2)帧同步:帧同步的理论基础是游戏逻辑由操作指令驱动,只要操作序列一致,那么游戏结果就应该一致。
也就是说,不管采用哪种数据同步机制,其实都是应用的C/M架构(即Client/Master架构)。
5、网络接入层的作用
网络接入层的主要任务是:
1)建立客户端和后台服务以及客户端之间的信道,;
2)接收来自客户端大量并发请求。
考核该层的主要性能指标是:
1)高吞吐;
2)低延迟。
因而网络接入层开发考验的是开发者高性能网络编程的功底,即解决C10K甚至C10M的能力。
题外话:有关高性能网络编程的C10K、C10M话题,请详细阅读以下文章
《高性能网络编程(一):单台服务器并发TCP连接数到底可以有多少》
《高性能网络编程(二):上一个10年,著名的C10K并发连接问题》
6、网络接入层的通信协议选择
根据OSI的七层网络参考模型,我们可将网游网络也做如下7层划分:
其中4层以下都由操作系统来负责,开发者无需为此操心,在实际的开发过程中开发者首要面临的问题便是传输层是采用TCP还是UDP,下表简要对比了两者的优劣。 综合两者优劣,简单来说除非对延迟有极致要求(例如FPS、MOBA类游戏)需采用UDP外,TCP可应对大部分游戏。
在实际游戏开发中不管是采用TCP还是UDP方式,都较少利用通过Socket编程方式直接进行,一来因为开发工作量大,质量性能难以保证;二来平台兼容性不好(比如H5并没有提供socket编程能力),而是基于更上层的通讯协议比如基于TCP的HTTP、Websocket协议,GRPC,以及基于UDP实现的QUIC,WebRTC协议等。
TCP、UDP协议的简要对比:
有关TCP、UDP协议的详细对比文章,您可简读一下资料:
值得注意的是基于安全性考虑,浏览器标准未提供UDP收发能力,QUIC协议也只在chrome得到了支持,WebRTC也还不是浏览器事实标准且协议初始目的是用于实现点对点的音视频通信,协议内容过于庞杂不容易提炼应用于游戏开发中,因而现阶段H5游戏还只能采用HTTP或Websocket方式通讯。
知识点扫盲:
1)关于QUIC协议:《技术扫盲:新一代基于UDP的低延时网络传输层协议——QUIC详解》;
2)关于WebRTC:《开源实时音视频技术WebRTC的现状》、《简述开源实时音视频技术WebRTC的优缺点》、《访谈WebRTC标准之父:WebRTC的过去、现在和未来》;
3)关于Websocket:《新手入门贴:史上最全Web端即时通讯技术原理详解》、《Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE》、《新手快速入门:WebSocket简明教程》、《WebSocket详解(一):初步认识WebSocket技术》。
另外 ,通讯协议确定后,随后要考虑的便是游戏对象的序列化,序列化主要有基于文本、基于二进制两种,其优劣如下表所示。在开发过程中一般会先采用文本序列化方式,便于前后端开发联调,在游戏正式上线前切换至二进制序列化方式以减少传输流量、提升编解码效率。
游戏对象的主要序列化方式:
关于Protobuf的详细资料,请见:
《强列建议将Protobuf作为你的即时通讯应用数据传输格式》
至于数据安全性问题,为了保护敏感数据安全开发者可以选择安全的https或WSS通讯协议,而对于直接基于TCP协议通讯,可采用先用RSA协商加密秘钥,然后使用对称加密方式将数据加密后发送。
通过以上分析,对于游戏协议类型的选择我们给出有以下准则:
1)弱联网类游戏:诸如休闲、卡牌类游戏可直接HTTP协议,对安全性有要求的话就使用HTTPS;
2)实时性,交互性要求较高:这类游戏一般需要保持长连接,优先选择标准的ws协议(同时使用二进制序列化方式,如Protobuf),如考虑安全性可使用wss协议。而对于提供socket接口的native平台也可使用TCP协议,同时对数据做对称加密增强安全性;
3)实时性要求极高:不仅需要和服务器保持长连接,且延迟和网络抖动都要求极高(如FPS,赛车类游戏),可使用基于UDP的实现流传输协议如QUIC,KCP等。
7、网络接入层的并发模型
为了处理来自客户端的并发请求,服务端有4种常见的并发模型。
7.1)进程:
进程是最早采用的并发模型,进程作为操作资源分配、调度的单位,拥有独立的运行空间。进程并发模型中每个请求由独立的进程来处理,进程一次只能处理一个请求,该模型最大的优点就是简单。如果处理请求的进程由于系统调用而阻塞或进程的时间片用完,抢占式的进程调度器就会暂停旧进程执行,调度执行新的进程,这个过程涉及大开销的上下文切换,进程并发模型的缺点是比较低效。最典型的采用进程模型的服务有Apache。
7.2)线程:
线程并发模型是进程模型的改进,线程从属于进程,是系统更小粒度的执行调度单元。不同请求可由进程内多个并发执行的线程来处理,这些线程由操作系统内核自动调度。线程相对进程的主要优势在于,调度上下文切换开销更小,但由于多个线程共享地址空间,需要额外的线程间互斥、同步机制来保证程序性正确性。典型的采用线程模型的服务有Tomcat。
7.3)IO多路复用:
利用操作系统提供的epoll等IO多路复用机制,能同时监控多个连接上读、写事件, IO多路复用也称事件驱动模型,网络程序执行逻辑可抽象为事件驱动的状态机。 IO多路复用避免了读写阻塞,减少了上下文切换,提升了CPU利用率和系统吞吐率。但IO多路复用它将原本“同步”、线性的处理逻辑变成事件驱动的状态机,处理逻辑分散于大量的事件回调函数。这种异步、非线性的模型,极大地增加了编程难度,如nodeJs的常见的回调地狱问题。典型的采用IO复用模型的服务有Nginx、Netty。
7.4)协程:
协程也称为轻量级线程,是一种协同的、非抢占式的多任务并发模型。 协程运行在用户空间,当遇到阻塞或特定入口时,通过显式调用切换方法主动让出CPU,由任务调度器选取另一个协程执行。
协程切换只是简单地改变执行函数栈,不涉及内核态与用户态转化,也涉及上下文切换,开销远小于进程/线程切换。协程的概念虽早已提出,随着近些年年越来越多的语言(go、 Haskell)内置对协程支持才被开发者所熟知,协程极大的优化了开发者编程体验,在同步、顺序编程风格能快速实现程序逻辑,还拥有IO多路复用异步编程的性能。典型的采用协程模型的服务有openresty(Lua), gevent(Python), golang。
7.5)小结:
以上总结了目前4种常用的并发模型,它们在工作原理、运行效率、编程难度等方面有显著区别,各自有适用场景,在实际使用时应该根据需求仔细评估。在实际开发过程中如果没有可复用的现成网络组件或历史包袱我们建议使用协程并发模式开发网络接入层服务。
附录:更多精华文章,供进一步学习
[1] 网络编程基础资料:
《技术往事:改变世界的TCP/IP协议(珍贵多图、手机慎点)》
《通俗易懂-深入理解TCP协议(下):RTT、滑动窗口、拥塞处理》
《理论联系实际:Wireshark抓包分析TCP 3次握手、4次挥手过程》
《P2P技术详解(一):NAT详解——详细原理、P2P简介》
《P2P技术详解(二):P2P中的NAT穿越(打洞)方案详解》
《P2P技术详解(三):P2P技术之STUN、TURN、ICE详解》
《不为人知的网络编程(一):浅析TCP协议中的疑难杂症(上篇)》
《不为人知的网络编程(二):浅析TCP协议中的疑难杂症(下篇)》
《不为人知的网络编程(三):关闭TCP连接时为什么会TIME_WAIT、CLOSE_WAIT》
《不为人知的网络编程(七):如何让不可靠的UDP变的可靠?》
《网络编程懒人入门(五):快速理解为什么说UDP有时比TCP更有优势》
《网络编程懒人入门(六):史上最通俗的集线器、交换机、路由器功能原理入门》
《网络编程懒人入门(八):手把手教你写基于TCP的Socket长连接》
《现代移动端网络短连接的优化手段总结:请求速度、弱网适应、安全保障》
《移动端IM开发者必读(一):通俗易懂,理解移动网络的“弱”和“慢”》
《移动端IM开发者必读(二):史上最全移动弱网络优化方法总结》
《从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路》
《脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手》
《脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?》
《脑残式网络编程入门(三):HTTP协议必知必会的一些知识》
《脑残式网络编程入门(四):快速理解HTTP/2的服务器推送(Server Push)》
《以网游服务端的网络接入层设计为例,理解实时通信的技术挑战》
>> 更多同类文章 ……
[2] 有关网络通信的格式、协议的选择:
《一个基于Protocol Buffer的Java代码演示》
《全方位评测:Protobuf性能到底有没有比JSON快5倍?》
《详解如何在NodeJS中使用Google的Protobuf》
>> 更多同类文章 ……
[3] 实时音视频开发的其它精华资料:
《即时通讯音视频开发(五):认识主流视频编码技术H.264》
《即时通讯音视频开发(九):实时语音通讯的回音及回音消除概述》
《即时通讯音视频开发(十):实时语音通讯的回音消除技术详解》
《即时通讯音视频开发(十一):实时语音通讯丢包补偿技术详解》
《即时通讯音视频开发(十三):实时视频编码H.264的特点与优势》
《即时通讯音视频开发(十五):聊聊P2P与实时音视频的应用情况》
《即时通讯音视频开发(十六):移动端实时音视频开发的几个建议》
《即时通讯音视频开发(十七):视频编码H.264、VP8的前世今生》
《首次披露:快手是如何做到百万观众同场看直播仍能秒开且不卡顿的?》
《腾讯音视频实验室:使用AI黑科技实现超低码率的高清实时视频聊天》
《七牛云技术分享:使用QUIC协议实现实时视频直播0卡顿!》
《实时视频直播客户端技术盘点:Native、HTML5、WebRTC、微信小程序》
(本文同步发布于:http://www.52im.net/thread-1915-1-1.html)
来源:https://www.cnblogs.com/imstudy/p/9556148.html