• 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

  • 相关阅读:
    Alpha冲刺(1/4)
    团队项目用户验收评审
    beta冲刺 第四天
    beta冲刺 第三天
    Beta冲刺 第二天
    Beta冲刺 第一天
    实验十一 团队作业7---团队项目设计完善&编码测试
    Alpha冲刺四
    Alpha冲刺
    《Miracle_House》团队项目系统设计改进
  • 原文地址:https://www.cnblogs.com/drashnane/p/6415973.html
Copyright © 2020-2023  润新知