Out with MagicOnion, in with LiteNetLib
Removed MagicOnion Added LiteNetLib Created client and server abstractions Fixed serialization bugs
This commit is contained in:
parent
433a7b4ae4
commit
e96146b7bb
869 changed files with 59401 additions and 16812 deletions
339
Assets/Scripts/ServerShared/NIH/LiteNetLib/NetSocket.cs
Normal file
339
Assets/Scripts/ServerShared/NIH/LiteNetLib/NetSocket.cs
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
#if UNITY_5_3_OR_NEWER
|
||||
#define UNITY
|
||||
#endif
|
||||
#if NETSTANDARD || NETCOREAPP
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
|
||||
namespace LiteNetLib
|
||||
{
|
||||
internal interface INetSocketListener
|
||||
{
|
||||
void OnMessageReceived(byte[] data, int length, SocketError errorCode, IPEndPoint remoteEndPoint);
|
||||
}
|
||||
|
||||
internal sealed class NetSocket
|
||||
{
|
||||
public const int ReceivePollingTime = 500000; //0.5 second
|
||||
private Socket _udpSocketv4;
|
||||
private Socket _udpSocketv6;
|
||||
private Thread _threadv4;
|
||||
private Thread _threadv6;
|
||||
private volatile bool _running;
|
||||
private readonly INetSocketListener _listener;
|
||||
private const int SioUdpConnreset = -1744830452; //SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12
|
||||
private static readonly IPAddress MulticastAddressV6 = IPAddress.Parse("FF02:0:0:0:0:0:0:1");
|
||||
internal static readonly bool IPv6Support;
|
||||
|
||||
public int LocalPort { get; private set; }
|
||||
|
||||
public short Ttl
|
||||
{
|
||||
get { return _udpSocketv4.Ttl; }
|
||||
set { _udpSocketv4.Ttl = value; }
|
||||
}
|
||||
|
||||
static NetSocket()
|
||||
{
|
||||
#if DISABLE_IPV6 || (!UNITY_EDITOR && ENABLE_IL2CPP && !UNITY_2018_3_OR_NEWER)
|
||||
IPv6Support = false;
|
||||
#elif !UNITY_2019_1_OR_NEWER && !UNITY_2018_4_OR_NEWER && (!UNITY_EDITOR && ENABLE_IL2CPP && UNITY_2018_3_OR_NEWER)
|
||||
string version = UnityEngine.Application.unityVersion;
|
||||
IPv6Support = Socket.OSSupportsIPv6 && int.Parse(version.Remove(version.IndexOf('f')).Split('.')[2]) >= 6;
|
||||
#elif UNITY_2018_2_OR_NEWER
|
||||
IPv6Support = Socket.OSSupportsIPv6;
|
||||
#elif UNITY
|
||||
#pragma warning disable 618
|
||||
IPv6Support = Socket.SupportsIPv6;
|
||||
#pragma warning restore 618
|
||||
#else
|
||||
IPv6Support = Socket.OSSupportsIPv6;
|
||||
#endif
|
||||
}
|
||||
|
||||
public NetSocket(INetSocketListener listener)
|
||||
{
|
||||
_listener = listener;
|
||||
}
|
||||
|
||||
private void ReceiveLogic(object state)
|
||||
{
|
||||
Socket socket = (Socket)state;
|
||||
EndPoint bufferEndPoint = new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0);
|
||||
byte[] receiveBuffer = new byte[NetConstants.MaxPacketSize];
|
||||
|
||||
while (_running)
|
||||
{
|
||||
int result;
|
||||
|
||||
//Reading data
|
||||
try
|
||||
{
|
||||
if (socket.Available == 0 && !socket.Poll(ReceivePollingTime, SelectMode.SelectRead))
|
||||
continue;
|
||||
result = socket.ReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None,
|
||||
ref bufferEndPoint);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
switch (ex.SocketErrorCode)
|
||||
{
|
||||
case SocketError.Interrupted:
|
||||
case SocketError.NotSocket:
|
||||
return;
|
||||
case SocketError.ConnectionReset:
|
||||
case SocketError.MessageSize:
|
||||
case SocketError.TimedOut:
|
||||
NetDebug.Write(NetLogLevel.Trace, "[R]Ignored error: {0} - {1}",
|
||||
(int) ex.SocketErrorCode, ex.ToString());
|
||||
break;
|
||||
default:
|
||||
NetDebug.WriteError("[R]Error code: {0} - {1}", (int) ex.SocketErrorCode,
|
||||
ex.ToString());
|
||||
_listener.OnMessageReceived(null, 0, ex.SocketErrorCode, (IPEndPoint) bufferEndPoint);
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//All ok!
|
||||
NetDebug.Write(NetLogLevel.Trace, "[R]Received data from {0}, result: {1}", bufferEndPoint.ToString(), result);
|
||||
_listener.OnMessageReceived(receiveBuffer, result, 0, (IPEndPoint)bufferEndPoint);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Bind(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool reuseAddress, bool ipv6)
|
||||
{
|
||||
_udpSocketv4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
if (!BindSocket(_udpSocketv4, new IPEndPoint(addressIPv4, port), reuseAddress))
|
||||
return false;
|
||||
LocalPort = ((IPEndPoint) _udpSocketv4.LocalEndPoint).Port;
|
||||
_running = true;
|
||||
_threadv4 = new Thread(ReceiveLogic);
|
||||
_threadv4.Name = "SocketThreadv4(" + LocalPort + ")";
|
||||
_threadv4.IsBackground = true;
|
||||
_threadv4.Start(_udpSocketv4);
|
||||
|
||||
//Check IPv6 support
|
||||
if (!IPv6Support || !ipv6)
|
||||
return true;
|
||||
|
||||
_udpSocketv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
|
||||
//Use one port for two sockets
|
||||
if (BindSocket(_udpSocketv6, new IPEndPoint(addressIPv6, LocalPort), reuseAddress))
|
||||
{
|
||||
try
|
||||
{
|
||||
#if !UNITY
|
||||
_udpSocketv6.SetSocketOption(
|
||||
SocketOptionLevel.IPv6,
|
||||
SocketOptionName.AddMembership,
|
||||
new IPv6MulticastOption(MulticastAddressV6));
|
||||
#endif
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
// Unity3d throws exception - ignored
|
||||
}
|
||||
|
||||
_threadv6 = new Thread(ReceiveLogic);
|
||||
_threadv6.Name = "SocketThreadv6(" + LocalPort + ")";
|
||||
_threadv6.IsBackground = true;
|
||||
_threadv6.Start(_udpSocketv6);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool BindSocket(Socket socket, IPEndPoint ep, bool reuseAddress)
|
||||
{
|
||||
//Setup socket
|
||||
socket.ReceiveTimeout = 500;
|
||||
socket.SendTimeout = 500;
|
||||
socket.ReceiveBufferSize = NetConstants.SocketBufferSize;
|
||||
socket.SendBufferSize = NetConstants.SocketBufferSize;
|
||||
#if !UNITY || UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
|
||||
#if NETSTANDARD || NETCOREAPP
|
||||
if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
#endif
|
||||
try
|
||||
{
|
||||
socket.IOControl(SioUdpConnreset, new byte[] {0}, null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
//ignored
|
||||
}
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
socket.ExclusiveAddressUse = !reuseAddress;
|
||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, reuseAddress);
|
||||
}
|
||||
catch
|
||||
{
|
||||
//Unity with IL2CPP throws an exception here, it doesn't matter in most cases so just ignore it
|
||||
}
|
||||
if (socket.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
socket.Ttl = NetConstants.SocketTTL;
|
||||
|
||||
#if NETSTANDARD || NETCOREAPP
|
||||
if(!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
#endif
|
||||
try { socket.DontFragment = true; }
|
||||
catch (SocketException e)
|
||||
{
|
||||
NetDebug.WriteError("[B]DontFragment error: {0}", e.SocketErrorCode);
|
||||
}
|
||||
|
||||
try { socket.EnableBroadcast = true; }
|
||||
catch (SocketException e)
|
||||
{
|
||||
NetDebug.WriteError("[B]Broadcast error: {0}", e.SocketErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
//Bind
|
||||
try
|
||||
{
|
||||
socket.Bind(ep);
|
||||
NetDebug.Write(NetLogLevel.Trace, "[B]Successfully binded to port: {0}", ((IPEndPoint)socket.LocalEndPoint).Port);
|
||||
}
|
||||
catch (SocketException bindException)
|
||||
{
|
||||
switch (bindException.SocketErrorCode)
|
||||
{
|
||||
//IPv6 bind fix
|
||||
case SocketError.AddressAlreadyInUse:
|
||||
if (socket.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, true);
|
||||
socket.Bind(ep);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
NetDebug.WriteError("[B]Bind exception: {0}, errorCode: {1}", ex.ToString(), ex.SocketErrorCode);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
//hack for iOS (Unity3D)
|
||||
case SocketError.AddressFamilyNotSupported:
|
||||
return true;
|
||||
}
|
||||
NetDebug.WriteError("[B]Bind exception: {0}, errorCode: {1}", bindException.ToString(), bindException.SocketErrorCode);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SendBroadcast(byte[] data, int offset, int size, int port)
|
||||
{
|
||||
bool broadcastSuccess = false;
|
||||
bool multicastSuccess = false;
|
||||
try
|
||||
{
|
||||
broadcastSuccess = _udpSocketv4.SendTo(
|
||||
data,
|
||||
offset,
|
||||
size,
|
||||
SocketFlags.None,
|
||||
new IPEndPoint(IPAddress.Broadcast, port)) > 0;
|
||||
|
||||
if (_udpSocketv6 != null)
|
||||
{
|
||||
multicastSuccess = _udpSocketv6.SendTo(
|
||||
data,
|
||||
offset,
|
||||
size,
|
||||
SocketFlags.None,
|
||||
new IPEndPoint(MulticastAddressV6, port)) > 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NetDebug.WriteError("[S][MCAST]" + ex);
|
||||
return broadcastSuccess;
|
||||
}
|
||||
return broadcastSuccess || multicastSuccess;
|
||||
}
|
||||
|
||||
public int SendTo(byte[] data, int offset, int size, IPEndPoint remoteEndPoint, ref SocketError errorCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
var socket = _udpSocketv4;
|
||||
if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Support)
|
||||
socket = _udpSocketv6;
|
||||
int result = socket.SendTo(data, offset, size, SocketFlags.None, remoteEndPoint);
|
||||
NetDebug.Write(NetLogLevel.Trace, "[S]Send packet to {0}, result: {1}", remoteEndPoint, result);
|
||||
return result;
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
switch (ex.SocketErrorCode)
|
||||
{
|
||||
case SocketError.NoBufferSpaceAvailable:
|
||||
case SocketError.Interrupted:
|
||||
return 0;
|
||||
case SocketError.MessageSize: //do nothing
|
||||
break;
|
||||
default:
|
||||
NetDebug.WriteError("[S]" + ex);
|
||||
break;
|
||||
}
|
||||
errorCode = ex.SocketErrorCode;
|
||||
return -1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NetDebug.WriteError("[S]" + ex);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_running = false;
|
||||
// first close sockets
|
||||
if (_udpSocketv4 != null)
|
||||
{
|
||||
_udpSocketv4.Close();
|
||||
_udpSocketv4 = null;
|
||||
}
|
||||
if (_udpSocketv6 != null)
|
||||
{
|
||||
_udpSocketv6.Close();
|
||||
_udpSocketv6 = null;
|
||||
}
|
||||
// then join threads
|
||||
if (_threadv4 != null)
|
||||
{
|
||||
if (_threadv4 != Thread.CurrentThread)
|
||||
_threadv4.Join();
|
||||
_threadv4 = null;
|
||||
}
|
||||
if (_threadv6 != null)
|
||||
{
|
||||
if (_threadv6 != Thread.CurrentThread)
|
||||
_threadv6.Join();
|
||||
_threadv6 = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue