Unity/DotNetty中集成Lidgren实现可靠UDP
2017-12-19

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();

                }

            }

        }

接收消息:

 

   ReadBytes( (!.EventLoop.Execute(()=>
        }

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 

更多推荐