####UDP协议特色:c#
1:不保证报文必定到达服务器
2:没有链接的概念网络
3:不保证报文按顺序到达设计
4:发送的是整个包,而不是数据流code
####UDP适合传输的数据对象
对于网络游戏中,玩家的坐标位置和朝向的同步,可使用UDP协议来进行,由于坐标和朝向只须要同步最新的值,中间移动过程,并非很是重要。队列
####实现游戏
结合使用TCP和UDP两个协议,能够比较容易实现以上应用。路由
首先在客户端和服务器之间创建一条可靠的TCP链接字符串
由于UDP是无链接的所以,须要实现创建一条UDP链接的协议流程。
接着使用三段握手的方式,创建UDP链接,若是没法创建UDP链接,则游戏运行时应该使用TCP链接进行数据发送。
三段握手实现:首先客户端发送 请求,例如BeginUDP 到服务端监听的端口上,服务端根据收到的客户端链接,向客户端发送EndUDP, 客户端若是收到了EndUDP 协议,则客户端使用TCP通道发送一个UDPSuccess 报文给服务器,由于TCP可靠,所以服务器是必定会受到这个UDPSuccess 报文的。
经过使用三段握手,以及TCP协议,就避免了半链接问题,即客户端有到服务器的UDP链接,而服务器没有到客户端的UDP链接,或者服务器有到客户端的UDP链接,而客户端没有创建链接成功。
若是创建链接失败,则游戏继续使用TCP进行正常的数据通讯。
####须要注意一点
有些路由器限制了UDP链接的创建,客户端A在路由器和交换机 NAT内部,而服务器B在公网上,要创建UDP链接就须要作一个隧道,而有些路由限制,B若是想向A发送报文,则B必须使用A链接本身的端口发送,不然B的报文没法到达A, 这个称为 对称路由, 也就是B不能换个端口,而后向A发送UDP报文,这样报文是没法到达的。
####客户端网络和服务器网络设计
这里使用c#语言来实现,c#中有一个UDPClient类能够用于实现UDP相关功能
服务器上对于每一个客户端的UDP链接有个 UDPAgent类进行管理
服务器上只有一个UDPClient 对象,这个对象负责全部的报文的接受和发送
每次服务器想要向客户端发送报文,都须要将报文先存入报文队列,接着经过UDPClient 一个一个发送出去
每次服务器接收到新的报文,根据报文来源IP和端口,将其分发到不一样的UDPAgent中进行处理
客户端握手协议实现: 发送 BeginUDP 等待服务器发送EndUDP, 若是超时,则UDP链接创建失败,网络通讯将只使用TCP处理 收到EndUDP,以TCP协议发送 UDPSuccess 给服务器
服务端握手协议实现: 收到客户端的BeginUDP 报文 以UDP协议发送EndUDP 报文 收到TCP 发送的UDPSuccess报文
报文除了有BeginUDP字符串以外,还有服务器分配的玩家的Id信息,经过这个ID信息来标识不一样的玩家,而ID 是经过TCP来初始化的。
####移动报文协议
在移动报文中有一个FrameID 用于标识每一个UDP报文的编号,这个FrameID是byte类型
UDP无序,不可靠,如何保证服务器只使用最新的移动报文,而对旧的移动报文直接丢弃掉呢? 每次收到新的报文,和以前收到的最大的FrameID 比较,若是比之大,则是一个最新的位置信息报文,对齐进行处理。 可是FrameID在255以后就会变为0,如何解决整数byte范围有限的问题呢? 1:简单方案可使用一个更大的数据类型,例如long
2:第二个方案:将0-255分红两部分,0-127 和 128-255,每次进入新的一段,须要使用TCP协议来通知服务器和客户端, FrameId区间发生了变化,再收到这个可靠地区间变化协议的时候,强制修改上次收到的最大的FrameID这个字段lastMaxFrameID
那么移动报文处理流程以下:
1:客户端以当前FrameID 发送位置报文,若是FrameID==0 或者FrameID==128,则使用TCP协议,不然使用UDP协议, 报文中须要标记该报文是否为可靠报文
2:服务器接收到报文,若是标记了可靠,则强制修改服务器的LastFrameID 为当前报文的FrameID 服务器将移动报文广播出去
服务器在广播的时候,若是发现当前为可靠报文,则标记一下当前报文的可靠性,将其以TCP广播,不然以UDP广播给全部客户端
3: 客户端收到服务器的报文,若是为可靠则强制修改,不然抛弃掉旧报文,只处理新报文
之因此要切割成两段,是由于,UDP自己不可靠,可能报文从240 到255都丢失了,255到10也丢失了,这时候报文 11到来,须要处理这种状况