Unity/DotNetty中集成Lidgren实现可靠UDP
lidgren有幾個優點:
分channel,每個channel都有單獨的消息隊列,不互相影響。
每個消息可以單獨選擇使用可靠/不可靠傳輸。
支持內網穿透
自帶加密算法。
?
前端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
總結
以上是生活随笔為你收集整理的Unity/DotNetty中集成Lidgren实现可靠UDP的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Visual Studio的语言服务器协
- 下一篇: ICanPay 统一支付网关