Unity/DotNetty中集成Lidgren实现可靠UDP

lidgren有几个优点:

  1. 分channel,每个channel都有单独的消息队列,不互相影响。

  2. 每个消息可以单独选择使用可靠/不可靠传输。

  3. 支持内网穿透

  4. 自带加密算法。

 

前端Unity:

先贴一张前端使用的网络框架图:

 

Lidgren的Github地址:https://github.com/lidgren/lidgren-network-gen3

在Player Setting中,要加上宏定义UNITY

连接:

NetPeerConfiguration config = new NetPeerConfiguration (NetworkConfig.CONNECTION_IDENTIFIER);//参数是一个字符串标识,前后端一致。

config.EnableMessageType (NetIncomingMessageType.ConnectionLatencyUpdated);//监听收发心跳的事件。

m_connection = new NetClient (config);

m_connection.Start ();

m_receiveCallBack = new SendOrPostCallback (OnReceiveMessage);//收发消息的回调

m_connection.RegisterReceivedCallback(m_receiveCallBack, new SynchronizationContext()); 

m_connection.Connect (ip, port);

 断开连接:

m_connection.Disconnect ("");
m_connection.UnregisterReceivedCallback (m_receiveCallBack);

 

发送消息:

NetOutgoingMessage nm = connection.CreateMessage (bytesLength);
nm.Write (bytes, 0, bytesLength);
m_connection.SendMessage (nm, (NetDeliveryMethod)channelType, channel);

 

NetDeliveryMethod有以下几类:

UnReliable不可靠传输,顺序和丢包都不能保证
UnReliableSequence不可靠传输,按顺序接收, 旧包直接丢弃
ReliableUnOrdered可靠传输,不保证顺序,但是不会丢包
ReliableSequence可靠传输,和UnReliableSequence,但是有一个缓冲窗口,超过缓冲范围的包会丢弃
ReliableOrdered可靠传输,不丢包,保证顺序

 接收消息:

void OnReceiveMessage(object state)

        {

            NetIncomingMessage im;

            while ((im = m_connection.ReadMessage()) != null)

            {

                switch (im.MessageType)

                {

                case NetIncomingMessageType.ErrorMessage:

                case NetIncomingMessageType.WarningMessage:

          //处理错误和警告

                    break;

                case NetIncomingMessageType.DebugMessage:

          //debug输出            

              break;

                case NetIncomingMessageType.VerboseDebugMessage:

          //消息重发或丢弃的debug消息

                    break;

                case NetIncomingMessageType.StatusChanged:

          //网络状态变化

                    break;

                case NetIncomingMessageType.Data:

                 //收到消息处理,ReceiveMessages (im.ReadBytes (im.LengthBytes), im.LengthBytes);

                    break;

                case NetIncomingMessageType.ConnectionLatencyUpdated:

          //Lidgren收发心跳包后回调

                    break;

                default:

                    break;

                }

                connection.Recycle(im);

            }

        }

后端DotNetty:

后端是参照TCPServerSocketChannel和TCPSocketChannel改的。

Server的UnSafe可以写一个空类:

class LidgrenUdpServerUnsafeChannel : AbstractUnsafe

        {


            public LidgrenUdpServerUnsafeChannel(AbstractChannel channel) : base(channel)

            {


            }


            public override Task ConnectAsync(EndPoint remoteAddress, EndPoint localAddress)

            {

                return null;

            }


            public void FinishRead()

            {

            }

        }

再实现一个ServerChannel:

public class LidgrenUdpServerChannel : AbstractChannel, IServerChannel

    {

        public const string CONNECTION_IDENTIFIER = "xxxxx";

        NetServer m_server;

        LidgrenChannelConfig m_config;


        public NetServer Server

        {

            get { return m_server; }

        }

        Dictionary<NetConnection, LidgrenUdpChannel> m_connectionList = new Dictionary<NetConnection, LidgrenUdpChannel>();

        public LidgrenUdpServerChannel()

            : base(null)

        {

            m_config = new LidgrenChannelConfig(CONNECTION_IDENTIFIER);

            m_server = new NetServer(m_config.Config);

        }

        protected override IChannelUnsafe NewUnsafe()

        {

            return new LidgrenUdpServerUnsafeChannel(this);

        }

    ...

    }

开始监听:


