You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CrowdControl/Assets/Plugins/ConsolePro/Remote/LiteNetLib/NetSocket.cs

456 lines
16 KiB
C#

2 months ago
#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA
#if !WINRT || UNITY_EDITOR
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace FlyingWormConsole3.LiteNetLib
{
internal sealed class NetSocket
{
private Socket _udpSocketv4;
private Socket _udpSocketv6;
private NetEndPoint _localEndPoint;
private Thread _threadv4;
private Thread _threadv6;
private bool _running;
private readonly NetManager.OnMessageReceived _onMessageReceived;
private static readonly IPAddress MulticastAddressV6 = IPAddress.Parse (NetConstants.MulticastGroupIPv6);
private static readonly bool IPv6Support;
private const int SocketReceivePollTime = 100000;
private const int SocketSendPollTime = 5000;
public NetEndPoint LocalEndPoint
{
get { return _localEndPoint; }
}
static NetSocket()
{
try
{
//Unity3d .NET 2.0 throws exception.
// IPv6Support = Socket.OSSupportsIPv6;
IPv6Support = false;
}
catch
{
IPv6Support = false;
}
}
public NetSocket(NetManager.OnMessageReceived onMessageReceived)
{
_onMessageReceived = onMessageReceived;
}
private void ReceiveLogic(object state)
{
Socket socket = (Socket)state;
EndPoint bufferEndPoint = new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0);
NetEndPoint bufferNetEndPoint = new NetEndPoint((IPEndPoint)bufferEndPoint);
byte[] receiveBuffer = new byte[NetConstants.PacketSizeLimit];
while (_running)
{
//wait for data
if (!socket.Poll(SocketReceivePollTime, SelectMode.SelectRead))
{
continue;
}
int result;
//Reading data
try
{
result = socket.ReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, ref bufferEndPoint);
if (!bufferNetEndPoint.EndPoint.Equals(bufferEndPoint))
{
bufferNetEndPoint = new NetEndPoint((IPEndPoint)bufferEndPoint);
}
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.ConnectionReset ||
ex.SocketErrorCode == SocketError.MessageSize)
{
//10040 - message too long
//10054 - remote close (not error)
//Just UDP
NetUtils.DebugWrite(ConsoleColor.DarkRed, "[R] Ingored error: {0} - {1}", (int)ex.SocketErrorCode, ex.ToString() );
continue;
}
NetUtils.DebugWriteError("[R]Error code: {0} - {1}", (int)ex.SocketErrorCode, ex.ToString());
_onMessageReceived(null, 0, (int)ex.SocketErrorCode, bufferNetEndPoint);
continue;
}
//All ok!
NetUtils.DebugWrite(ConsoleColor.Blue, "[R]Recieved data from {0}, result: {1}", bufferNetEndPoint.ToString(), result);
_onMessageReceived(receiveBuffer, result, 0, bufferNetEndPoint);
}
}
public bool Bind(int port, bool reuseAddress)
{
_udpSocketv4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
_udpSocketv4.Blocking = false;
_udpSocketv4.ReceiveBufferSize = NetConstants.SocketBufferSize;
_udpSocketv4.SendBufferSize = NetConstants.SocketBufferSize;
_udpSocketv4.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, NetConstants.SocketTTL);
if(reuseAddress)
_udpSocketv4.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
#if !NETCORE
_udpSocketv4.DontFragment = true;
#endif
try
{
_udpSocketv4.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
}
catch (SocketException e)
{
NetUtils.DebugWriteError("Broadcast error: {0}", e.ToString());
}
if (!BindSocket(_udpSocketv4, new IPEndPoint(IPAddress.Any, port)))
{
return false;
}
_localEndPoint = new NetEndPoint((IPEndPoint)_udpSocketv4.LocalEndPoint);
_running = true;
_threadv4 = new Thread(ReceiveLogic);
_threadv4.Name = "SocketThreadv4(" + port + ")";
_threadv4.IsBackground = true;
_threadv4.Start(_udpSocketv4);
//Check IPv6 support
if (!IPv6Support)
return true;
//Use one port for two sockets
port = _localEndPoint.Port;
_udpSocketv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
_udpSocketv6.Blocking = false;
_udpSocketv6.ReceiveBufferSize = NetConstants.SocketBufferSize;
_udpSocketv6.SendBufferSize = NetConstants.SocketBufferSize;
if (reuseAddress)
_udpSocketv6.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
if (BindSocket(_udpSocketv6, new IPEndPoint(IPAddress.IPv6Any, port)))
{
_localEndPoint = new NetEndPoint((IPEndPoint)_udpSocketv6.LocalEndPoint);
try
{
_udpSocketv6.SetSocketOption(
SocketOptionLevel.IPv6,
SocketOptionName.AddMembership,
new IPv6MulticastOption(MulticastAddressV6));
}
catch(Exception)
{
// Unity3d throws exception - ignored
}
_threadv6 = new Thread(ReceiveLogic);
_threadv6.Name = "SocketThreadv6(" + port + ")";
_threadv6.IsBackground = true;
_threadv6.Start(_udpSocketv6);
}
return true;
}
private bool BindSocket(Socket socket, IPEndPoint ep)
{
try
{
socket.Bind(ep);
NetUtils.DebugWrite(ConsoleColor.Blue, "[B]Succesfully binded to port: {0}", ((IPEndPoint)socket.LocalEndPoint).Port);
}
catch (SocketException ex)
{
NetUtils.DebugWriteError("[B]Bind exception: {0}", ex.ToString());
//TODO: very temporary hack for iOS (Unity3D)
if (ex.SocketErrorCode == SocketError.AddressFamilyNotSupported)
{
return true;
}
return false;
}
return true;
}
public bool SendBroadcast(byte[] data, int offset, int size, int port)
{
try
{
int result = _udpSocketv4.SendTo(data, offset, size, SocketFlags.None, new IPEndPoint(IPAddress.Broadcast, port));
if (result <= 0)
return false;
if (IPv6Support)
{
result = _udpSocketv6.SendTo(data, offset, size, SocketFlags.None, new IPEndPoint(MulticastAddressV6, port));
if (result <= 0)
return false;
}
}
catch (Exception ex)
{
NetUtils.DebugWriteError("[S][MCAST]" + ex);
return false;
}
return true;
}
public int SendTo(byte[] data, int offset, int size, NetEndPoint remoteEndPoint, ref int errorCode)
{
try
{
int result = 0;
if (remoteEndPoint.EndPoint.AddressFamily == AddressFamily.InterNetwork)
{
if (!_udpSocketv4.Poll(SocketSendPollTime, SelectMode.SelectWrite))
return -1;
result = _udpSocketv4.SendTo(data, offset, size, SocketFlags.None, remoteEndPoint.EndPoint);
}
else if(IPv6Support)
{
if (!_udpSocketv6.Poll(SocketSendPollTime, SelectMode.SelectWrite))
return -1;
result = _udpSocketv6.SendTo(data, offset, size, SocketFlags.None, remoteEndPoint.EndPoint);
}
NetUtils.DebugWrite(ConsoleColor.Blue, "[S]Send packet to {0}, result: {1}", remoteEndPoint.EndPoint, result);
return result;
}
catch (SocketException ex)
{
if (ex.SocketErrorCode != SocketError.MessageSize)
{
NetUtils.DebugWriteError("[S]" + ex);
}
errorCode = (int)ex.SocketErrorCode;
return -1;
}
catch (Exception ex)
{
NetUtils.DebugWriteError("[S]" + ex);
return -1;
}
}
private void CloseSocket(Socket s)
{
#if NETCORE
s.Dispose();
#else
s.Close();
#endif
}
public void Close()
{
_running = false;
//Close IPv4
if (Thread.CurrentThread != _threadv4)
{
_threadv4.Join();
}
_threadv4 = null;
if (_udpSocketv4 != null)
{
CloseSocket(_udpSocketv4);
_udpSocketv4 = null;
}
//No ipv6
if (_udpSocketv6 == null)
return;
//Close IPv6
if (Thread.CurrentThread != _threadv6)
{
_threadv6.Join();
}
_threadv6 = null;
if (_udpSocketv6 != null)
{
CloseSocket(_udpSocketv6);
_udpSocketv6 = null;
}
}
}
}
#else
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading;
using System.Threading.Tasks;
using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
namespace FlyingWormConsole3.LiteNetLib
{
internal sealed class NetSocket
{
private DatagramSocket _datagramSocket;
private readonly Dictionary<NetEndPoint, IOutputStream> _peers = new Dictionary<NetEndPoint, IOutputStream>();
private readonly NetManager.OnMessageReceived _onMessageReceived;
private readonly byte[] _byteBuffer = new byte[NetConstants.PacketSizeLimit];
private readonly IBuffer _buffer;
private NetEndPoint _bufferEndPoint;
private NetEndPoint _localEndPoint;
private static readonly HostName BroadcastAddress = new HostName("255.255.255.255");
private static readonly HostName MulticastAddressV6 = new HostName(NetConstants.MulticastGroupIPv6);
public NetEndPoint LocalEndPoint
{
get { return _localEndPoint; }
}
public NetSocket(NetManager.OnMessageReceived onMessageReceived)
{
_onMessageReceived = onMessageReceived;
_buffer = _byteBuffer.AsBuffer();
}
private void OnMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
{
var result = args.GetDataStream().ReadAsync(_buffer, _buffer.Capacity, InputStreamOptions.None).AsTask().Result;
int length = (int)result.Length;
if (length <= 0)
return;
if (_bufferEndPoint == null ||
!_bufferEndPoint.HostName.IsEqual(args.RemoteAddress) ||
!_bufferEndPoint.PortStr.Equals(args.RemotePort))
{
_bufferEndPoint = new NetEndPoint(args.RemoteAddress, args.RemotePort);
}
_onMessageReceived(_byteBuffer, length, 0, _bufferEndPoint);
}
public bool Bind(int port, bool reuseAddress)
{
_datagramSocket = new DatagramSocket();
_datagramSocket.Control.InboundBufferSizeInBytes = NetConstants.SocketBufferSize;
_datagramSocket.Control.DontFragment = true;
_datagramSocket.Control.OutboundUnicastHopLimit = NetConstants.SocketTTL;
_datagramSocket.MessageReceived += OnMessageReceived;
try
{
_datagramSocket.BindServiceNameAsync(port.ToString()).AsTask().Wait();
_datagramSocket.JoinMulticastGroup(MulticastAddressV6);
_localEndPoint = new NetEndPoint(_datagramSocket.Information.LocalAddress, _datagramSocket.Information.LocalPort);
}
catch (Exception ex)
{
NetUtils.DebugWriteError("[B]Bind exception: {0}", ex.ToString());
return false;
}
return true;
}
public bool SendBroadcast(byte[] data, int offset, int size, int port)
{
var portString = port.ToString();
try
{
var outputStream =
_datagramSocket.GetOutputStreamAsync(BroadcastAddress, portString)
.AsTask()
.Result;
var writer = outputStream.AsStreamForWrite();
writer.Write(data, offset, size);
writer.Flush();
outputStream =
_datagramSocket.GetOutputStreamAsync(MulticastAddressV6, portString)
.AsTask()
.Result;
writer = outputStream.AsStreamForWrite();
writer.Write(data, offset, size);
writer.Flush();
}
catch (Exception ex)
{
NetUtils.DebugWriteError("[S][MCAST]" + ex);
return false;
}
return true;
}
public int SendTo(byte[] data, int offset, int length, NetEndPoint remoteEndPoint, ref int errorCode)
{
Task<uint> task = null;
try
{
IOutputStream writer;
if (!_peers.TryGetValue(remoteEndPoint, out writer))
{
writer =
_datagramSocket.GetOutputStreamAsync(remoteEndPoint.HostName, remoteEndPoint.PortStr)
.AsTask()
.Result;
_peers.Add(remoteEndPoint, writer);
}
task = writer.WriteAsync(data.AsBuffer(offset, length)).AsTask();
return (int)task.Result;
}
catch (Exception ex)
{
if (task?.Exception?.InnerExceptions != null)
{
ex = task.Exception.InnerException;
}
var errorStatus = SocketError.GetStatus(ex.HResult);
switch (errorStatus)
{
case SocketErrorStatus.MessageTooLong:
errorCode = 10040;
break;
default:
errorCode = (int)errorStatus;
NetUtils.DebugWriteError("[S " + errorStatus + "(" + errorCode + ")]" + ex);
break;
}
return -1;
}
}
internal void RemovePeer(NetEndPoint ep)
{
_peers.Remove(ep);
}
public void Close()
{
_datagramSocket.Dispose();
_datagramSocket = null;
ClearPeers();
}
internal void ClearPeers()
{
_peers.Clear();
}
}
}
#endif
#endif