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();
}
}
}
接收消息:
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