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

1526 行
74 KiB

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Unity.Baselib.LowLevel;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Networking.Transport.Utilities.LowLevel.Unsafe;
using Unity.Networking.Transport.Protocols;
using ErrorState = Unity.Baselib.LowLevel.Binding.Baselib_ErrorState;
using ErrorCode = Unity.Baselib.LowLevel.Binding.Baselib_ErrorCode;
using Random = Unity.Mathematics.Random;
namespace Unity.Networking.Transport
{
[BurstCompile]
public struct WebSocketNetworkInterface : INetworkInterface
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS && !UNITY_WEBGL
private class SocketList
{
public struct SocketId
{
public Binding.Baselib_Socket_Handle socket;
}
public List<SocketId> OpenSockets = new List<SocketId>();
~SocketList()
{
foreach (var socket in OpenSockets)
{
Binding.Baselib_Socket_Close(socket.socket);
}
}
}
private static SocketList AllSockets = new SocketList();
#endif
unsafe struct BaselibData
{
public NetworkInterfaceEndPoint BoundAddress;
#if !UNITY_WEBGL
public Binding.Baselib_Socket_Handle ListenSocket;
#endif
}
unsafe struct ConnectionState
{
#if UNITY_WEBGL
public int Socket;
#else
public Binding.Baselib_Socket_Handle Socket;
public WebSocketPacket Packet;
public WebSocketPacket PendingSendPacket;
public ulong RemainingPacketSize;
public int HeaderSize;
public int PacketStart;
public uint Key0;
public uint Key1;
public uint Key2;
public uint Key3;
public long CloseTimeStamp;
#endif
public int ConnectState;
}
const int WebSocketMaxHeaderSize = 14;
const int ConnectionSentClose = 16;
const int ConnectionIsClient = 8;
const int ConnectionKnownByDriver = 4;
const int ConnectionStateMask = 3;
unsafe struct WebSocketPacket
{
public fixed byte Data[NetworkParameterConstants.MTU + WebSocketMaxHeaderSize];
public int DataLength;
#if !UNITY_WEBGL
public void ConstructBinary(void* payload, int payloadLen, bool useMask, uint mask)
{
// fin + binary
int headerLen = 0;
Data[headerLen++] = 0x82;
byte maskFlag = (byte)(useMask ? 0x80 : 0);
if (payloadLen < 126)
Data[headerLen++] = (byte)(maskFlag | payloadLen);
else if (payloadLen <= 0xffff)
{
Data[headerLen++] = (byte)(maskFlag | 126);
Data[headerLen++] = (byte)(payloadLen>>8);
Data[headerLen++] = (byte)(payloadLen&0xff);
}
else
{
Data[headerLen++] = (byte)(maskFlag | 127);
Data[headerLen++] = (byte)0;
Data[headerLen++] = (byte)0;
Data[headerLen++] = (byte)0;
Data[headerLen++] = (byte)0;
Data[headerLen++] = (byte)((payloadLen>>24)&0xff);
Data[headerLen++] = (byte)((payloadLen>>16)&0xff);
Data[headerLen++] = (byte)((payloadLen>>8)&0xff);
Data[headerLen++] = (byte)(payloadLen&0xff);
}
if (useMask)
{
Data[headerLen++] = (byte)(mask>>24);
Data[headerLen++] = (byte)((mask>>16)&0xff);
Data[headerLen++] = (byte)((mask>>8)&0xff);
Data[headerLen++] = (byte)(mask&0xff);
fixed (byte* ptr = Data)
{
var maskBytes = ptr+headerLen-4;
byte* dst = ptr + headerLen;
byte* src = (byte*)payload;
for (int i = 0; i < payloadLen; ++i)
dst[i] = (byte)(src[i]^maskBytes[i&3]);
}
}
else
{
fixed (byte* ptr = Data)
UnsafeUtility.MemCpy(ptr+headerLen, payload, payloadLen);
}
DataLength = headerLen + payloadLen;
}
public void ConstructPong(void* payload, int payloadLen, bool useMask, uint mask)
{
ConstructBinary(payload, payloadLen, useMask, mask);
Data[0] = 0x8a;
}
public void ConstructClose(ushort status, bool useMask, uint mask)
{
ConstructBinary(&status, 2, useMask, mask);
Data[0] = 0x88;
}
#endif
}
#if UNITY_WEBGL
static int s_NextSocketId = 0;
private const string DLL = "__Internal";
[DllImport(DLL, EntryPoint = "js_html_utpWebSocketCreate")]
private static extern void WebSocketCreate(int sockId, IntPtr addrData, int addrSize, IntPtr data, int size);
[DllImport(DLL, EntryPoint = "js_html_utpWebSocketDestroy")]
private static extern void WebSocketDestroy(int sockId);
[DllImport(DLL, EntryPoint = "js_html_utpWebSocketSend")]
private static extern int WebSocketSend(int sockId, IntPtr data, int size);
[DllImport(DLL, EntryPoint = "js_html_utpWebSocketRecv")]
private static extern int WebSocketRecv(int sockId, IntPtr data, int size);
[DllImport(DLL, EntryPoint = "js_html_utpWebSocketIsConnected")]
private static extern int WebSocketIsConnected(int sockId);
#else
unsafe struct SHA1
{
private void UpdateABCDE(int i, ref uint a, ref uint b, ref uint c, ref uint d, ref uint e, uint f, uint k)
{
var tmp = ((a << 5) | (a >> 27)) + e + f + k + words[i];
e = d;
d = c;
c = (b << 30) | (b >> 2);
b = a;
a = tmp;
}
private void UpdateHash()
{
for (int i = 16; i < 80; ++i)
{
words[i] = (words[i-3] ^ words[i-8] ^ words[i-14] ^ words[i-16]);
words[i] = (words[i] << 1) | (words[i] >> 31);
}
var a = h0;
var b = h1;
var c = h2;
var d = h3;
var e = h4;
for (int i = 0; i < 20; ++i)
{
var f = (b & c) | ((~b) & d);
var k = 0x5a827999u;
UpdateABCDE(i, ref a, ref b, ref c, ref d, ref e, f, k);
}
for (int i = 20; i < 40; ++i)
{
var f = b ^ c ^ d;
var k = 0x6ed9eba1u;
UpdateABCDE(i, ref a, ref b, ref c, ref d, ref e, f, k);
}
for (int i = 40; i < 60; ++i)
{
var f = (b & c) | (b & d) | (c & d);
var k = 0x8f1bbcdcu;
UpdateABCDE(i, ref a, ref b, ref c, ref d, ref e, f, k);
}
for (int i = 60; i < 80; ++i)
{
var f = b ^ c ^ d;
var k = 0xca62c1d6u;
UpdateABCDE(i, ref a, ref b, ref c, ref d, ref e, f, k);
}
h0 += a;
h1 += b;
h2 += c;
h3 += d;
h4 += e;
}
public SHA1(in FixedString128 str)
{
h0 = 0x67452301u;
h1 = 0xefcdab89u;
h2 = 0x98badcfeu;
h3 = 0x10325476u;
h4 = 0xc3d2e1f0u;
var bitLen = str.Length << 3;
var numFullChunks = bitLen>>9;
byte* ptr = str.GetUnsafePtr();
for (int chunk = 0; chunk < numFullChunks; ++chunk)
{
for (int i = 0; i < 16; ++i)
{
words[i] = (uint)((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]);
ptr += 4;
}
UpdateHash();
}
var remainingBits = (bitLen&0x1ff);
var remainingBytes = (remainingBits>>3);
var fullWords = (remainingBytes>>2);
for (int i = 0; i < fullWords; ++i)
{
words[i] = (uint)((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]);
ptr += 4;
}
var fullBytes = remainingBytes&3;
switch (fullBytes)
{
case 3:
words[fullWords] = (uint)((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | 0x80u);
ptr += 3;
break;
case 2:
words[fullWords] = (uint)((ptr[0]<<24) | (ptr[1]<<16) | (0x80u << 8));
ptr += 2;
break;
case 1:
words[fullWords] = (uint)((ptr[0]<<24) | (0x80u << 16));
ptr += 1;
break;
case 0:
words[fullWords] = (uint)((0x80u << 24));
break;
}
++fullWords;
if (remainingBits >= 448)
{
// Needs two chunks, one for the remaining bits and one for size
for (int i = fullWords; i < 16; ++i)
words[i] = 0;
UpdateHash();
for (int i = 0; i < 15; ++i)
words[i] = 0;
words[15] = (uint)bitLen;
UpdateHash();
}
else
{
for (int i = fullWords; i < 15; ++i)
words[i] = 0;
words[15] = (uint)bitLen;
UpdateHash();
}
}
public FixedString32 ToBase64()
{
FixedString32 base64 = default;
AppendBase64(ref base64, (byte)(h0>>24), (byte)(h0>>16), (byte)(h0>>8));
AppendBase64(ref base64, (byte)(h0), (byte)(h1>>24), (byte)(h1>>16));
AppendBase64(ref base64, (byte)(h1>>8), (byte)(h1), (byte)(h2>>24));
AppendBase64(ref base64, (byte)(h2>>16), (byte)(h2>>8), (byte)(h2));
AppendBase64(ref base64, (byte)(h3>>24), (byte)(h3>>16), (byte)(h3>>8));
AppendBase64(ref base64, (byte)(h3), (byte)(h4>>24), (byte)(h4>>16));
AppendBase64(ref base64, (byte)(h4>>8), (byte)(h4));
return base64;
}
private fixed uint words[80];
private uint h0;
private uint h1;
private uint h2;
private uint h3;
private uint h4;
}
static byte ApplyTable(byte val)
{
if (val < 26)
return (byte)(val + 'A');
else if (val < 52)
return (byte)(val + 'a' - 26);
else if (val < 62)
return (byte)(val + '0' - 52);
else if (val == 62)
return (byte)'+';
return (byte)'/';
}
static void AppendBase64(ref FixedString32 base64, byte b0, byte b1, byte b2)
{
var c1 = ApplyTable((byte)(b0>>2));
var c2 = ApplyTable((byte)(((b0&3)<<4) | (b1>>4)));
var c3 = ApplyTable((byte)(((b1&0xf)<<2) | (b2>>6)));
var c4 = ApplyTable((byte)(b2&0x3f));
base64.Add(c1);
base64.Add(c2);
base64.Add(c3);
base64.Add(c4);
}
static void AppendBase64(ref FixedString32 base64, byte b0, byte b1)
{
var c1 = ApplyTable((byte)(b0>>2));
var c2 = ApplyTable((byte)(((b0&3)<<4) | (b1>>4)));
var c3 = ApplyTable((byte)((b1&0xf)<<2));
base64.Add(c1);
base64.Add(c2);
base64.Add(c3);
base64.Add((byte)'=');
}
static void AppendBase64(ref FixedString32 base64, byte b0)
{
var c1 = ApplyTable((byte)(b0>>2));
var c2 = ApplyTable((byte)((b0&3)<<4));
base64.Add(c1);
base64.Add(c2);
base64.Add((byte)'=');
base64.Add((byte)'=');
}
private static void WebSocketDestroy(Binding.Baselib_Socket_Handle socket)
{
Binding.Baselib_Socket_Close(socket);
}
private static void GenerateBase64Key(out FixedString32 key, uint key0, uint key1, uint key2, uint key3)
{
key = default;
AppendBase64(ref key, (byte)(key0>>24), (byte)(key0>>16), (byte)(key0>>8));
AppendBase64(ref key, (byte)(key0), (byte)(key1>>24), (byte)(key1>>16));
AppendBase64(ref key, (byte)(key1>>8), (byte)(key1), (byte)(key2>>24));
AppendBase64(ref key, (byte)(key2>>16), (byte)(key2>>8), (byte)(key2));
AppendBase64(ref key, (byte)(key3>>24), (byte)(key3>>16), (byte)(key3>>8));
AppendBase64(ref key, (byte)(key3));
}
private static unsafe int WebSocketIsConnected(Binding.Baselib_Socket_Handle socket, in NetworkInterfaceEndPoint address, uint key0, uint key1, uint key2, uint key3)
{
var error = default(ErrorState);
var sockError = default(ErrorState);
var sockFd = new Binding.Baselib_Socket_PollFd
{
handle = socket,
requestedEvents = Binding.Baselib_Socket_PollEvents.Connected,
errorState = &sockError
};
Binding.Baselib_Socket_Poll(&sockFd, 1, 0, &error);
if (sockFd.errorState->code != ErrorCode.Success)
return -1;
if ((sockFd.resultEvents & Binding.Baselib_Socket_PollEvents.Connected) != 0)
{
FixedString32 end = "\r\n";
FixedString512 handshake = "GET / HTTP/1.1\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Version: 13\r\n";
FixedString32 key = "Sec-WebSocket-Key: ";
handshake.Append(key);
GenerateBase64Key(out key, key0, key1, key2, key3);
handshake.Append(key);
handshake.Append(end);
FixedString128 host = "Host: ";
handshake.Append(host);
fixed (void* dataptr = address.data)
handshake.Append(NetworkEndPoint.AddressToString(*(Binding.Baselib_NetworkAddress*)dataptr));
handshake.Append(end);
handshake.Append(end);
var count = (int) Binding.Baselib_Socket_TCP_Send(
socket,
(IntPtr)handshake.GetUnsafePtr(),
(uint)handshake.Length,
&error);
if (sockFd.errorState->code != ErrorCode.Success || count != handshake.Length)
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
UnityEngine.Debug.LogWarning("Failed to send WebSocket client handshake.");
#endif
return -1;
}
return 1;
}
return 0;
}
private static unsafe int WebSocketIsHandshakeComplete(ref ConnectionState connection)
{
var error = default(ErrorState);
FixedString4096 recvHandshake = default;
int receivedBytes = (int)Binding.Baselib_Socket_TCP_Recv(connection.Socket, (IntPtr)recvHandshake.GetUnsafePtr(), (uint)FixedString4096.UTF8MaxLengthInBytes, &error);
if (error.code != ErrorCode.Success)
return -1;
if (receivedBytes == 0)
return 0;
recvHandshake.Length = (ushort)receivedBytes;
// If handshake does not end with \r\n\r\n it was an invalid or incomplete message
if (recvHandshake.Length < 4 ||
recvHandshake[recvHandshake.Length-4] != '\r' || recvHandshake[recvHandshake.Length-3] != '\n' ||
recvHandshake[recvHandshake.Length-2] != '\r' || recvHandshake[recvHandshake.Length-1] != '\n')
{
// Invalid header
#if ENABLE_UNITY_COLLECTIONS_CHECKS
UnityEngine.Debug.LogWarning("Received invalid http or incomplete message for handshake.");
#endif
return -1;
}
int lineStart = 0;
int lineEnd = 0;
while (recvHandshake[lineEnd] != '\r' || recvHandshake[lineEnd+1] != '\n')
++lineEnd;
int firstLineEnd = lineEnd;
var headerLookup = new NativeHashMap<FixedString128, FixedString128>(16, Allocator.Temp);
while (true)
{
lineEnd += 2;
lineStart = lineEnd;
while (recvHandshake[lineEnd] != '\r' || recvHandshake[lineEnd+1] != '\n')
++lineEnd;
if (lineStart == lineEnd)
break;
// Found a line - analyze it
int keyStart = lineStart;
while (recvHandshake[keyStart] == ' ' || recvHandshake[keyStart] == '\t')
++keyStart;
int keyEnd = keyStart;
FixedString128 key = default;
// Not allowing whitespace in keys
while (recvHandshake[keyEnd] != ':' && recvHandshake[keyEnd] != ' ' && recvHandshake[keyEnd] != '\t' && recvHandshake[keyEnd] != '\r' && recvHandshake[keyEnd] != '\n')
{
byte ch = recvHandshake[keyEnd];
if (ch >= (byte)'A' && ch <= (byte)'Z')
ch = (byte)(ch + 'a' - 'A');
key.Add(ch);
++keyEnd;
}
int valueStart = keyEnd;
while (recvHandshake[valueStart] != ':')
{
if (recvHandshake[valueStart] != ' ' && recvHandshake[valueStart] != '\t' && recvHandshake[valueStart] != '\r' && recvHandshake[valueStart] != '\n')
break;
++valueStart;
}
if (recvHandshake[valueStart] != ':')
continue;
++valueStart;
while (recvHandshake[valueStart] == ' ' || recvHandshake[valueStart] == '\t')
++valueStart;
FixedString128 value = default;
int valueEnd = valueStart;
while (recvHandshake[valueEnd] != '\r')
{
value.Add(recvHandshake[valueEnd]);
++valueEnd;
}
// Trim trailing whitespace
while (value.Length > 0 && (value[value.Length-1] == ' ' || value[value.Length-1] == '\t'))
value.Length = value.Length-1;
headerLookup.TryAdd(key, value);
}
FixedString128 keyMagic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
FixedString128 connectionHeader = "connection";
FixedString128 upgradeHeader = "upgrade";
FixedString128 headerValue;
var invalidConnection = !headerLookup.TryGetValue(connectionHeader, out headerValue) || headerValue.Length < 7;
// Scan for "upgrade" in a coma separated list
if (!invalidConnection)
{
invalidConnection = true;
int upPos = 0;
int len = 0;
while ((len = headerValue.Length - upPos) >= 7)
{
invalidConnection = ((headerValue[upPos+0]|32) != 'u' || (headerValue[upPos+1]|32) != 'p' || (headerValue[upPos+2]|32) != 'g' ||
(headerValue[upPos+3]|32) != 'r' || (headerValue[upPos+4]|32) != 'a' || (headerValue[upPos+5]|32) != 'd' || (headerValue[upPos+6]|32) != 'e');
if (!invalidConnection)
{
if (len == 7 || headerValue[upPos+7] == ',' || headerValue[upPos+7] == ' ' || headerValue[upPos+7] == '\t')
break;
invalidConnection = true;
}
while (upPos < headerValue.Length && headerValue[upPos] != ',')
++upPos;
// Skip ,
++upPos;
// skip whitespace
while (upPos < headerValue.Length && (headerValue[upPos] == ' ' || headerValue[upPos] == '\t'))
++upPos;
}
}
var invalidUpgrade = (!headerLookup.TryGetValue(upgradeHeader, out headerValue) || headerValue.Length != 9 ||
(headerValue[0]|32) != 'w' || (headerValue[1]|32) != 'e' || (headerValue[2]|32) != 'b' || (headerValue[3]|32) != 's' || (headerValue[4]|32) != 'o' || (headerValue[5]|32) != 'c' || (headerValue[6]|32) != 'k' || (headerValue[7]|32) != 'e' || (headerValue[8]|32) != 't');
// Receive handshake, different handshake depending on if PayloadSize is 0 (server) or > 0 (client)
if (connection.Packet.DataLength > 0)
{
var invalidStatusLine = (firstLineEnd < 14 ||
recvHandshake[0] != 'H' || recvHandshake[1] != 'T' || recvHandshake[2] != 'T' || recvHandshake[3] != 'P' ||
recvHandshake[4] != '/' || recvHandshake[5] != '1' || recvHandshake[6] != '.' || recvHandshake[7] != '1' ||
recvHandshake[8] != ' ' || recvHandshake[9] != '1' || recvHandshake[10] != '0' || recvHandshake[11] != '1' ||
recvHandshake[12] != ' ');
if (invalidStatusLine)
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
UnityEngine.Debug.LogWarning("Could not parse http status line.");
#endif
return -1;
}
FixedString128 protocolHeader = "sec-websocket-protocol";
FixedString128 extensionHeader = "sec-websocket-extensions";
FixedString128 acceptHeader = "sec-websocket-accept";
FixedString128 wsKey;
if (invalidConnection || invalidUpgrade || headerLookup.ContainsKey(protocolHeader) ||
headerLookup.ContainsKey(extensionHeader) || !headerLookup.TryGetValue(acceptHeader, out wsKey))
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
if (invalidConnection)
UnityEngine.Debug.LogWarning("Received handshake with invalid or missing Connection key.");
if (invalidUpgrade)
UnityEngine.Debug.LogWarning("Received handshake with invalid or missing Upgrade key.");
if (headerLookup.ContainsKey(protocolHeader))
UnityEngine.Debug.LogWarning("Received handshake with a subprotocol != null.");
if (headerLookup.ContainsKey(extensionHeader))
UnityEngine.Debug.LogWarning("Received handshake with an extension.");
if (!headerLookup.ContainsKey(acceptHeader))
UnityEngine.Debug.LogWarning("Received handshake with a missing sec-websocket-accept key.");
#endif
return -1;
}
// validate the accept header
FixedString128 refWsKey = default;
GenerateBase64Key(out var clientKey, connection.Key0, connection.Key1, connection.Key2, connection.Key3);
refWsKey.Append(clientKey);
refWsKey.Append(keyMagic);
var hash = new SHA1(refWsKey);
clientKey = hash.ToBase64();
if (wsKey != clientKey)
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
UnityEngine.Debug.LogWarning("Received handshake with incorrect sec-websocket-accept.");
#endif
return -1;
}
if (connection.PendingSendPacket.DataLength == 0)
{
// Send the pending connect message
uint count;
fixed (byte* ptr = connection.Packet.Data)
{
count = Binding.Baselib_Socket_TCP_Send(
connection.Socket,
(IntPtr)ptr,
(uint)connection.Packet.DataLength,
&error);
}
if (count != 0 && count != connection.Packet.DataLength)
{
// Backup the pending packet for sending later
connection.PendingSendPacket.DataLength = connection.Packet.DataLength - (int)count;
fixed (byte* dst = connection.PendingSendPacket.Data)
fixed (byte* src = connection.Packet.Data)
{
UnsafeUtility.MemCpy(dst, src + count, connection.PendingSendPacket.DataLength);
}
}
}
}
else
{
FixedString512 handshake;
FixedString128 hostHeader = "host";
FixedString128 keyHeader = "sec-websocket-key";
FixedString128 versionHeader = "sec-websocket-version";
var invalidRequestLine = (firstLineEnd < 14 || recvHandshake[0] != 'G' || recvHandshake[1] != 'E' || recvHandshake[2] != 'T' || recvHandshake[3] != ' ' ||
recvHandshake[firstLineEnd-9] != ' ' ||
recvHandshake[firstLineEnd-8] != 'H' || recvHandshake[firstLineEnd-7] != 'T' || recvHandshake[firstLineEnd-6] != 'T' || recvHandshake[firstLineEnd-5] != 'P' ||
recvHandshake[firstLineEnd-4] != '/' || recvHandshake[firstLineEnd-3] != '1' || recvHandshake[firstLineEnd-2] != '.' || recvHandshake[firstLineEnd-1] != '1');
FixedString128 wsKey;
var invalidVersion = (!headerLookup.TryGetValue(versionHeader, out headerValue) || headerValue.Length != 2 ||
headerValue[0] != '1' || headerValue[1] != '3');
var invalidKey = (!headerLookup.TryGetValue(keyHeader, out wsKey) || wsKey.Length != 24);
if (invalidRequestLine || !headerLookup.ContainsKey(hostHeader) || invalidKey ||
invalidVersion || invalidConnection || invalidUpgrade)
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
if (invalidRequestLine)
UnityEngine.Debug.LogWarning("Received handshake with invalid http request line.");
if (invalidVersion)
UnityEngine.Debug.LogWarning("Received handshake with invalid or missing sec-websocket-version key.");
if (invalidConnection)
UnityEngine.Debug.LogWarning("Received handshake with invalid or missing Connection key.");
if (invalidUpgrade)
UnityEngine.Debug.LogWarning("Received handshake with invalid or missing Upgrade key.");
if (!headerLookup.ContainsKey(hostHeader))
UnityEngine.Debug.LogWarning("Received handshake with a missing host key.");
if (invalidKey)
UnityEngine.Debug.LogWarning("Received handshake with a missing or invalid sec-websocket-key key.");
#endif
// Not a valid get request
handshake = "HTTP/1.1 400 Bad Request\r\nSec-WebSocket-Version: 13\r\n\r\n";
Binding.Baselib_Socket_TCP_Send(
connection.Socket,
(IntPtr)handshake.GetUnsafePtr(),
(uint)handshake.Length,
&error);
return -1;
}
// Only / is available
if (firstLineEnd != 14 || recvHandshake[4] != '/')
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
UnityEngine.Debug.LogWarning("Received handshake with an incorrect resource name.");
#endif
handshake = "HTTP/1.1 404 Not Found\r\n\r\n";
Binding.Baselib_Socket_TCP_Send(
connection.Socket,
(IntPtr)handshake.GetUnsafePtr(),
(uint)handshake.Length,
&error);
return -1;
}
handshake = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n";
wsKey.Append(keyMagic);
var hash = new SHA1(wsKey);
FixedString128 accept = "Sec-WebSocket-Accept: ";
handshake.Append(accept);
handshake.Append(hash.ToBase64());
FixedString32 end = "\r\n";
handshake.Append(end);
handshake.Append(end);
var count = (int) Binding.Baselib_Socket_TCP_Send(
connection.Socket,
(IntPtr)handshake.GetUnsafePtr(),
(uint)handshake.Length,
&error);
if (error.code != ErrorCode.Success || count != handshake.Length)
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
UnityEngine.Debug.LogWarning("Failed to send WebSocket server handshake.");
#endif
return -1;
}
}
return 1;
}
#endif
[ReadOnly]
NativeHashMap<NetworkInterfaceEndPoint, ConnectionState> m_Connections;
[ReadOnly]
private NativeArray<BaselibData> m_Baselib;
/// <summary>
/// Returns the local endpoint.
/// </summary>
/// <value>NetworkInterfaceEndPoint</value>
public unsafe NetworkInterfaceEndPoint LocalEndPoint
{
// error handling: handle the errors...
get
{
var address = m_Baselib[0].BoundAddress;
#if !UNITY_WEBGL
if (m_Baselib[0].ListenSocket.handle != Binding.Baselib_Socket_Handle_Invalid.handle)
{
var error = default(ErrorState);
Binding.Baselib_Socket_GetAddress(m_Baselib[0].ListenSocket, (Binding.Baselib_NetworkAddress*)address.data, &error);
}
#endif
return address;
}
}
public bool IsCreated => m_Baselib.IsCreated;
/// <summary>
/// Creates a interface endpoint.
/// </summary>
/// <value>NetworkInterfaceEndPoint</value>
public unsafe int CreateInterfaceEndPoint(NetworkEndPoint address, out NetworkInterfaceEndPoint endpoint)
{
endpoint.dataLength = address.length;
fixed (void* ptr = endpoint.data)
*(Binding.Baselib_NetworkAddress*)ptr = address.rawNetworkAddress;
return (int) Error.StatusCode.Success;
}
public unsafe NetworkEndPoint GetGenericEndPoint(NetworkInterfaceEndPoint endpoint)
{
// Set to a valid address so length is set correctly
var address = NetworkEndPoint.LoopbackIpv4;
address.rawNetworkAddress = *(Binding.Baselib_NetworkAddress*)endpoint.data;
address.length = endpoint.dataLength;
return address;
}
/// <summary>
/// Initializes a instance of the BaselibNetworkInterface struct.
/// </summary>
/// <param name="param">An array of INetworkParameter. There is currently only <see cref="BaselibNetworkParameter"/> that can be passed.</param>
public unsafe int Initialize(params INetworkParameter[] param)
{
m_Baselib = new NativeArray<BaselibData>(1, Allocator.Persistent);
var baselib = default(BaselibData);
#if !UNITY_WEBGL
baselib.ListenSocket = Binding.Baselib_Socket_Handle_Invalid;
#endif
CreateInterfaceEndPoint(NetworkEndPoint.AnyIpv4, out baselib.BoundAddress);
m_Baselib[0] = baselib;
m_Connections = new NativeHashMap<NetworkInterfaceEndPoint, ConnectionState>(1, Allocator.Persistent);
return 0;
}
public unsafe void Dispose()
{
#if !UNITY_WEBGL
if (m_Baselib[0].ListenSocket.handle != Binding.Baselib_Socket_Handle_Invalid.handle)
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS && !UNITY_WEBGL
AllSockets.OpenSockets.Remove(new SocketList.SocketId
{socket = m_Baselib[0].ListenSocket});
#endif
Binding.Baselib_Socket_Close(m_Baselib[0].ListenSocket);
}
WebSocketPacket packet = default;
#endif
var keys = m_Connections.GetKeyArray(Allocator.Temp);
for (int connectionIndex = 0; connectionIndex < keys.Length; ++connectionIndex)
{
var connection = m_Connections[keys[connectionIndex]];
#if !UNITY_WEBGL
if ((connection.ConnectState&ConnectionSentClose) == 0 && connection.PendingSendPacket.DataLength == 0)
{
var error = default(ErrorState);
packet.ConstructClose(1001, (connection.ConnectState&ConnectionIsClient) != 0, new Random((uint)Stopwatch.GetTimestamp()).NextUInt());
var count = (int)Binding.Baselib_Socket_TCP_Send(
connection.Socket,
(IntPtr)packet.Data,
(uint)packet.DataLength,
&error);
}
#endif
WebSocketDestroy(connection.Socket);
}
m_Baselib.Dispose();
m_Connections.Dispose();
}
#region ReceiveJob
#if !UNITY_WEBGL // this job is calling external js methods which currently does not work from burst
[BurstCompile]
#endif
struct ReceiveJob : IJob
{
public NetworkPacketReceiver Receiver;
[NativeDisableContainerSafetyRestriction]
public NativeArray<BaselibData> Baselib;
[NativeDisableContainerSafetyRestriction]
public NativeHashMap<NetworkInterfaceEndPoint, ConnectionState> Connections;
public Random rand;
int headerSize;
private unsafe bool CopyToStream(in NetworkInterfaceEndPoint address, byte* data, int dataLength)
{
if (dataLength < headerSize)
return true;
var stream = Receiver.GetDataStream();
int dataStreamSize = Receiver.GetDataStreamSize();
if (Receiver.DynamicDataStreamSize())
{
while (dataStreamSize + dataLength >= stream.Length)
stream.ResizeUninitialized(stream.Length*2);
}
else if (dataStreamSize + dataLength > stream.Length)
{
Receiver.ReceiveErrorCode = 10040;//(int)ErrorCode.OutOfMemory;
return false;
}
UnsafeUtility.MemCpy(
(byte*)stream.GetUnsafePtr() + dataStreamSize,
(byte*)data,
dataLength);
Receiver.ReceiveCount += Receiver.AppendPacket(address, dataLength);
return true;
}
public unsafe void Execute()
{
#if !UNITY_WEBGL
if (Baselib[0].ListenSocket.handle != Binding.Baselib_Socket_Handle_Invalid.handle)
{
var error = default(ErrorState);
var socket = Binding.Baselib_Socket_TCP_Accept(Baselib[0].ListenSocket, &error);
if (socket.handle != Binding.Baselib_Socket_Handle_Invalid.handle)
{
var address = Baselib[0].BoundAddress;
Binding.Baselib_Socket_GetAddress(socket, (Binding.Baselib_NetworkAddress*)address.data, &error);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
if (error.code != ErrorCode.Success)
UnityEngine.Debug.LogWarning($"Failed to get address {error.code}");
#endif
while (!Connections.TryAdd(address, new ConnectionState
{
Socket = socket,
ConnectState = 1
}))
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
UnityEngine.Debug.LogWarning("Duplicate address - test next");
#endif
// FIXME: HACK: probe for another address which is unique
ushort* port = (ushort*)&(((Binding.Baselib_NetworkAddress*)address.data)->port0);
*port = (ushort)(*port + 1);
}
// The client is responsible to send a UTP connect message which will be propagated to the driver
}
}
#endif
headerSize = UnsafeUtility.SizeOf<UdpCHeader>();
var keys = Connections.GetKeyArray(Allocator.Temp);
WebSocketPacket packet = default;
for (int connectionIndex = 0; connectionIndex < keys.Length; ++connectionIndex)
{
var address = keys[connectionIndex];
var connection = Connections[address];
#if !UNITY_WEBGL
// Process any pending partial send
if (connection.PendingSendPacket.DataLength > 0)
{
var error = default(ErrorState);
var count = (int) Binding.Baselib_Socket_TCP_Send(
connection.Socket,
(IntPtr)connection.PendingSendPacket.Data,
(uint)connection.PendingSendPacket.DataLength,
&error);
if (count == connection.PendingSendPacket.DataLength)
{
connection.PendingSendPacket.DataLength = 0;
Connections[address] = connection;
}
else if (count > 0)
{
UnsafeUtility.MemMove(connection.PendingSendPacket.Data, connection.PendingSendPacket.Data + count, connection.PendingSendPacket.DataLength - count);
connection.PendingSendPacket.DataLength -= count;
Connections[address] = connection;
}
}
#endif
// Detect if the driver has removed this connection and clean up in case we missed a disconnect or timeout
if ((connection.ConnectState&ConnectionKnownByDriver) != 0 && !Receiver.IsAddressUsed(address))
{
#if !UNITY_WEBGL
if ((connection.ConnectState&ConnectionStateMask) >= 2 && connection.PendingSendPacket.DataLength == 0)
{
if ((connection.ConnectState&ConnectionSentClose) == 0)
{
var error = default(ErrorState);
packet.ConstructClose(1000, (connection.ConnectState&ConnectionIsClient) != 0, rand.NextUInt());
var count = (int)Binding.Baselib_Socket_TCP_Send(
connection.Socket,
(IntPtr)packet.Data,
(uint)packet.DataLength,
&error);
if (count != packet.DataLength || error.code != ErrorCode.Success)
{
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
}
else
{
connection.ConnectState |= ConnectionSentClose;
Connections[address] = connection;
}
}
}
else
#endif
{
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
continue;
}
}
#if !UNITY_WEBGL
if ((connection.ConnectState&ConnectionStateMask) == 3)
{
// Client waiting for server to close connection
// If server did not close the connection within 30 sec the client can close it
if (connection.CloseTimeStamp - Receiver.LastUpdateTime > 30*1000)
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
UnityEngine.Debug.LogWarning("Server did not close the socket fast enough, closing it on the client");
#endif
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
}
// Read and throw away just to check for disconnect messages, if more data was sent after websoket close - just close the socket
var error = default(ErrorState);
var receivedBytes = Binding.Baselib_Socket_TCP_Recv(connection.Socket, (IntPtr)packet.Data, (uint)(math.min(connection.RemainingPacketSize, NetworkParameterConstants.MTU)), &error);
if (Binding.Baselib_Socket_TCP_Recv(connection.Socket, (IntPtr)packet.Data, (uint)(math.min(connection.RemainingPacketSize, NetworkParameterConstants.MTU)), &error) > 0
|| error.code != ErrorCode.Success)
{
// Disconnected
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
}
continue;
}
if ((connection.ConnectState&ConnectionSentClose) != 0)
{
// If we sent a close request and did not receive a reply within 30 sec just close the connection
if (connection.CloseTimeStamp == 0)
{
connection.CloseTimeStamp = Receiver.LastUpdateTime;
Connections[address] = connection;
}
else if (connection.CloseTimeStamp - Receiver.LastUpdateTime > 30*1000)
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
UnityEngine.Debug.LogWarning("Remote end did not send a websocket close handshake fast enough, closing socket");
#endif
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
continue;
}
}
#endif
if ((connection.ConnectState&ConnectionStateMask) == 0)
{
var conState = WebSocketIsConnected(connection.Socket
#if !UNITY_WEBGL
, address, connection.Key0, connection.Key1, connection.Key2, connection.Key3
#endif
);
if (conState == 0)
continue;
if (conState < 0)
{
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
continue;
}
++connection.ConnectState;
Connections[address] = connection;
}
if ((connection.ConnectState&ConnectionStateMask) == 1)
{
#if !UNITY_WEBGL
var conState = WebSocketIsHandshakeComplete(ref connection);
Connections[address] = connection;
if (conState == 0)
continue;
if (conState < 0)
{
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
continue;
}
#endif
++connection.ConnectState;
Connections[address] = connection;
}
#if UNITY_WEBGL
while ((packet.DataLength = WebSocketRecv(connection.Socket, (IntPtr)packet.Data, NetworkParameterConstants.MTU)) != 0)
{
if (packet.DataLength < 0)
{
// Disconnected
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
break;
}
if (!CopyToStream(address, packet.Data, packet.DataLength))
{
Connections[address] = connection;
break;
}
connection.ConnectState |= ConnectionKnownByDriver;
}
#else
bool pendingData = true;
while (pendingData)
{
// Time to read
if (connection.RemainingPacketSize > 0 && connection.PacketStart > 0)
{
var error = default(ErrorState);
var receivedBytes = Binding.Baselib_Socket_TCP_Recv(connection.Socket, (IntPtr)connection.Packet.Data+connection.Packet.DataLength, (uint)connection.RemainingPacketSize, &error);
pendingData = receivedBytes>0;
if (error.code != ErrorCode.Success)
{
// Disconnected
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
break;
}
connection.RemainingPacketSize -= receivedBytes;
connection.Packet.DataLength += (int)receivedBytes;
if (connection.RemainingPacketSize == 0)
{
if ((connection.Packet.Data[1]&0x80)!=0)
{
var maskBytes = connection.Packet.Data+connection.HeaderSize-4;
var wsPayload = connection.Packet.Data+connection.PacketStart;
int maskLen = connection.Packet.DataLength - connection.PacketStart;
for (int i = 0; i < maskLen; ++i)
wsPayload[i] = (byte)(wsPayload[i]^maskBytes[i&3]);
}
connection.HeaderSize = 0;
connection.PacketStart = 0;
// This was not the final fragment, so wait for more
if ((connection.Packet.Data[0]&0x80) == 0)
{
Connections[address] = connection;
continue;
}
if (!CopyToStream(address, connection.Packet.Data + WebSocketMaxHeaderSize, connection.Packet.DataLength - WebSocketMaxHeaderSize))
{
Connections[address] = connection;
break;
}
connection.ConnectState |= ConnectionKnownByDriver;
}
}
else if (connection.RemainingPacketSize > 0)
{
// Read and throw away
var error = default(ErrorState);
var receivedBytes = Binding.Baselib_Socket_TCP_Recv(connection.Socket, (IntPtr)packet.Data, (uint)(math.min(connection.RemainingPacketSize, NetworkParameterConstants.MTU)), &error);
pendingData = receivedBytes>0;
if (error.code != ErrorCode.Success)
{
// Disconnected
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
break;
}
connection.RemainingPacketSize -= receivedBytes;
}
else
{
// No remaining packet or skip size, so we need to read a new frame header
var error = default(ErrorState);
if (connection.HeaderSize < 2)
{
int receivedBytes = (int)Binding.Baselib_Socket_TCP_Recv(connection.Socket, (IntPtr)connection.Packet.Data + connection.HeaderSize, (uint)(2 - connection.HeaderSize), &error);
pendingData = receivedBytes>0;
if (error.code != ErrorCode.Success)
{
// Disconnected
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
break;
}
connection.HeaderSize += receivedBytes;
if (connection.HeaderSize < 2)
{
Connections[address] = connection;
continue;
}
}
// Calculate the header size
int payloadByte = connection.Packet.Data[1]&0x7f;
var wsHeaderSize = 2;
if ((connection.Packet.Data[1]&0x80) != 0)
wsHeaderSize += 4;
if (payloadByte == 126)
wsHeaderSize += 2;
else if (payloadByte == 127)
wsHeaderSize += 8;
if (connection.HeaderSize < wsHeaderSize)
{
int receivedBytes = (int)Binding.Baselib_Socket_TCP_Recv(connection.Socket, (IntPtr)connection.Packet.Data + connection.HeaderSize, (uint)(wsHeaderSize - connection.HeaderSize), &error);
pendingData = receivedBytes>0;
if (error.code != ErrorCode.Success)
{
// Disconnected
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
break;
}
connection.HeaderSize += receivedBytes;
if (connection.HeaderSize < wsHeaderSize)
{
Connections[address] = connection;
continue;
}
}
// Full header is available - figure out how big the payload is
ulong payloadSize = 0;
if (payloadByte == 127)
{
payloadSize = ((ulong)connection.Packet.Data[6]<<56) + ((ulong)connection.Packet.Data[7]<<48) +
((ulong)connection.Packet.Data[6]<<40) + ((ulong)connection.Packet.Data[7]<<32) +
((ulong)connection.Packet.Data[6]<<24) + ((ulong)connection.Packet.Data[7]<<16) +
((ulong)connection.Packet.Data[8]<<8) + (ulong)connection.Packet.Data[9]; }
else if (payloadByte == 126)
{
payloadSize = ((ulong)connection.Packet.Data[2]<<8) + connection.Packet.Data[3];
}
else
payloadSize = (ulong)payloadByte;
var masked = (connection.Packet.Data[1] & 0x80) != 0;
var isClient = (connection.ConnectState&ConnectionIsClient) != 0;
// Receiving a masked message on the client is an error, receiving an unmasked message on the server is an error
if ((connection.Packet.Data[0] & 0x70) != 0 || masked == isClient)
{
// Bad header
#if ENABLE_UNITY_COLLECTIONS_CHECKS
if ((connection.Packet.Data[0] & 0x70) != 0)
UnityEngine.Debug.LogWarning("Received message with invalid reserved header bits");
if (masked == isClient)
UnityEngine.Debug.LogWarning("Received message with unexpected masking");
#endif
if ((connection.ConnectState&ConnectionSentClose) == 0 && connection.PendingSendPacket.DataLength == 0)
{
packet.ConstructClose(1002, (connection.ConnectState&ConnectionIsClient) != 0, rand.NextUInt());
Binding.Baselib_Socket_TCP_Send(
connection.Socket,
(IntPtr)packet.Data,
(uint)packet.DataLength,
&error);
}
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
break;
}
var opcode = connection.Packet.Data[0] & 0xf;
connection.HeaderSize = 0;
connection.RemainingPacketSize = payloadSize;
if (opcode == 0)
{
// Continuation
// validate that there is an actual packet to continue and that it's binary
// and that the full packet fits in an MTU
if (connection.Packet.DataLength < WebSocketMaxHeaderSize ||
(ulong)connection.Packet.DataLength + payloadSize > (ulong)NetworkParameterConstants.MTU)
{
connection.Packet.DataLength = 0;
}
else
{
connection.PacketStart = connection.Packet.DataLength;
connection.HeaderSize = wsHeaderSize;
}
}
else if (opcode == 2)
{
// Binary
// if dataLength is > 0 we probably never got the final part of a message which is a protocol error - but utp is not reliable so just drop it
// Reset the packet length, otherwise we'll just append data to it
if (payloadSize <= (ulong)NetworkParameterConstants.MTU)
{
connection.Packet.DataLength = WebSocketMaxHeaderSize;
connection.PacketStart = connection.Packet.DataLength;
connection.HeaderSize = wsHeaderSize;
}
}
else if (opcode == 8)
{
// Close
if ((connection.ConnectState&ConnectionSentClose) == 0)
{
// FIXME: should echo the status code
int count = -1;
if (connection.PendingSendPacket.DataLength == 0)
{
packet.ConstructClose(1000, (connection.ConnectState&ConnectionIsClient) != 0, rand.NextUInt());
count = (int)Binding.Baselib_Socket_TCP_Send(
connection.Socket,
(IntPtr)packet.Data,
(uint)packet.DataLength,
&error);
}
if (count != packet.DataLength || error.code != ErrorCode.Success)
{
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
break;
}
connection.ConnectState |= ConnectionSentClose;
}
if (isClient)
{
connection.ConnectState = (connection.ConnectState&(~ConnectionStateMask)) | 3;
connection.CloseTimeStamp = Receiver.LastUpdateTime;
Connections[address] = connection;
}
else
{
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
}
break;
}
else if (opcode == 9)
{
// Ping
if ((connection.ConnectState&ConnectionSentClose) == 0 && connection.PendingSendPacket.DataLength == 0)
{
// FIXME: should echo the ping payload
packet.ConstructPong(null, 0, (connection.ConnectState&ConnectionIsClient) != 0, rand.NextUInt());
var count = (int)Binding.Baselib_Socket_TCP_Send(
connection.Socket,
(IntPtr)packet.Data,
(uint)packet.DataLength,
&error);
if (count != 0 && count != packet.DataLength)
{
// Backup the pending packet for sending later
connection.PendingSendPacket.DataLength = packet.DataLength - count;
UnsafeUtility.MemCpy(connection.PendingSendPacket.Data, packet.Data + count, connection.PendingSendPacket.DataLength);
}
}
}
else if (opcode == 10)
{
// Pong
}
else
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
UnityEngine.Debug.LogWarning("Received message with an unsupported opcode");
#endif
// Unsupported opcode
if ((connection.ConnectState&ConnectionSentClose) == 0 && connection.PendingSendPacket.DataLength == 0)
{
packet.ConstructClose((ushort)((opcode==1) ? 1003 : 1002), (connection.ConnectState&ConnectionIsClient) != 0, rand.NextUInt());
Binding.Baselib_Socket_TCP_Send(
connection.Socket,
(IntPtr)packet.Data,
(uint)packet.DataLength,
&error);
}
WebSocketDestroy(connection.Socket);
Connections.Remove(address);
break;
}
}
Connections[address] = connection;
}
#endif
}
}
}
#endregion
public JobHandle ScheduleReceive(NetworkPacketReceiver receiver, JobHandle dep)
{
var job = new ReceiveJob
{
Receiver = receiver,
Baselib = m_Baselib,
Connections = m_Connections,
rand = new Random((uint)Stopwatch.GetTimestamp())
};
return job.Schedule(dep);
}
#if !UNITY_WEBGL // this job is calling external js methods which currently does not work from burst
[BurstCompile]
#endif
unsafe struct SendJob : IJob
{
public NativeQueue<QueuedSendMessage> sendQueue;
[NativeDisableContainerSafetyRestriction]
public NativeArray<BaselibData> Baselib;
[NativeDisableContainerSafetyRestriction]
public NativeHashMap<NetworkInterfaceEndPoint, ConnectionState> Connections;
public Random rand;
public void Execute()
{
var baselib = Baselib[0];
#if !UNITY_WEBGL
WebSocketPacket packet = default;
#endif
while (sendQueue.TryDequeue(out var msg))
{
if (msg.DataLength < UnsafeUtility.SizeOf<UdpCHeader>())
continue;
bool hasConnection = Connections.TryGetValue(msg.Dest, out var state);
var header = (UdpCHeader*)msg.Data;
if (header->Type == (byte)UdpCProtocol.ConnectionRequest)
{
if (hasConnection && (state.ConnectState&ConnectionSentClose) != 0)
{
// Reconnecting while the connection is still being closed, just force close it
WebSocketDestroy(state.Socket);
Connections.Remove(msg.Dest);
hasConnection = false;
}
if (!hasConnection)
{
Binding.Baselib_NetworkAddress* address = (Binding.Baselib_NetworkAddress*)msg.Dest.data;
#if UNITY_WEBGL
var addressString = NetworkEndPoint.AddressToString(*address);
WebSocketCreate(s_NextSocketId, (IntPtr)addressString.GetUnsafePtr(), addressString.Length, (IntPtr)msg.Data, msg.DataLength);
var conState = new ConnectionState
{
Socket = s_NextSocketId,
ConnectState = ConnectionKnownByDriver | ConnectionIsClient
};
++s_NextSocketId;
Connections.TryAdd(msg.Dest, conState);
#else
var error = default(ErrorState);
var socket = Binding.Baselib_Socket_Create(
(Binding.Baselib_NetworkAddress_Family)address->family, Binding.Baselib_Socket_Protocol.TCP,
&error);
if (error.code != ErrorCode.Success)
continue;
Binding.Baselib_Socket_TCP_Connect(socket, address, Binding.Baselib_NetworkAddress_AddressReuse.Allow, &error);
if (error.code != ErrorCode.Success)
{
Binding.Baselib_Socket_Close(socket);
continue;
}
var conState = new ConnectionState
{
Socket = socket,
ConnectState = ConnectionKnownByDriver | ConnectionIsClient,
Key0 = rand.NextUInt(),
Key1 = rand.NextUInt(),
Key2 = rand.NextUInt(),
Key3 = rand.NextUInt()
};
conState.Packet.ConstructBinary(msg.Data, msg.DataLength, true, rand.NextUInt());
Connections.TryAdd(msg.Dest, conState);
#endif
}
}
else if (hasConnection)
{
if ((state.ConnectState&ConnectionSentClose) != 0)
continue;
#if UNITY_WEBGL
WebSocketSend(state.Socket, (IntPtr)msg.Data, msg.DataLength);
if (header->Type == (byte)UdpCProtocol.Disconnect)
{
WebSocketDestroy(state.Socket);
Connections.Remove(msg.Dest);
}
#else
var error = default(ErrorState);
int count = 0;
if (state.PendingSendPacket.DataLength == 0)
{
packet.ConstructBinary(msg.Data, msg.DataLength, (state.ConnectState&ConnectionIsClient) != 0, rand.NextUInt());
count = (int) Binding.Baselib_Socket_TCP_Send(
state.Socket,
(IntPtr)packet.Data,
(uint)packet.DataLength,
&error);
if (count != 0 && count != packet.DataLength)
{
// Backup the pending packet for sending later
state.PendingSendPacket.DataLength = packet.DataLength - count;
UnsafeUtility.MemCpy(state.PendingSendPacket.Data, packet.Data + count, state.PendingSendPacket.DataLength);
Connections[msg.Dest] = state;
}
}
if (header->Type == (byte)UdpCProtocol.Disconnect)
{
count = -1;
if (state.PendingSendPacket.DataLength == 0)
{
packet.ConstructClose(1000, (state.ConnectState&ConnectionIsClient) != 0, rand.NextUInt());
count = (int)Binding.Baselib_Socket_TCP_Send(
state.Socket,
(IntPtr)packet.Data,
(uint)packet.DataLength,
&error);
}
if (count != packet.DataLength || error.code != ErrorCode.Success)
{
WebSocketDestroy(state.Socket);
Connections.Remove(msg.Dest);
}
else
{
state.ConnectState |= ConnectionSentClose;
Connections[msg.Dest] = state;
}
}
#endif
}
}
}
}
public JobHandle ScheduleSend(NativeQueue<QueuedSendMessage> sendQueue, JobHandle dep)
{
var sendJob = new SendJob
{
sendQueue = sendQueue,
Baselib = m_Baselib,
Connections = m_Connections,
rand = new Random((uint)Stopwatch.GetTimestamp())
};
return sendJob.Schedule(dep);
}
/// <summary>
/// Binds the BaselibNetworkInterface to the endpoint passed.
/// </summary>
/// <param name="endpoint">A valid ipv4 or ipv6 address</param>
/// <value>int</value>
public unsafe int Bind(NetworkInterfaceEndPoint endpoint)
{
var baselib = m_Baselib[0];
baselib.BoundAddress = endpoint;
m_Baselib[0] = baselib;
return 0;
}
public unsafe int Listen()
{
#if UNITY_WEBGL
throw new InvalidOperationException("WebGL does not support listening for connections");
#else
var baselib = m_Baselib[0];
var error = default(ErrorState);
Binding.Baselib_NetworkAddress* address = (Binding.Baselib_NetworkAddress*)baselib.BoundAddress.data;
baselib.ListenSocket = Binding.Baselib_Socket_Create(
(Binding.Baselib_NetworkAddress_Family)address->family, Binding.Baselib_Socket_Protocol.TCP,
&error);
if (error.code != ErrorCode.Success)
return (int) error.code == -1 ? -1 : -(int) error.code;
Binding.Baselib_Socket_Bind(baselib.ListenSocket, address, Binding.Baselib_NetworkAddress_AddressReuse.Allow, &error);
if (error.code == ErrorCode.Success)
{
Binding.Baselib_Socket_TCP_Listen(baselib.ListenSocket, &error);
}
if (error.code != ErrorCode.Success)
{
Binding.Baselib_Socket_Close(baselib.ListenSocket);
return (int) error.code == -1 ? -1 : -(int) error.code;
}
// Update the bound address
Binding.Baselib_Socket_GetAddress(baselib.ListenSocket, address, &error);
#if ENABLE_UNITY_COLLECTIONS_CHECKS && !UNITY_WEBGL
AllSockets.OpenSockets.Add(new SocketList.SocketId
{socket = baselib.ListenSocket});
#endif
m_Baselib[0] = baselib;
return 0;
#endif
}
static TransportFunctionPointer<NetworkSendInterface.BeginSendMessageDelegate> BeginSendMessageFunctionPointer = new TransportFunctionPointer<NetworkSendInterface.BeginSendMessageDelegate>(BeginSendMessage);
static TransportFunctionPointer<NetworkSendInterface.EndSendMessageDelegate> EndSendMessageFunctionPointer = new TransportFunctionPointer<NetworkSendInterface.EndSendMessageDelegate>(EndSendMessage);
static TransportFunctionPointer<NetworkSendInterface.AbortSendMessageDelegate> AbortSendMessageFunctionPointer = new TransportFunctionPointer<NetworkSendInterface.AbortSendMessageDelegate>(AbortSendMessage);
public unsafe NetworkSendInterface CreateSendInterface()
{
return new NetworkSendInterface
{
BeginSendMessage = BeginSendMessageFunctionPointer,
EndSendMessage = EndSendMessageFunctionPointer,
AbortSendMessage = AbortSendMessageFunctionPointer,
UserData = (IntPtr)m_Baselib.GetUnsafePtr()
};
}
[BurstCompile(DisableDirectCall = true)]
[AOT.MonoPInvokeCallback(typeof(NetworkSendInterface.BeginSendMessageDelegate))]
private static unsafe int BeginSendMessage(out NetworkInterfaceSendHandle handle, IntPtr userData, int requiredPayloadSize)
{
handle.id = 0;
handle.size = 0;
handle.capacity = requiredPayloadSize;
handle.data = (IntPtr)UnsafeUtility.Malloc(handle.capacity, 8, Allocator.Temp);
handle.flags = default;
return 0;
}
[BurstCompile(DisableDirectCall = true)]
[AOT.MonoPInvokeCallback(typeof(NetworkSendInterface.EndSendMessageDelegate))]
private static unsafe int EndSendMessage(ref NetworkInterfaceSendHandle handle, ref NetworkInterfaceEndPoint address, IntPtr userData, ref NetworkSendQueueHandle sendQueueHandle)
{
var sendQueue = sendQueueHandle.FromHandle();
var msg = default(QueuedSendMessage);
msg.Dest = address;
msg.DataLength = handle.size;
UnsafeUtility.MemCpy(msg.Data, (void*)handle.data, handle.size);
sendQueue.Enqueue(msg);
return handle.size;
}
[BurstCompile(DisableDirectCall = true)]
[AOT.MonoPInvokeCallback(typeof(NetworkSendInterface.AbortSendMessageDelegate))]
private static unsafe void AbortSendMessage(ref NetworkInterfaceSendHandle handle, IntPtr userData)
{
}
}
}