protected override void DoBind(EndPoint localAddress)

        {

            m_config.Config.Port = ((IPEndPoint)localAddress).Port;

            this.m_server.Start();

            this.m_server.RegisterReceivedCallback(new System.Threading.SendOrPostCallback(OnRead), new System.Threading.SynchronizationContext());


        }

 OnRead类似客户端的OnReceiveMessage:

建立/断开连接:

case NetIncomingMessageType.StatusChanged:

                        NetConnectionStatus ns = (NetConnectionStatus)im.ReadByte();

                        if (ns == NetConnectionStatus.Connected)

                        {

                            LidgrenUdpChannel udpChannel = new LidgrenUdpChannel(this, im.SenderConnection);

                            Pipeline.FireChannelRead(udpChannel);

                            lock (((System.Collections.ICollection)m_connectionList).SyncRoot)

                            {

                                m_connectionList.Add(im.SenderConnection, udpChannel);

                            }

                        }

                        if (ns == NetConnectionStatus.Disconnected)

                        {

                            lock (((System.Collections.ICollection)m_connectionList).SyncRoot)

                            {

                                LidgrenUdpChannel channel = null;

                                if (m_connectionList.TryGetValue(im.SenderConnection, out channel))

                                {

                                    channel.Pipeline.FireChannelInactive();

                                    m_connectionList.Remove(im.SenderConnection);

                                }

                             

                            }

                        }

              break;

接收消息:

case NetIncomingMessageType.Data:

                        LidgrenUdpChannel readChannel = null;

                        lock (((System.Collections.ICollection)m_connectionList).SyncRoot)

                        {

                            if (m_connectionList.TryGetValue(im.SenderConnection, out readChannel))

                            {

                                readChannel.ReadBytes(im.ReadBytes(im.LengthBytes));

                            }

                        }

                      break;

对于每一个客户端,都有一个单独的channel:

    public class LidgrenUdpChannel : AbstractChannel

发送消息:

protected int DoWriteBytes(IByteBuffer buf)

        {

            if (!buf.HasArray)

            {

                throw new NotImplementedException("Only IByteBuffer implementations backed by array are supported.");

            }

            int sent = buf.ReadableBytes;

            NetOutgoingMessage msg = m_parentChannel.Server.CreateMessage();

            msg.Write(buf.Array, buf.ArrayOffset + buf.ReaderIndex, buf.ReadableBytes);

            m_connection.SendMessage(msg, NetDeliveryMethod.ReliableOrdered, 0);


            if (sent > 0)

            {

                buf.SetReaderIndex(buf.ReaderIndex + sent);

            }


            return sent;

        }

        protected override void DoWrite(ChannelOutboundBuffer input)

        {

            while (true)

            {

                object msg = input.Current;

                if (msg == null)

                {

                    // Wrote all messages.

                    break;

                }


                if (msg is IByteBuffer)

                {

                    IByteBuffer buf = (IByteBuffer)msg;

                    int readableBytes = buf.ReadableBytes;

                    if (readableBytes == 0)

                    {

                        input.Remove();

                        continue;

                    }


                    bool done = false;

                    long flushedAmount = 0;


                    int localFlushedAmount = this.DoWriteBytes(buf);

                    flushedAmount += localFlushedAmount;

                    if (!buf.IsReadable())

                    {

                        done = true;

                    }


                    input.Progress(flushedAmount);

                    buf.Release();

                    if (done)

                    {

                        input.Remove();

                    }

                    else

                    {

                        throw new InvalidOperationException();

                    }

                }

                else

                {

                    // Should not reach here.

                    throw new InvalidOperationException();

                }

            }

        }

接收消息:

 

 public void ReadBytes(byte[] bytes){         
   
if (!Open)          
     
return;        
   
this.EventLoop.Execute(()=> { ((LidgrenUdpUnsafe)Unsafe).ReadBytes(bytes); });}

UnSafe中:

public void FinishRead()

            {

                channel.Read();

            }


            public void ReadBytes(byte[] bytes)

            {

                IByteBufferAllocator allocator = channel.Allocator;

                IRecvByteBufAllocatorHandle allocHandle = RecvBufAllocHandle;


                IByteBuffer byteBuf = allocHandle.Allocate(allocator);

                byteBuf.WriteBytes(bytes);

                channel.Pipeline.FireChannelRead(byteBuf);

                channel.Pipeline.FireChannelReadComplete();

                allocHandle.ReadComplete();

            }

