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.
456 lines
16 KiB
C#
456 lines
16 KiB
C#
#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
|