我是靠谱客的博主 灵巧老鼠,最近开发中收集的这篇文章主要介绍Unity/DotNetty中集成Lidgren实现可靠UDP前端Unity: 后端DotNetty:,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

 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

转载于:https://www.cnblogs.com/drashnane/p/6415973.html

最后

以上就是灵巧老鼠为你收集整理的Unity/DotNetty中集成Lidgren实现可靠UDP前端Unity: 后端DotNetty:的全部内容,希望文章能够帮你解决Unity/DotNetty中集成Lidgren实现可靠UDP前端Unity: 后端DotNetty:所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(33)

评论列表共有 0 条评论

立即
投稿
返回
顶部