Lidgren不支持ipv6,修改方法参照这里https://github.com/SteveProXNA/UnityLidgrenIPv6/tree/master/IPv6

原文:http://www.cnblogs.com/drashnane/p/6415973.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/322568.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

欢乐纪中A组周六赛【2019.4.13】

前言 做A组被虐好惨 成绩 RankRankRank是有算别人的 RankRankRankPersonPersonPersonScoreScoreScoreAAABBBCCC222巨佬WHF巨佬WHF巨佬WHF140140140404040100100100000222巨佬ZZY巨佬ZZY巨佬ZZY140140140404040100100100000555巨佬HJW巨佬HJW巨佬HJW10010010010010010000000014…

面试官问:如果MySQL引起CPU消耗过大,你会怎么优化

转载自 面试官问&#xff1a;如果MySQL引起CPU消耗过大&#xff0c;你会怎么优化 谁在消耗cpu? 用户系统IO等待软硬中断空闲 祸首是谁&#xff1f; 用户 用户空间CPU消耗&#xff0c;各种逻辑运算 正在进行大量tps 函数/排序/类型转化/逻辑IO访问… 用户空间消耗大量cpu&…

【译】使用Jwt身份认证保护 Asp.Net Core Web Api

原文出自Rui Figueiredo的博客&#xff0c;原文链接《Secure a Web Api in ASP.NET Core》摘要&#xff1a;这篇文章阐述了如何使用 Json Web Token (Jwt)方式 来配置身份验证中间件。这种方式十分适合移动App 后端等不使用cookie的后端程序。网络上有许多资源可以教你如何保护…

禁用Cookie后,Session怎么样使用

转载自 禁用Cookie后&#xff0c;Session怎么样使用 在上篇中更多的是在分析通过Session Cookie这一方式&#xff0c;在每次请求时都将 sessionId以Cookie的形式发到服务端&#xff0c;来保持一致。这也是许多人印象中的 Session在浏览器关闭之后就失效这一说法的来源。 其…

ASP.NET MVC使用Oauth2.0实现身份验证

随着软件的不断发展&#xff0c;出现了更多的身份验证使用场景&#xff0c;除了典型的服务器与客户端之间的身份验证外还有&#xff0c;如服务与服务之间的(如微服务架构)、服务器与多种客户端的(如PC、移动、Web等)&#xff0c;甚至还有需要以服务的形式开放给第三方的&#x…

Spring Boot API 接口文档 Swagger 入门

转载自 芋道 Spring Boot API 接口文档 Swagger 入门 摘要: 原创出处 http://www.iocoder.cn/Spring-Boot/Swagger/ 「芋道源码」欢迎转载&#xff0c;保留摘要&#xff0c;谢谢&#xff01; 本文在提供完整代码示例&#xff0c;可见 https://github.com/YunaiV/SpringBoot-Lab…

ICanPay 统一支付网关

https://github.com/hiihellox10/ICanPay 统一支付网关。对原代码优化。支持NET46和NETSTANDARD2_0。支持支付宝&#xff0c;微信&#xff0c;银联支付渠道通过Web&#xff0c;App&#xff0c;Wap&#xff0c;QRCode方式支付。简化订单的创建、查询、退款跟接收网关返回的支付通…

使用 dotnet core 和 Azure PaaS服务进行devOps开发(Web API 实例)

引子这一篇文章将用一个完整的实例&#xff0c;给大家介绍如何基于dotnet core&#xff08;微软.NET的最新版本&#xff0c;支持跨平台&#xff0c;跨设备的应用开发&#xff0c;详情请参考 https://www.microsoft.com/net 开发一个Web API Service&#xff0c;并且利用Azure的…

如何基于Canal 和 Kafka,实现 MySQL 的 Binlog 近实时同步

转载自 如何基于Canal 和 Kafka&#xff0c;实现 MySQL 的 Binlog 近实时同步 近段时间&#xff0c;业务系统架构基本完备&#xff0c;数据层面的建设比较薄弱&#xff0c;因为笔者目前工作重心在于搭建一个小型的数据平台。优先级比较高的一个任务就是需要近实时同步业务系统…

采用Opserver来监控你的ASP.NET项目系列(二、监控SQL Server与Asp.Net项目)

