|
@@ -1,18 +1,23 @@
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
+using System.IO;
|
|
|
using System.Linq;
|
|
|
+using System.Net.WebSockets;
|
|
|
using System.Threading;
|
|
|
using System.Threading.Tasks;
|
|
|
using InABox.Clients;
|
|
|
using InABox.Core;
|
|
|
-using WebSocketSharp;
|
|
|
-using ErrorEventArgs = WebSocketSharp.ErrorEventArgs;
|
|
|
|
|
|
namespace InABox.Rpc
|
|
|
{
|
|
|
public class RpcClientSocketTransport : RpcClientTransport, IDisposable
|
|
|
{
|
|
|
- private WebSocket? _socket;
|
|
|
+ private ClientWebSocket? _socket;
|
|
|
+ private Task? readTask;
|
|
|
+
|
|
|
+ private string? _host;
|
|
|
+ private object _sendLock = new object();
|
|
|
+ private CancellationTokenSource _tokenSource = new CancellationTokenSource();
|
|
|
|
|
|
private string[] _urls;
|
|
|
|
|
@@ -20,48 +25,104 @@ namespace InABox.Rpc
|
|
|
{
|
|
|
_urls = urls;
|
|
|
}
|
|
|
-
|
|
|
- private void Socket_OnOpen(object? sender, EventArgs e)
|
|
|
- {
|
|
|
- DoOpen();
|
|
|
- }
|
|
|
-
|
|
|
- private void Socket_OnMessage(object? sender, MessageEventArgs e)
|
|
|
- {
|
|
|
- RpcMessage? message = null;
|
|
|
- if (e.IsBinary && (e.RawData != null))
|
|
|
- message = Serialization.ReadBinary<RpcMessage>(e.RawData, BinarySerializationSettings.Latest);
|
|
|
- else if (e.IsText && !string.IsNullOrWhiteSpace(e.Data))
|
|
|
- message = Serialization.Deserialize<RpcMessage>(e.Data);
|
|
|
-
|
|
|
- Accept(message);
|
|
|
- }
|
|
|
|
|
|
- private void Socket_OnClose(object? sender, CloseEventArgs e)
|
|
|
+ // Returns true if we are to continue the receive loop.
|
|
|
+ private bool DoReceive()
|
|
|
{
|
|
|
- DoClose(RpcTransportCloseEventType.Closed);
|
|
|
- }
|
|
|
+ if(_socket != null)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var buffer = new ArraySegment<byte>(new byte[1024]);
|
|
|
+ using (var ms = new MemoryStream())
|
|
|
+ {
|
|
|
+ WebSocketReceiveResult result;
|
|
|
+ do
|
|
|
+ {
|
|
|
+ var task = _socket.ReceiveAsync(buffer, _tokenSource.Token);
|
|
|
+ task.Wait();
|
|
|
+ result = task.Result;
|
|
|
|
|
|
- private void Socket_OnError(object? sender, ErrorEventArgs e)
|
|
|
- {
|
|
|
- DoException(e.Exception);
|
|
|
+ ms.Write(buffer.Array, buffer.Offset, result.Count);
|
|
|
+ } while (!result.EndOfMessage);
|
|
|
+
|
|
|
+ ms.Seek(0, SeekOrigin.Begin);
|
|
|
+
|
|
|
+ if (result.MessageType == WebSocketMessageType.Close)
|
|
|
+ {
|
|
|
+ if (result.CloseStatus == WebSocketCloseStatus.NormalClosure)
|
|
|
+ {
|
|
|
+ Task.Run(() =>
|
|
|
+ {
|
|
|
+ DoClose(RpcTransportCloseEventType.Closed);
|
|
|
+ });
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ DoException(new Exception(result.CloseStatusDescription));
|
|
|
+ Task.Run(() =>
|
|
|
+ {
|
|
|
+ DoClose(RpcTransportCloseEventType.Error);
|
|
|
+ });
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ RpcMessage? rpcMessage = null;
|
|
|
+ if (result.MessageType == WebSocketMessageType.Binary)
|
|
|
+ {
|
|
|
+ rpcMessage = Serialization.ReadBinary<RpcMessage>(ms, BinarySerializationSettings.Latest);
|
|
|
+ }
|
|
|
+ else if (result.MessageType == WebSocketMessageType.Text)
|
|
|
+ {
|
|
|
+ rpcMessage = Serialization.Deserialize<RpcMessage>(ms);
|
|
|
+ }
|
|
|
+ Accept(rpcMessage);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch(Exception e)
|
|
|
+ {
|
|
|
+ DoException(e);
|
|
|
+ if (!IsConnected())
|
|
|
+ {
|
|
|
+ Task.Run(() =>
|
|
|
+ {
|
|
|
+ DoClose(RpcTransportCloseEventType.Error);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
- private WebSocket? CreateSocket(string url, bool secure)
|
|
|
+ private ClientWebSocket? CreateSocket(string url, bool secure)
|
|
|
{
|
|
|
- WebSocket socket = null;
|
|
|
+ var client = new ClientWebSocket();
|
|
|
+
|
|
|
+ //WebsocketClient client;
|
|
|
+
|
|
|
+ //WebSocket socket = null;
|
|
|
var address = $"{(secure ? "wss" : "ws")}://{url}";
|
|
|
try
|
|
|
{
|
|
|
- socket = new WebSocket(address);
|
|
|
+ client.ConnectAsync(new Uri(address), _tokenSource.Token).Wait();
|
|
|
+ //socket = new WebSocket(address);
|
|
|
}
|
|
|
catch (Exception e)
|
|
|
{
|
|
|
+ client.Dispose();
|
|
|
return null;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ DoOpen();
|
|
|
+ _host = url;
|
|
|
+
|
|
|
// Time to wait before disconnect - the default meant that the client disconnected during debugging, since the ping would fail
|
|
|
- socket.WaitTime = TimeSpan.FromSeconds(20);
|
|
|
+ /*socket.WaitTime = TimeSpan.FromSeconds(20);
|
|
|
socket.OnOpen -= Socket_OnOpen;
|
|
|
socket.OnError -= Socket_OnError;
|
|
|
socket.OnClose -= Socket_OnClose;
|
|
@@ -77,17 +138,19 @@ namespace InABox.Rpc
|
|
|
socket.OnMessage += Socket_OnMessage;
|
|
|
|
|
|
return socket;
|
|
|
- }
|
|
|
- return null;
|
|
|
+ }*/
|
|
|
+ return client;
|
|
|
}
|
|
|
|
|
|
public override void Connect()
|
|
|
{
|
|
|
- List<Task<WebSocket>> tasks = new List<Task<WebSocket>>();
|
|
|
+ _socket?.Dispose();
|
|
|
+
|
|
|
+ var tasks = new List<Task<ClientWebSocket?>>();
|
|
|
foreach (var url in _urls)
|
|
|
{
|
|
|
- tasks.Add(Task<WebSocket>.Run(() => CreateSocket(url, true)));
|
|
|
- tasks.Add(Task<WebSocket>.Run(() => CreateSocket(url, false)));
|
|
|
+ tasks.Add(Task.Run(() => CreateSocket(url, true)));
|
|
|
+ tasks.Add(Task.Run(() => CreateSocket(url, false)));
|
|
|
}
|
|
|
while (tasks.Count > 0)
|
|
|
{
|
|
@@ -97,25 +160,40 @@ namespace InABox.Rpc
|
|
|
else
|
|
|
{
|
|
|
_socket = result.Result;
|
|
|
+
|
|
|
+ Task.Run(() =>
|
|
|
+ {
|
|
|
+ while (IsConnected())
|
|
|
+ {
|
|
|
+ if (!DoReceive())
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public override bool IsConnected() => _socket?.ReadyState == WebSocketState.Open;
|
|
|
- public override bool IsSecure() => _socket?.IsSecure == true;
|
|
|
+ public override bool IsConnected() => _socket?.State == WebSocketState.Open;
|
|
|
+ public override bool IsSecure() => false;
|
|
|
|
|
|
- public override String? ServerName() => _socket?.Url.Host;
|
|
|
+ public override String? ServerName() => _host;
|
|
|
|
|
|
public override void Disconnect()
|
|
|
{
|
|
|
- _socket?.Close(CloseStatusCode.Normal);
|
|
|
+ _socket?.CloseAsync(WebSocketCloseStatus.NormalClosure, "", _tokenSource.Token).Wait();
|
|
|
}
|
|
|
|
|
|
public override void Send(RpcMessage message)
|
|
|
{
|
|
|
- var buffer = message.WriteBinary(BinarySerializationSettings.Latest);
|
|
|
- _socket?.Send(buffer);
|
|
|
+ lock (_sendLock)
|
|
|
+ {
|
|
|
+ var buffer = message.WriteBinary(BinarySerializationSettings.Latest);
|
|
|
+ _socket?.SendAsync(buffer, WebSocketMessageType.Binary, true, _tokenSource.Token)?.Wait();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
protected override RpcClientTransport Clone() => new RpcClientSocketTransport(_urls);
|
|
@@ -124,6 +202,8 @@ namespace InABox.Rpc
|
|
|
{
|
|
|
if (IsConnected())
|
|
|
Disconnect();
|
|
|
+ _tokenSource.Cancel();
|
|
|
+ _socket?.Dispose();
|
|
|
}
|
|
|
}
|
|
|
}
|