您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

418 行
12 KiB

/*using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace SocketExtensions
{
public enum SocketEvent
{
Empty,
Data,
Connect,
Disconnect
}
public class UdpCSocket
{
public UdpCSocket()
{
m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
m_Socket.Blocking = false;
m_TimeoutMS = 30 * 1000;
}
public void Bind(EndPoint ep)
{
try
{
m_Socket.Bind(ep);
}
catch (SocketException e)
{
m_SocketError = e.SocketErrorCode;
throw e;
}
}
public void Listen()
{
m_Listening = true;
}
public int Connect(EndPoint ep)
{
if (m_Listening)
throw new SocketException(10048);
int addressLenght = 0;
var address = SocketExtension.MarshalAddress(ep, out addressLenght);
var id = m_NextConnectionId++;
var c = new Connection()
{
Id = id,
State = ConnectionState.Connecting,
Address = address,
AddressLength = addressLenght,
Attempts = 1,
LastAttempt = NetworkUtils.stopwatch.ElapsedMilliseconds
};
m_IdToConnection.Add(c.Id, c);
SendPacket(UdpCProtocol.ConnectionRequest, id);
return id;
}
public void Disconnect(int id = 0)
{
Connection connection;
if (!m_IdToConnection.TryGetValue(id, out connection))
return;
if (connection.State == ConnectionState.Connected)
{
SendPacket(UdpCProtocol.Disconnect, id);
m_IdToConnection.Remove(id);
}
}
public void Close()
{
Disconnect();
}
public SocketEvent ReceiveFrom(ref byte[] data, out int size, out int id)
{
var now = NetworkUtils.stopwatch.ElapsedMilliseconds;
foreach (KeyValuePair<int, Connection> entry in m_IdToConnection)
{
if (entry.Value.State == ConnectionState.Connecting ||
entry.Value.State == ConnectionState.AwaitingResponse)
{
var elapsed = now - entry.Value.LastAttempt;
if (elapsed > ConnectTimeout * entry.Value.Attempts)
{
entry.Value.Attempts++;
entry.Value.LastAttempt = NetworkUtils.stopwatch.ElapsedMilliseconds;
if (entry.Value.State == ConnectionState.Connecting)
SendPacket(UdpCProtocol.ConnectionRequest, entry.Value.Id);
else
SendPacket(UdpCProtocol.ConnectionAccept, entry.Value.Id);
}
}
}
if (m_Disconnected.Count > 0)
{
var connection = m_Disconnected.Dequeue();
id = connection.Id;
size = 0;
return SocketEvent.Disconnect;
}
return ProcessPackets(ref data, out size, out id);
}
public void SendTo(byte[] data, int size, int id)
{
Connection connection;
if (!m_IdToConnection.TryGetValue(id, out connection))
return;
var now = NetworkUtils.stopwatch.ElapsedMilliseconds;
if (now - connection.LastAttempt > m_TimeoutMS)
{
m_Disconnected.Enqueue(connection);
Disconnect(connection.Id);
return;
}
var header = new UdpCHeader()
{
Type = (int)UdpCProtocol.Data
};
unsafe
{
fixed (void* ptr = data)
{
NativeSend(ref header, ptr, size, connection.Address, connection.AddressLength);
}
}
}
#region Properties
public SocketError LastError { get { return m_SocketError; } }
public bool Listening { get { return m_Listening; } }
public int Timeout { get { return m_TimeoutMS; } set { m_TimeoutMS = value; } }
#endregion
#region Private Members
enum ConnectionState
{
Disconnected,
Connecting,
AwaitingResponse,
Connected
}
int m_TimeoutMS;
class Connection
{
public int Id;
public sockaddr_storage Address;
public int Attempts;
public long LastAttempt;
public int AddressLength;
public ConnectionState State;
}
int m_NextConnectionId;
Socket m_Socket;
SocketError m_SocketError;
bool m_Listening;
Dictionary<int, Connection> m_IdToConnection = new Dictionary<int, Connection>();
const int ConnectTimeout = 1000;
Queue<Connection> m_Disconnected = new Queue<Connection>();
// Protocol
enum UdpCProtocol
{
ConnectionRequest,
ConnectionReject,
ConnectionAccept,
Disconnect,
Data
}
[StructLayout(LayoutKind.Explicit)]
unsafe struct UdpCHeader
{
public const int Length = 4;
[FieldOffset(0)] public fixed byte Data[Length];
[FieldOffset(0)] public byte Type;
[FieldOffset(1)] public byte Reserved;
[FieldOffset(2)] public ushort ConnectionId;
}
#endregion
#region Private Impl
Connection FindConnectionByAddress(sockaddr_storage address)
{
if (m_IdToConnection.Count == 0)
return null;
foreach (KeyValuePair<int, Connection> entry in m_IdToConnection)
{
if (address.ReallyEquals(entry.Value.Address))
return entry.Value;
}
return null;
}
int SendPacket(UdpCProtocol type, int id)
{
Connection connection;
if (!m_IdToConnection.TryGetValue(id, out connection))
return 0;
var header = new UdpCHeader()
{
Type = (byte)type
};
unsafe
{
return NativeSend(ref header, null, 0, connection.Address, connection.AddressLength);
}
}
unsafe SocketEvent ProcessPackets(ref byte[] data, out int size, out int id)
{
size = 0;
id = -1;
var address = new sockaddr_storage();
var addressLength = sizeof(sockaddr_storage);
var header = new UdpCHeader();
var result = NativeReceive(ref header, ref data, ref address, ref addressLength);
if (result == -1)
{
throw new SocketException();
}
if (result == 0)
return SocketEvent.Empty;
switch ((UdpCProtocol)header.Type)
{
case UdpCProtocol.ConnectionRequest:
{
var retval = SocketEvent.Empty;
if (!m_Listening)
return retval;
Connection c = FindConnectionByAddress(address);
if (c == null)
{
c = new Connection()
{
Id = m_NextConnectionId++,
State = ConnectionState.Connected,
Address = address,
AddressLength = addressLength,
Attempts = 1,
LastAttempt = NetworkUtils.stopwatch.ElapsedMilliseconds
};
m_IdToConnection.Add(c.Id, c);
retval = SocketEvent.Connect;
id = c.Id;
}
c.Attempts++;
c.LastAttempt = NetworkUtils.stopwatch.ElapsedMilliseconds;
SendPacket(UdpCProtocol.ConnectionAccept, c.Id);
return retval;
} break;
case UdpCProtocol.ConnectionReject:
{
} break;
case UdpCProtocol.ConnectionAccept:
{
Connection c = FindConnectionByAddress(address);
if (c != null && c.State == ConnectionState.Connecting)
{
id = c.Id;
c.State = ConnectionState.Connected;
return SocketEvent.Connect;
}
} break;
case UdpCProtocol.Disconnect:
{
Connection c = FindConnectionByAddress(address);
if (c != null)
{
id = c.Id;
m_IdToConnection.Remove(id);
return SocketEvent.Disconnect;
}
} break;
case UdpCProtocol.Data:
{
Connection c = FindConnectionByAddress(address);
if (c == null)
return SocketEvent.Empty;
c.LastAttempt = NetworkUtils.stopwatch.ElapsedMilliseconds;
if (c.State == ConnectionState.Connected)
{
id = c.Id;
size = result - UdpCHeader.Length;
return SocketEvent.Data;
}
else if (c.State == ConnectionState.Connecting)
{
id = c.Id;
c.State = ConnectionState.Connected;
return SocketEvent.Connect;
}
} break;
}
return SocketEvent.Empty;
}
unsafe int NativeSend(ref UdpCHeader header, void* payload, int payloadSize, sockaddr_storage remote, int remoteSize)
{
var iov = stackalloc iovec[2];
int result, sent = 0;
fixed (byte* ptr = header.Data)
{
iov[0].buf = ptr;
iov[0].len = UdpCHeader.Length;
iov[1].buf = (byte*)payload;
iov[1].len = (ulong)payloadSize;
sent = 0;
int flags = 0;
int addressLen = remoteSize;
result = m_Socket.SendMessageEx(iov, 2, out sent, flags, ref remote, addressLen, null, null);
}
if (result == -1)
{
int error = Marshal.GetLastWin32Error();
GameDebug.Log(string.Format("error on send {0}", error));
throw new SocketException(error);
}
else
result = sent;
return result;
}
unsafe int NativeReceive(ref UdpCHeader header, ref byte[] data, ref sockaddr_storage address, ref int addressSize)
{
var iov = stackalloc iovec[2];
fixed (byte* ptr = header.Data, ptr2 = data)
{
iov[0].buf = ptr;
iov[0].len = UdpCHeader.Length;
iov[1].buf = ptr2;
iov[1].len = (ulong)data.Length;
}
int flags = 0;
int received = 0;
int addressLen = addressSize;
var result = m_Socket.ReceiveMessageEx(iov, 2, out received, out flags, ref address, out addressLen, null, null);
if (result == -1)
{
int err2 = Marshal.GetLastWin32Error();
if (err2 == 10035 || err2 == 35)
return 0;
GameDebug.Log(string.Format("error on receive {0}", err2));
throw new SocketException();
}
else
result = received;
addressSize = addressLen;
return result;
}
#endregion
}
}*/