前言之前有过2篇关于如何监控ASP.NET core项目的文章,有兴趣的也可以看看.ASP.NET Core之跨平台的实时性能监控ASP.NET Core之跨平台的实时性能监控(2.健康检查)今天我们主要来介绍一下,如何使用Opserver监控我们的SQL Server 和ASP.NET项目的异常监控监控效果如下:SQL Server的…

Spring Boot 参数校验 Validation 入门

转载自 芋道 Spring Boot 参数校验 Validation 入门 本文在提供完整代码示例&#xff0c;可见 https://github.com/YunaiV/SpringBoot-Labs 的 lab-22 目录。 原创不易&#xff0c;给点个 Star 嘿&#xff0c;一起冲鸭&#xff01; 1. 概述 在想标题的时候&#xff0c;到底应该…

Orchard Core一分钟搭建ASP.NET Core CMS

Orchard Core 是Orchard CMS的ASP.NET Core版本。Orchard Core是全新一代的ASP.NET Core CMS。官方文档介绍&#xff1a;http://orchardcore.readthedocs.io/en/latest/GitHub: https://github.com/OrchardCMS/OrchardCore下面快速开始搭建CMS新建项目打开VS2017 新建一个CMSWe…

面试:你说你精通Java并发,给我讲讲Java并发之J.U.C

转载自 面试&#xff1a;你说你精通Java并发&#xff0c;给我讲讲Java并发之J.U.C J.U.C J.U.C即java.util.concurrent包&#xff0c;为我们提供了很多高性能的并发类&#xff0c;可以说是java并发的核心。 J.U.C和CAS和Unsafe和AQS Concurrent包下所有类底层都是依靠CAS操…

.Net Core配置与自动更新

.Net Core 将之前Web.Config中的配置迁移到了appsettings.json文件中&#xff0c;并使用ConfigurationBuilder来读取这个配置文件。并可设置在配置文件变化以后&#xff0c;自动重新加载&#xff0c;这样可不用重启你的程序。12345var builder new ConfigurationBuilder().Set…

初级Java开发与架构之间的差距不仅仅是开发时间

转载自 初级Java开发与架构之间的差距不仅仅是开发时间 一、基础篇 JVM JVM内存结构 堆、栈、方法区、直接内存、堆和栈区别 Java内存模型 内存可见性、重排序、顺序一致性、volatile、锁、final 垃圾回收 内存分配策略、垃圾收集器&#xff08;G1&#xff09;、GC算法…

ASP.NET Core Web API下事件驱动型架构的实现(一):一个简单的实现

很长一段时间以来&#xff0c;我都在思考如何在ASP.NET Core的框架下&#xff0c;实现一套完整的事件驱动型架构。这个问题看上去有点大&#xff0c;其实主要目标是为了实现一个基于ASP.NET Core的微服务&#xff0c;它能够非常简单地订阅来自于某个渠道的事件消息&#xff0c;…

面试了 N 个候选人后,我总结出这份 Java 面试准备技巧

转载自 面试了 N 个候选人后&#xff0c;我总结出这份 Java 面试准备技巧 目录&#xff1a; 框架是重点&#xff0c;但别让人感觉你只会山寨别人的代码 别只看单机版的框架&#xff0c;分布式也需要了解 对于数据库&#xff0c;别只知道增删改查&#xff0c;得了解性能优化…

Scratc3.0作品转化成exe文件

Scratch 3的作品&#xff08;sb3格式的文件&#xff09;怎么生成可执行exe文件 Scratch 3.0和Scratch 2.0软件相比&#xff0c;界面和内部实现机制有了较大变化。 与以前2.0版本不同&#xff0c;Scratch3.0版本改用H5和JS语言编写&#xff1b;软件界面有较大变化&#xff0c;将…

来腾讯云开发者实验室 学习.NET Core 2.0

腾讯云开发者实验室为开发者提供了一个零门槛的在线实验平台,开发者实验室提供的能力&#xff1a;零门槛扫码即可免费领取实验机器&#xff0c;支持使用自有机器参与&#xff0c;实验完成后支持保留实验成果&#xff1b;在线 WEB IDE 支持 shell 命令操作&#xff0c;支持机器文…

Redis面试题(2020最新版)

转载自 Redis面试题&#xff08;2020最新版&#xff09; 概述 什么是Redis Redis(Remote Dictionary Server) 是一个使用 C 语言编写的&#xff0c;开源的&#xff08;BSD许可&#xff09;高性能非关系型&#xff08;NoSQL&#xff09;的键值对数据库。 Redis 可以存储键和五…