Unity/DotNetty中集成Lidgren实现可靠UDP
lidgren有幾個(gè)優(yōu)點(diǎn):
分channel,每個(gè)channel都有單獨(dú)的消息隊(duì)列,不互相影響。
每個(gè)消息可以單獨(dú)選擇使用可靠/不可靠傳輸。
支持內(nèi)網(wǎng)穿透
自帶加密算法。
?
前端Unity:
先貼一張前端使用的網(wǎng)絡(luò)框架圖:
?
Lidgren的Github地址:https://github.com/lidgren/lidgren-network-gen3
在Player Setting中,要加上宏定義UNITY
連接:
NetPeerConfiguration config = new NetPeerConfiguration (NetworkConfig.CONNECTION_IDENTIFIER);//參數(shù)是一個(gè)字符串標(biāo)識(shí),前后端一致。
config.EnableMessageType (NetIncomingMessageType.ConnectionLatencyUpdated);//監(jiān)聽(tīng)收發(fā)心跳的事件。
m_connection = new NetClient (config);
m_connection.Start ();
m_receiveCallBack = new SendOrPostCallback (OnReceiveMessage);//收發(fā)消息的回調(diào)
m_connection.RegisterReceivedCallback(m_receiveCallBack, new SynchronizationContext());?
m_connection.Connect (ip, port);
?斷開(kāi)連接:
m_connection.Disconnect (""); m_connection.UnregisterReceivedCallback (m_receiveCallBack);?
發(fā)送消息:
NetOutgoingMessage nm = connection.CreateMessage (bytesLength); nm.Write (bytes, 0, bytesLength); m_connection.SendMessage (nm, (NetDeliveryMethod)channelType, channel);?
NetDeliveryMethod有以下幾類:
| UnReliable | 不可靠傳輸,順序和丟包都不能保證 |
| UnReliableSequence | 不可靠傳輸,按順序接收, 舊包直接丟棄 |
| ReliableUnOrdered | 可靠傳輸,不保證順序,但是不會(huì)丟包 |
| ReliableSequence | 可靠傳輸,和UnReliableSequence,但是有一個(gè)緩沖窗口,超過(guò)緩沖范圍的包會(huì)丟棄 |
| ReliableOrdered | 可靠傳輸,不丟包,保證順序 |
?接收消息:
void OnReceiveMessage(object state)
? ? ? ? {
? ? ? ? ? ? NetIncomingMessage im;
? ? ? ? ? ? while ((im = m_connection.ReadMessage()) != null)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? switch (im.MessageType)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? case NetIncomingMessageType.ErrorMessage:
? ? ? ? ? ? ? ? case NetIncomingMessageType.WarningMessage:
//處理錯(cuò)誤和警告
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case NetIncomingMessageType.DebugMessage:
//debug輸出? ? ? ? ? ??
? ? ? break;
? ? ? ? ? ? ? ? case NetIncomingMessageType.VerboseDebugMessage:
//消息重發(fā)或丟棄的debug消息
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case NetIncomingMessageType.StatusChanged:
//網(wǎng)絡(luò)狀態(tài)變化
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case NetIncomingMessageType.Data:
? ? ? ? ? ? ? ? ?//收到消息處理,ReceiveMessages (im.ReadBytes (im.LengthBytes), im.LengthBytes);
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case NetIncomingMessageType.ConnectionLatencyUpdated:
//Lidgren收發(fā)心跳包后回調(diào)
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? default:
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? connection.Recycle(im);
? ? ? ? ? ? }
? ? ? ? }
后端DotNetty:
后端是參照TCPServerSocketChannel和TCPSocketChannel改的。
Server的UnSafe可以寫一個(gè)空類:
class LidgrenUdpServerUnsafeChannel : AbstractUnsafe
? ? ? ? {
? ? ? ? ? ? public LidgrenUdpServerUnsafeChannel(AbstractChannel channel) : base(channel)
? ? ? ? ? ? {
? ? ? ? ? ? }
? ? ? ? ? ? public override Task ConnectAsync(EndPoint remoteAddress, EndPoint localAddress)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? return null;
? ? ? ? ? ? }
? ? ? ? ? ? public void FinishRead()
? ? ? ? ? ? {
? ? ? ? ? ? }
? ? ? ? }
再實(shí)現(xiàn)一個(gè)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);
? ? ? ? }
? ? ...
? ? }
開(kāi)始監(jiān)聽(tīng):
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:
建立/斷開(kāi)連接:
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;
對(duì)于每一個(gè)客戶端,都有一個(gè)單獨(dú)的channel:
public class LidgrenUdpChannel : AbstractChannel發(fā)送消息:
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社區(qū)新聞,深度好文,歡迎訪問(wèn)公眾號(hào)文章匯總 http://www.csharpkit.com
總結(jié)
以上是生活随笔為你收集整理的Unity/DotNetty中集成Lidgren实现可靠UDP的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Visual Studio的语言服务器协
- 下一篇: 【译】使用Jwt身份认证保护 Asp.N