|
|
|
using System;
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.IO;
|
|
|
|
using System.Net.Sockets;
|
|
|
|
using System.Text;
|
|
|
|
using UMC.Net;
|
|
|
|
|
|
|
|
namespace UMC.Host
|
|
|
|
{
|
|
|
|
|
|
|
|
public class NetWebSocket : UMC.Net.MimeRequest, IDisposable
|
|
|
|
{
|
|
|
|
NetWriteData _writer;
|
|
|
|
Action _close;
|
|
|
|
Guid _device;
|
|
|
|
public NetWebSocket(NetWriteData writeData, Guid device, Action close)
|
|
|
|
{
|
|
|
|
this._writer = writeData;
|
|
|
|
this._close = close;
|
|
|
|
this._device = device;
|
|
|
|
if (_WebSockets.TryGetValue(device, out var webSocket))
|
|
|
|
{
|
|
|
|
webSocket.Dispose();
|
|
|
|
}
|
|
|
|
_WebSockets[_device] = this;
|
|
|
|
}
|
|
|
|
public static void Send(Guid device, String msg)
|
|
|
|
{
|
|
|
|
if (_WebSockets.TryGetValue(device, out var webSocket))
|
|
|
|
{
|
|
|
|
webSocket.Send(msg);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
internal static ConcurrentDictionary<Guid, NetWebSocket> _WebSockets = new ConcurrentDictionary<Guid, NetWebSocket>();
|
|
|
|
|
|
|
|
byte[] _buffers = Array.Empty<byte>();
|
|
|
|
int _buferSize = 0;
|
|
|
|
|
|
|
|
void Send(String msg)
|
|
|
|
{
|
|
|
|
var bs = new List<byte>();
|
|
|
|
bs.Add(0x81);
|
|
|
|
var by = System.Text.Encoding.UTF8.GetBytes(msg);
|
|
|
|
if (by.Length < 126)
|
|
|
|
{
|
|
|
|
bs.Add((byte)by.Length);
|
|
|
|
}
|
|
|
|
else if (by.Length < UInt16.MaxValue)
|
|
|
|
{
|
|
|
|
bs.Add(126);
|
|
|
|
UInt16 v = (UInt16)by.Length;
|
|
|
|
var bx = new byte[2];
|
|
|
|
BitConverter.TryWriteBytes(bx, v);
|
|
|
|
bs.AddRange(bx);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bs.Add(127);
|
|
|
|
ulong v = (ulong)by.Length;
|
|
|
|
var bx = new byte[4];
|
|
|
|
BitConverter.TryWriteBytes(bx, v);
|
|
|
|
bs.AddRange(bx);
|
|
|
|
}
|
|
|
|
bs.AddRange(by);
|
|
|
|
|
|
|
|
this._writer(bs.ToArray(), 0, bs.Count);
|
|
|
|
|
|
|
|
}
|
|
|
|
public override void Receive(byte[] buffer, int offset, int size)
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
if (_buferSize > 0)
|
|
|
|
{
|
|
|
|
var len = size + _buferSize;
|
|
|
|
if (len > _buffers.Length)
|
|
|
|
{
|
|
|
|
if (_buffers.Length < len)
|
|
|
|
{
|
|
|
|
var bs = System.Buffers.ArrayPool<byte>.Shared.Rent(len);
|
|
|
|
Array.Copy(_buffers, 0, _buffers, 0, _buferSize);
|
|
|
|
if (_buffers.Length > 0)
|
|
|
|
{
|
|
|
|
System.Buffers.ArrayPool<byte>.Shared.Return(_buffers);
|
|
|
|
}
|
|
|
|
_buffers = bs;
|
|
|
|
};
|
|
|
|
Array.Copy(buffer, offset, _buffers, _buferSize, size);
|
|
|
|
_buferSize += size;
|
|
|
|
Frame(buffer, 0, _buferSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Frame(buffer, offset, size);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void Frame(byte[] buffer, int offset, int size)
|
|
|
|
{
|
|
|
|
byte len = buffer[offset + 1];
|
|
|
|
int start = 2;
|
|
|
|
var isMask = len >> 7 == 1;
|
|
|
|
len <<= 1;
|
|
|
|
len >>= 1;
|
|
|
|
int length = len;//<<= 1;
|
|
|
|
switch (length)
|
|
|
|
{
|
|
|
|
case 126:
|
|
|
|
start += 2;
|
|
|
|
length = BitConverter.ToUInt16(buffer, buffer[2]);
|
|
|
|
break;
|
|
|
|
case 127:
|
|
|
|
start += 8;
|
|
|
|
length = Convert.ToInt32(BitConverter.ToUInt64(buffer, buffer[2]));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (isMask)
|
|
|
|
{
|
|
|
|
start += 4;
|
|
|
|
}
|
|
|
|
var opcode = buffer[offset];
|
|
|
|
opcode <<= 4;
|
|
|
|
opcode >>= 4;
|
|
|
|
if ((start + length) <= size)
|
|
|
|
{
|
|
|
|
if (isMask)
|
|
|
|
{
|
|
|
|
var mask = new byte[4];
|
|
|
|
Array.Copy(buffer, offset + start - 4, mask, 0, 4);
|
|
|
|
|
|
|
|
for (uint i = 0; i < length; i++)
|
|
|
|
{
|
|
|
|
buffer[offset + start + i] ^= mask[i % 4];
|
|
|
|
}
|
|
|
|
//;
|
|
|
|
switch (opcode)
|
|
|
|
{
|
|
|
|
case 0x00:
|
|
|
|
break;
|
|
|
|
case 0x01:
|
|
|
|
case 0x02:
|
|
|
|
break;
|
|
|
|
case 0x08:
|
|
|
|
this.Dispose();
|
|
|
|
break;
|
|
|
|
case 0x89:
|
|
|
|
case 0x09:
|
|
|
|
|
|
|
|
// var byt2e = buffer[offset + 1];
|
|
|
|
buffer[offset + 1] = len;// (byte)(byt2e << 1 >> 1);
|
|
|
|
buffer[offset] = 0x8a;
|
|
|
|
Array.Copy(buffer, offset + start, buffer, offset + start - 4, length);
|
|
|
|
this._writer(buffer, offset, length + start - 4);
|
|
|
|
break;
|
|
|
|
case 0x0A:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (opcode)
|
|
|
|
{
|
|
|
|
case 0x00:
|
|
|
|
break;
|
|
|
|
case 0x01:
|
|
|
|
case 0x02:
|
|
|
|
break;
|
|
|
|
case 0x08:
|
|
|
|
this.Dispose();
|
|
|
|
break;
|
|
|
|
case 0x89:
|
|
|
|
case 0x09:
|
|
|
|
buffer[offset] = 0x8a;
|
|
|
|
Array.Copy(buffer, offset + start, buffer, offset + start - 4, length);
|
|
|
|
this._writer(buffer, offset, length + start - 4);
|
|
|
|
break;
|
|
|
|
case 0x0A:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
_buferSize = 0;
|
|
|
|
if (start + length < size)
|
|
|
|
{
|
|
|
|
Frame(buffer, offset + start + length, size - start - length);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (_buferSize == 0)
|
|
|
|
{
|
|
|
|
if (_buffers.Length < size)
|
|
|
|
{
|
|
|
|
if (_buffers.Length > 0)
|
|
|
|
{
|
|
|
|
System.Buffers.ArrayPool<byte>.Shared.Return(_buffers);
|
|
|
|
}
|
|
|
|
_buffers = System.Buffers.ArrayPool<byte>.Shared.Rent(size);
|
|
|
|
};
|
|
|
|
_buferSize = size;
|
|
|
|
Array.Copy(buffer, offset, _buffers, 0, size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void Header(byte[] data, int offset, int size)
|
|
|
|
{
|
|
|
|
// throw new NotImplementedException();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal static string GetSecWebSocketAcceptString(string secWebSocketKey)
|
|
|
|
{//258EAFA5-E914-47DA-95CA-C5AB0DC85B11
|
|
|
|
string s = secWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
|
|
byte[] bytes = Encoding.UTF8.GetBytes(s);
|
|
|
|
byte[] inArray = System.Security.Cryptography.SHA1.HashData(bytes);
|
|
|
|
return Convert.ToBase64String(inArray);
|
|
|
|
}
|
|
|
|
|
|
|
|
// internal static bool ProcessWebSocketProtocolHeader(string clientSecWebSocketProtocol, out string acceptProtocol)
|
|
|
|
// {
|
|
|
|
// acceptProtocol = string.Empty;
|
|
|
|
// if (string.IsNullOrEmpty(clientSecWebSocketProtocol))
|
|
|
|
// {
|
|
|
|
// return false;
|
|
|
|
// }
|
|
|
|
// string[] array = clientSecWebSocketProtocol.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
|
|
|
// if (array.Length > 0)
|
|
|
|
// {
|
|
|
|
// acceptProtocol = array[0];
|
|
|
|
// }
|
|
|
|
// return true;
|
|
|
|
// }
|
|
|
|
|
|
|
|
internal static bool ValidateWebSocketHeaders(HttpMimeRequest context)
|
|
|
|
{
|
|
|
|
string text = context.Headers["Sec-WebSocket-Version"];
|
|
|
|
if (string.IsNullOrEmpty(text))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!string.Equals(text, "13", StringComparison.OrdinalIgnoreCase))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
string text2 = context.Headers["Sec-WebSocket-Key"];
|
|
|
|
if (!string.IsNullOrWhiteSpace(text2))
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
return Convert.FromBase64String(text2).Length == 16;
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
_WebSockets.TryRemove(this._device, out var _);
|
|
|
|
_close();
|
|
|
|
|
|
|
|
if (_buffers.Length > 0)
|
|
|
|
{
|
|
|
|
System.Buffers.ArrayPool<byte>.Shared.Return(_buffers);
|
|
|
|
}
|
|
|
|
// throw new NotImplementedException();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
abstract class WebSocket : UMC.Net.MimeRequest, IDisposable
|
|
|
|
{
|
|
|
|
public override bool IsWebSocket => true;
|
|
|
|
public abstract void Dispose();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class HttpWebSocket : WebSocket
|
|
|
|
{
|
|
|
|
|
|
|
|
public byte[] buffer = System.Buffers.ArrayPool<byte>.Shared.Rent(0x600);
|
|
|
|
Socket socket;
|
|
|
|
public HttpWebSocket(HttpMime mime, Socket stream)
|
|
|
|
{
|
|
|
|
this.mime = mime;
|
|
|
|
this.socket = stream;
|
|
|
|
SocketAsyncEventArgs eventArgs = new SocketAsyncEventArgs();
|
|
|
|
eventArgs.SetBuffer(buffer);
|
|
|
|
eventArgs.Completed += Http;
|
|
|
|
if (!this.socket.ReceiveAsync(eventArgs))
|
|
|
|
{
|
|
|
|
ProcessReceive(eventArgs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
HttpMime mime;
|
|
|
|
|
|
|
|
private void Http(object sender, SocketAsyncEventArgs e)
|
|
|
|
{
|
|
|
|
switch (e.LastOperation)
|
|
|
|
{
|
|
|
|
case SocketAsyncOperation.Receive:
|
|
|
|
ProcessReceive(e);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private void ProcessReceive(SocketAsyncEventArgs e)
|
|
|
|
{
|
|
|
|
if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
this.mime.Write(buffer, e.Offset, e.BytesTransferred);
|
|
|
|
if (!socket.ReceiveAsync(e))
|
|
|
|
{
|
|
|
|
ProcessReceive(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
this.Dispose();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.Dispose();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void Receive(byte[] buffer, int offset, int size)
|
|
|
|
{
|
|
|
|
socket.Send(buffer, offset, size, SocketFlags.None);
|
|
|
|
}
|
|
|
|
protected override void Header(byte[] data, int offset, int size)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
public override void Dispose()
|
|
|
|
{
|
|
|
|
this.Dispose();
|
|
|
|
try
|
|
|
|
{
|
|
|
|
socket.Shutdown(SocketShutdown.Both);
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
socket.Close();
|
|
|
|
System.Buffers.ArrayPool<byte>.Shared.Return(buffer);
|
|
|
|
buffer = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|