|
@@ -12,10 +12,37 @@ namespace NTMiner.Ws {
|
|
private string _closeReason = string.Empty;
|
|
private string _closeReason = string.Empty;
|
|
private string _closeCode = string.Empty;
|
|
private string _closeCode = string.Empty;
|
|
private WebSocket _ws = null;
|
|
private WebSocket _ws = null;
|
|
- private bool _isOuterUserEnabled;
|
|
|
|
|
|
+ private string _wsServerIp = string.Empty;
|
|
|
|
+ private void NeedReWebSocket() {
|
|
|
|
+ _ws = null;
|
|
|
|
+ }
|
|
|
|
+ private bool IsOuterUserEnabled {
|
|
|
|
+ get {
|
|
|
|
+ switch (_appType) {
|
|
|
|
+ case NTMinerAppType.MinerClient:
|
|
|
|
+ return NTMinerRegistry.GetIsOuterUserEnabled();
|
|
|
|
+ case NTMinerAppType.MinerStudio:
|
|
|
|
+ return true;
|
|
|
|
+ default:
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ private string OuterUserId {
|
|
|
|
+ get {
|
|
|
|
+ switch (_appType) {
|
|
|
|
+ case NTMinerAppType.MinerClient:
|
|
|
|
+ return NTMinerRegistry.GetOuterUserId();
|
|
|
|
+ case NTMinerAppType.MinerStudio:
|
|
|
|
+ return RpcRoot.RpcUser.LoginName;
|
|
|
|
+ default:
|
|
|
|
+ return string.Empty;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
// 用来判断用户是否改变了outerUserId,如果改变了则需要关闭连接使用新用户重新连接,该值根据_appType的不同而不同,
|
|
// 用来判断用户是否改变了outerUserId,如果改变了则需要关闭连接使用新用户重新连接,该值根据_appType的不同而不同,
|
|
// MinerClient时_outerUserId是来自注册表的OuterUserId,MinerStudio时_outerUserId是RpcRoot.RpcUser.LoginName
|
|
// MinerClient时_outerUserId是来自注册表的OuterUserId,MinerStudio时_outerUserId是RpcRoot.RpcUser.LoginName
|
|
- private string _outerUserId = string.Empty;
|
|
|
|
|
|
+ private string _preOuterUserId = string.Empty;
|
|
private AESPassword _aesPassword;
|
|
private AESPassword _aesPassword;
|
|
|
|
|
|
#region ctor
|
|
#region ctor
|
|
@@ -38,30 +65,27 @@ namespace NTMiner.Ws {
|
|
}
|
|
}
|
|
}, typeof(VirtualRoot));
|
|
}, typeof(VirtualRoot));
|
|
VirtualRoot.BuildEventPath<Per20SecondEvent>("周期Ws Ping", LogEnum.None, path: message => {
|
|
VirtualRoot.BuildEventPath<Per20SecondEvent>("周期Ws Ping", LogEnum.None, path: message => {
|
|
- try {
|
|
|
|
- if (_ws != null && _ws.ReadyState == WebSocketState.Open) {
|
|
|
|
- // 或者_ws.IsAlive,因为_ws.IsAlive内部也是一个Ping,所以用Ping从而显式化这里有个网络请求
|
|
|
|
- Task.Factory.StartNew(() => {
|
|
|
|
- if (!_ws.Ping()) {
|
|
|
|
- _ws.CloseAsync(CloseStatusCode.Away);
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- catch (Exception e) {
|
|
|
|
- Logger.ErrorDebugLine(e);
|
|
|
|
|
|
+ if (_ws != null && _ws.ReadyState == WebSocketState.Open) {
|
|
|
|
+ // 或者_ws.IsAlive,因为_ws.IsAlive内部也是一个Ping,所以用Ping从而显式化这里有个网络请求
|
|
|
|
+ Task.Factory.StartNew(() => {
|
|
|
|
+ if (!_ws.Ping()) {
|
|
|
|
+ _ws.CloseAsync(CloseStatusCode.Away);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
}
|
|
}
|
|
}, typeof(VirtualRoot));
|
|
}, typeof(VirtualRoot));
|
|
VirtualRoot.BuildEventPath<AppExitEvent>("退出程序时关闭Ws连接", LogEnum.DevConsole, path: message => {
|
|
VirtualRoot.BuildEventPath<AppExitEvent>("退出程序时关闭Ws连接", LogEnum.DevConsole, path: message => {
|
|
_ws?.CloseAsync(CloseStatusCode.Normal, "客户端程序退出");
|
|
_ws?.CloseAsync(CloseStatusCode.Normal, "客户端程序退出");
|
|
}, this.GetType());
|
|
}, this.GetType());
|
|
|
|
+ /// 1,进程启动后第一次连接时;
|
|
|
|
+ NeedReWebSocket();
|
|
OpenOrCloseWs();
|
|
OpenOrCloseWs();
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
|
|
public bool IsOpen {
|
|
public bool IsOpen {
|
|
get {
|
|
get {
|
|
- if (!_isOuterUserEnabled) {
|
|
|
|
|
|
+ if (!IsOuterUserEnabled) {
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
return _ws != null && _ws.ReadyState == WebSocketState.Open;
|
|
return _ws != null && _ws.ReadyState == WebSocketState.Open;
|
|
@@ -75,85 +99,54 @@ namespace NTMiner.Ws {
|
|
protected abstract bool TryGetHandler(string messageType, out Action<Action<WsMessage>, WsMessage> handler);
|
|
protected abstract bool TryGetHandler(string messageType, out Action<Action<WsMessage>, WsMessage> handler);
|
|
protected abstract void UpdateWsStateAsync(string description, bool toOut);
|
|
protected abstract void UpdateWsStateAsync(string description, bool toOut);
|
|
|
|
|
|
- #region SendAsync
|
|
|
|
- public void SendAsync(WsMessage message) {
|
|
|
|
- if (!IsOpen) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- switch (_appType) {
|
|
|
|
- case NTMinerAppType.MinerClient:
|
|
|
|
- _ws.SendAsync(message.ToBytes(), null);
|
|
|
|
- break;
|
|
|
|
- case NTMinerAppType.MinerStudio:
|
|
|
|
- _ws.SendAsync(message.SignToBytes(RpcRoot.RpcUser.Password), null);
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- #endregion
|
|
|
|
-
|
|
|
|
#region StartOrCloseWs
|
|
#region StartOrCloseWs
|
|
|
|
+ /// <summary>
|
|
|
|
+ ///
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="isResetFailCount">
|
|
|
|
+ /// 当由于用户行为执行该方法时传true,用户行为有:点击界面上的重连按钮、由内网切换到外网时的登录
|
|
|
|
+ /// </param>
|
|
public void OpenOrCloseWs(bool isResetFailCount = false) {
|
|
public void OpenOrCloseWs(bool isResetFailCount = false) {
|
|
try {
|
|
try {
|
|
if (isResetFailCount) {
|
|
if (isResetFailCount) {
|
|
ResetFailCount();
|
|
ResetFailCount();
|
|
}
|
|
}
|
|
- switch (_appType) {
|
|
|
|
- case NTMinerAppType.MinerClient:
|
|
|
|
- _isOuterUserEnabled = NTMinerRegistry.GetIsOuterUserEnabled();
|
|
|
|
- break;
|
|
|
|
- case NTMinerAppType.MinerStudio:
|
|
|
|
- _isOuterUserEnabled = true;
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- _isOuterUserEnabled = false;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- string outerUserId;
|
|
|
|
- switch (_appType) {
|
|
|
|
- case NTMinerAppType.MinerClient:
|
|
|
|
- outerUserId = NTMinerRegistry.GetOuterUserId();
|
|
|
|
- break;
|
|
|
|
- case NTMinerAppType.MinerStudio:
|
|
|
|
- outerUserId = RpcRoot.RpcUser.LoginName;
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- outerUserId = string.Empty;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- if (!_isOuterUserEnabled) {
|
|
|
|
|
|
+ if (!IsOuterUserEnabled) {
|
|
ResetFailCount();
|
|
ResetFailCount();
|
|
if (_ws != null && _ws.ReadyState == WebSocketState.Open) {
|
|
if (_ws != null && _ws.ReadyState == WebSocketState.Open) {
|
|
_ws.CloseAsync(CloseStatusCode.Normal, "外网群控已禁用");
|
|
_ws.CloseAsync(CloseStatusCode.Normal, "外网群控已禁用");
|
|
}
|
|
}
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
+ string outerUserId = OuterUserId;
|
|
if (string.IsNullOrEmpty(outerUserId)) {
|
|
if (string.IsNullOrEmpty(outerUserId)) {
|
|
ResetFailCount();
|
|
ResetFailCount();
|
|
_ws?.CloseAsync(CloseStatusCode.Normal, "未填写用户");
|
|
_ws?.CloseAsync(CloseStatusCode.Normal, "未填写用户");
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- bool isUserIdChanged = _outerUserId != outerUserId;
|
|
|
|
|
|
+ bool isUserIdChanged = _preOuterUserId != outerUserId;
|
|
if (isUserIdChanged) {
|
|
if (isUserIdChanged) {
|
|
- _outerUserId = outerUserId;
|
|
|
|
|
|
+ _preOuterUserId = outerUserId;
|
|
}
|
|
}
|
|
- // 1,进程启动后第一次连接时
|
|
|
|
if (_ws == null) {
|
|
if (_ws == null) {
|
|
- NewWebSocket(webSocket => _ws = webSocket);
|
|
|
|
|
|
+ NewWebSocket();
|
|
}
|
|
}
|
|
else if (isUserIdChanged) {
|
|
else if (isUserIdChanged) {
|
|
ResetFailCount();
|
|
ResetFailCount();
|
|
if (_ws.ReadyState == WebSocketState.Open) {
|
|
if (_ws.ReadyState == WebSocketState.Open) {
|
|
- // 因为旧的用户名密码成连接过且用户名或密码变更了,那么关闭连接即可,无需立即再次连接因为一定连接不成功因为用户名密码不再正确。
|
|
|
|
string why = string.Empty;
|
|
string why = string.Empty;
|
|
if (isUserIdChanged) {
|
|
if (isUserIdChanged) {
|
|
why = ",因为用户变更";
|
|
why = ",因为用户变更";
|
|
}
|
|
}
|
|
|
|
+ // 因为连接处在打开中说明用户名正确,现在用户名变更了则极有可能用户名不再正确所以没必要立即做
|
|
|
|
+ // 因用户名变更而需要的重连操作。另外如果是用户名是由一个正确的用户名变更为另一个正确的用户名
|
|
|
|
+ // 的话应该立即重连,但由于不知道新用户名是否正确所以还是等下个周期自动重连吧这里就不立即重连了,
|
|
|
|
+ // 只需关闭当前连接即可。
|
|
_ws.CloseAsync(CloseStatusCode.Normal, $"关闭连接{why}");
|
|
_ws.CloseAsync(CloseStatusCode.Normal, $"关闭连接{why}");
|
|
}
|
|
}
|
|
else {
|
|
else {
|
|
- // 因为旧用户名密码没有成功连接过,说明旧用户名密码不正确,而现在用户名密码变更了,有可能变更正确了所以尝试连接一次。
|
|
|
|
|
|
+ // 因为旧用户名密码没有成功连接过,说明旧用户名密码不正确,而现在用户名密码变更了,
|
|
|
|
+ // 有可能变更正确了所以立即尝试连接一次。
|
|
ConnectAsync(_ws);
|
|
ConnectAsync(_ws);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -167,7 +160,7 @@ namespace NTMiner.Ws {
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
|
|
- #region GetWsDaemonState
|
|
|
|
|
|
+ #region GetState
|
|
public WsClientState GetState() {
|
|
public WsClientState GetState() {
|
|
var ws = _ws;
|
|
var ws = _ws;
|
|
string description = $"{_closeCode} {_closeReason}";
|
|
string description = $"{_closeCode} {_closeReason}";
|
|
@@ -181,6 +174,7 @@ namespace NTMiner.Ws {
|
|
var state = new WsClientState {
|
|
var state = new WsClientState {
|
|
Status = status,
|
|
Status = status,
|
|
Description = description,
|
|
Description = description,
|
|
|
|
+ WsServerIp = _wsServerIp,
|
|
NextTrySecondsDelay = NextTrySecondsDelay,
|
|
NextTrySecondsDelay = NextTrySecondsDelay,
|
|
LastTryOn = LastTryOn
|
|
LastTryOn = LastTryOn
|
|
};
|
|
};
|
|
@@ -188,8 +182,27 @@ namespace NTMiner.Ws {
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
|
|
|
|
+ #region SendAsync
|
|
|
|
+ public void SendAsync(WsMessage message) {
|
|
|
|
+ if (!IsOpen) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ switch (_appType) {
|
|
|
|
+ case NTMinerAppType.MinerClient:
|
|
|
|
+ _ws.SendAsync(message.ToBytes(), null);
|
|
|
|
+ break;
|
|
|
|
+ case NTMinerAppType.MinerStudio:
|
|
|
|
+ _ws.SendAsync(message.SignToBytes(RpcRoot.RpcUser.Password), null);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
#region 私有方法
|
|
#region 私有方法
|
|
#region NewWebSocket
|
|
#region NewWebSocket
|
|
|
|
+ private readonly object _wsLocker = new object();
|
|
/// <summary>
|
|
/// <summary>
|
|
/// 这是一次彻底的重连,重新获取服务器地址的重连,以下情况下才会调用该方法:
|
|
/// 这是一次彻底的重连,重新获取服务器地址的重连,以下情况下才会调用该方法:
|
|
/// 1,进程启动后第一次连接时;
|
|
/// 1,进程启动后第一次连接时;
|
|
@@ -197,159 +210,209 @@ namespace NTMiner.Ws {
|
|
/// 3,收到服务器的WsMessage.ReGetServerAddress类型的消息时;
|
|
/// 3,收到服务器的WsMessage.ReGetServerAddress类型的消息时;
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <returns></returns>
|
|
- private void NewWebSocket(Action<WebSocket> callback) {
|
|
|
|
- RpcRoot.OfficialServer.WsServerNodeService.GetNodeAddressAsync(NTMinerRegistry.GetClientId(_appType), _outerUserId, (response, ex) => {
|
|
|
|
|
|
+ private void NewWebSocket() {
|
|
|
|
+ if (_ws != null) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ RpcRoot.OfficialServer.WsServerNodeService.GetNodeAddressAsync(NTMinerRegistry.GetClientId(_appType), _preOuterUserId, (response, ex) => {
|
|
|
|
+ if (_ws == null) {
|
|
|
|
+ lock (_wsLocker) {
|
|
|
|
+ if (_ws == null) {
|
|
|
|
+ LastTryOn = DateTime.Now;
|
|
|
|
+ _wsServerIp = string.Empty;
|
|
|
|
+
|
|
|
|
+ #region 网络错误或没有获取到WsServer节点时退出
|
|
|
|
+ if (!response.IsSuccess()) {
|
|
|
|
+ // 没有获取到该矿机的WsServer分片节点,递增失败次数以增大重试延迟周期
|
|
|
|
+ IncreaseFailCount();
|
|
|
|
+ UpdateNextTrySecondsDelay();
|
|
|
|
+ string description;
|
|
|
|
+ if (response == null || response.ReasonPhrase == null) {
|
|
|
|
+ description = "网络错误";
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ description = response.ReadMessage(ex);
|
|
|
|
+ }
|
|
|
|
+ UpdateWsStateAsync(description, toOut: false);
|
|
|
|
+ // 退出
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ string server = response.Data;
|
|
|
|
+ if (string.IsNullOrEmpty(server)) {
|
|
|
|
+ // 没有获取到该矿机的WsServer分片节点,递增失败次数以增大重试延迟周期
|
|
|
|
+ IncreaseFailCount();
|
|
|
|
+ UpdateNextTrySecondsDelay();
|
|
|
|
+ UpdateWsStateAsync("服务器不在线", toOut: false);
|
|
|
|
+ // 退出
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ _wsServerIp = server;
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ var ws = new WebSocket($"ws://{server}/{_appType.GetName()}") {
|
|
|
|
+ Compression = CompressionMethod.Deflate
|
|
|
|
+ };
|
|
|
|
+ ws.Log.File = TempPath.WebSocketSharpMinerClientLogFileFullName;
|
|
|
|
+ ws.Log.Output = WebSocketSharpOutput;
|
|
|
|
+ ws.OnOpen += new EventHandler(Ws_OnOpen);
|
|
|
|
+ ws.OnMessage += new EventHandler<MessageEventArgs>(Ws_OnMessage);
|
|
|
|
+ ws.OnError += new EventHandler<ErrorEventArgs>(Ws_OnError);
|
|
|
|
+ ws.OnClose += new EventHandler<CloseEventArgs>(Ws_OnClose);
|
|
|
|
+
|
|
|
|
+ ConnectAsync(ws);
|
|
|
|
+ _ws = ws;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ #endregion
|
|
|
|
+
|
|
|
|
+ private static void WebSocketSharpOutput(LogData data, string path) {
|
|
|
|
+ Console.WriteLine(data.Message);
|
|
|
|
+ if (path != null && path.Length > 0) {
|
|
|
|
+ using (var writer = new System.IO.StreamWriter(path, true))
|
|
|
|
+ using (var syncWriter = System.IO.TextWriter.Synchronized(writer)) {
|
|
|
|
+ syncWriter.WriteLine(data.ToString());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void Ws_OnClose(object sender, CloseEventArgs e) {
|
|
|
|
+ WebSocket ws = (WebSocket)sender;
|
|
|
|
+ if (_ws != ws) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ #region
|
|
|
|
+ try {
|
|
LastTryOn = DateTime.Now;
|
|
LastTryOn = DateTime.Now;
|
|
- if (!response.IsSuccess()) {
|
|
|
|
|
|
+ CloseStatusCode closeStatus = CloseStatusCode.Undefined;
|
|
|
|
+ if (Enum.IsDefined(typeof(CloseStatusCode), e.Code)) {
|
|
|
|
+ closeStatus = (CloseStatusCode)e.Code;
|
|
|
|
+ _closeCode = closeStatus.GetName();
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ _closeCode = e.Code.ToString();
|
|
|
|
+ Logger.InfoDebugLine($"意外的Ws关闭码:{_closeCode}");
|
|
|
|
+ }
|
|
|
|
+ if (closeStatus == CloseStatusCode.ProtocolError || closeStatus == CloseStatusCode.Undefined) {
|
|
|
|
+ // 协议错误导致连接关闭,递增失败次数以增大重试延迟周期
|
|
|
|
+ IncreaseFailCount();
|
|
|
|
+ // WebSocket协议并无定义状态码用于表达握手阶段身份验证失败的情况,阅读WebSocketSharp的源码发现验证不通过包含于ProtocolError中,
|
|
|
|
+ // 姑且将ProtocolError视为用户名密码验证不通过,因为ProtocolError包含的其它失败情况在编程正确下不会发送。
|
|
|
|
+ _closeReason = "登录失败";
|
|
|
|
+ }
|
|
|
|
+ else if (closeStatus == CloseStatusCode.Away) {
|
|
|
|
+ // 服务器ping不通时和服务器关闭进程时,递增失败次数以增大重试延迟周期
|
|
|
|
+ IncreaseFailCount();
|
|
|
|
+ _closeReason = "失去连接,请稍后重试";
|
|
|
|
+ // 可能是因为服务器节点不存在导致的失败,所以下一次进行重新获取服务器地址的全新连接
|
|
|
|
+ // 2,连不上服务器时
|
|
|
|
+ NeedReWebSocket();
|
|
|
|
+ }
|
|
|
|
+ else if (closeStatus == CloseStatusCode.Abnormal) {
|
|
|
|
+ // 服务器或本机网络不可用时,递增失败次数以增大重试延迟周期
|
|
IncreaseFailCount();
|
|
IncreaseFailCount();
|
|
- UpdateNextTrySecondsDelay();
|
|
|
|
- string description;
|
|
|
|
- if (response == null || response.ReasonPhrase == null) {
|
|
|
|
- description = "网络错误";
|
|
|
|
|
|
+ _closeReason = "网络错误";
|
|
|
|
+ // 可能是因为服务器节点不存在导致的失败,所以下一次进行重新获取服务器地址的全新连接
|
|
|
|
+ // 2,连不上服务器时
|
|
|
|
+ NeedReWebSocket();
|
|
|
|
+ }
|
|
|
|
+ else if (closeStatus == CloseStatusCode.Normal) {
|
|
|
|
+ _closeReason = e.Reason;
|
|
|
|
+ if (e.Reason == WsMessage.ReGetServerAddress) {
|
|
|
|
+ _closeReason = "服务器通知客户端重连";
|
|
|
|
+ // 3,收到服务器的WsMessage.ReGetServerAddress类型的消息时
|
|
|
|
+ NeedReWebSocket();
|
|
|
|
+ // 立即重连以防止在上一个连接关闭之前重连成功从而在界面上留下错误顺序的消息
|
|
|
|
+ NewWebSocket();
|
|
}
|
|
}
|
|
else {
|
|
else {
|
|
- description = response.ReadMessage(ex);
|
|
|
|
|
|
+ // 正常关闭时,递增失败次数以增大重试延迟周期
|
|
|
|
+ IncreaseFailCount();
|
|
}
|
|
}
|
|
- UpdateWsStateAsync(description, toOut: false);
|
|
|
|
- callback?.Invoke(null);
|
|
|
|
- // 退出
|
|
|
|
- return;
|
|
|
|
}
|
|
}
|
|
- string server = response.Data;
|
|
|
|
- if (string.IsNullOrEmpty(server)) {
|
|
|
|
- IncreaseFailCount();
|
|
|
|
- UpdateNextTrySecondsDelay();
|
|
|
|
- UpdateWsStateAsync("服务器不在线", toOut: false);
|
|
|
|
- callback?.Invoke(null);
|
|
|
|
- // 退出
|
|
|
|
|
|
+ }
|
|
|
|
+ catch {
|
|
|
|
+ }
|
|
|
|
+ UpdateNextTrySecondsDelay();
|
|
|
|
+ UpdateWsStateAsync($"{_closeCode} {_closeReason}", toOut: false);
|
|
|
|
+ #endregion
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void Ws_OnError(object sender, ErrorEventArgs e) {
|
|
|
|
+ NTMinerConsole.DevError(e.Message);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void Ws_OnMessage(object sender, MessageEventArgs e) {
|
|
|
|
+ WebSocket ws = (WebSocket)sender;
|
|
|
|
+ if (_ws != ws) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ #region
|
|
|
|
+ if (e.IsPing) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ WsMessage message = null;
|
|
|
|
+ if (e.IsBinary) {
|
|
|
|
+ message = VirtualRoot.BinarySerializer.Deserialize<WsMessage>(e.RawData);
|
|
|
|
+ }
|
|
|
|
+ else {// 此时一定IsText,因为取值只有IsBinary、IsPing、IsText这三种
|
|
|
|
+ if (string.IsNullOrEmpty(e.Data) || e.Data[0] != '{' || e.Data[e.Data.Length - 1] != '}') {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
-
|
|
|
|
- var ws = new WebSocket($"ws://{server}/{_appType.GetName()}");
|
|
|
|
- ws.OnOpen += (sender, e) => {
|
|
|
|
- ResetFailCount();
|
|
|
|
- UpdateWsStateAsync("连接服务器成功", toOut: false);
|
|
|
|
- };
|
|
|
|
- ws.OnMessage += (sender, e) => {
|
|
|
|
- if (_ws != ws) {
|
|
|
|
|
|
+ message = VirtualRoot.JsonSerializer.Deserialize<WsMessage>(e.Data);
|
|
|
|
+ }
|
|
|
|
+ if (message == null) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ switch (_appType) {
|
|
|
|
+ case NTMinerAppType.MinerClient:
|
|
|
|
+ if (message.Type == WsMessage.UpdateAESPassword) {
|
|
|
|
+ if (message.TryGetData(out AESPassword aesPassword)) {
|
|
|
|
+ aesPassword.Password = Cryptography.RSAUtil.DecryptString(aesPassword.Password, aesPassword.PublicKey);
|
|
|
|
+ _aesPassword = aesPassword;
|
|
|
|
+ }
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- #region
|
|
|
|
- if (e.IsPing) {
|
|
|
|
|
|
+ if (_aesPassword == null) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- WsMessage message = null;
|
|
|
|
- if (e.IsBinary) {
|
|
|
|
- message = VirtualRoot.BinarySerializer.Deserialize<WsMessage>(e.RawData);
|
|
|
|
- }
|
|
|
|
- else {// 此时一定IsText,因为取值只有IsBinary、IsPing、IsText这三种
|
|
|
|
- if (string.IsNullOrEmpty(e.Data) || e.Data[0] != '{' || e.Data[e.Data.Length - 1] != '}') {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- message = VirtualRoot.JsonSerializer.Deserialize<WsMessage>(e.Data);
|
|
|
|
- }
|
|
|
|
- if (message == null) {
|
|
|
|
|
|
+ if (message.Sign != message.CalcSign(_aesPassword.Password)) {
|
|
|
|
+ UpdateWsStateAsync(description: "来自群控的消息签名错误", toOut: true);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- switch (_appType) {
|
|
|
|
- case NTMinerAppType.MinerClient:
|
|
|
|
- if (message.Type == WsMessage.UpdateAESPassword) {
|
|
|
|
- if (message.TryGetData(out AESPassword aesPassword)) {
|
|
|
|
- aesPassword.Password = Cryptography.RSAUtil.DecryptString(aesPassword.Password, aesPassword.PublicKey);
|
|
|
|
- _aesPassword = aesPassword;
|
|
|
|
- }
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- if (_aesPassword == null) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- if (message.Sign != message.CalcSign(_aesPassword.Password)) {
|
|
|
|
- UpdateWsStateAsync(description: "来自群控的消息签名错误", toOut: true);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
- case NTMinerAppType.MinerStudio:
|
|
|
|
- if (message.Type == WsMessage.ReLogin) {
|
|
|
|
- UpdateWsStateAsync(description: "用户密码已变更,请重新登录", toOut: true);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- if (message.Sign != message.CalcSign(RpcRoot.RpcUser.Password)) {
|
|
|
|
- UpdateWsStateAsync(description: "来自群控的消息签名错误", toOut: true);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- if (message.Type == WsMessage.ReGetServerAddress) {
|
|
|
|
- ws.CloseAsync(CloseStatusCode.Normal, WsMessage.ReGetServerAddress);
|
|
|
|
|
|
+ break;
|
|
|
|
+ case NTMinerAppType.MinerStudio:
|
|
|
|
+ if (message.Type == WsMessage.ReLogin) {
|
|
|
|
+ UpdateWsStateAsync(description: "用户密码已变更,请重新登录", toOut: true);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- if (TryGetHandler(message.Type, out Action<Action<WsMessage>, WsMessage> handler)) {
|
|
|
|
- handler.Invoke(SendAsync, message);
|
|
|
|
- }
|
|
|
|
- else {
|
|
|
|
- NTMinerConsole.DevWarn(() => $"OnMessage Received InvalidType {e.Data}");
|
|
|
|
- }
|
|
|
|
- #endregion
|
|
|
|
- };
|
|
|
|
- ws.OnError += (sender, e) => {
|
|
|
|
- NTMinerConsole.DevError(e.Message);
|
|
|
|
- };
|
|
|
|
- ws.OnClose += (sender, e) => {
|
|
|
|
- if (_ws != ws) {
|
|
|
|
|
|
+ if (message.Sign != message.CalcSign(RpcRoot.RpcUser.Password)) {
|
|
|
|
+ UpdateWsStateAsync(description: "来自群控的消息签名错误", toOut: true);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- #region
|
|
|
|
- try {
|
|
|
|
- LastTryOn = DateTime.Now;
|
|
|
|
- _closeCode = e.Code.ToString();
|
|
|
|
- CloseStatusCode closeStatus = (CloseStatusCode)e.Code;
|
|
|
|
- _closeCode = closeStatus.GetName();
|
|
|
|
- if (closeStatus == CloseStatusCode.ProtocolError) {
|
|
|
|
- IncreaseFailCount();
|
|
|
|
- // WebSocket协议并无定义状态码用于表达握手阶段身份验证失败的情况,阅读WebSocketSharp的源码发现验证不通过包含于ProtocolError中,
|
|
|
|
- // 姑且将ProtocolError视为用户名密码验证不通过,因为ProtocolError包含的其它失败情况在编程正确下不会发送。
|
|
|
|
- _closeReason = "登录失败";
|
|
|
|
- }
|
|
|
|
- else if (closeStatus == CloseStatusCode.Away) { // 服务器ping不通时和服务器关闭进程时都是Away
|
|
|
|
- IncreaseFailCount();
|
|
|
|
- _closeReason = "失去连接,请稍后重试";
|
|
|
|
- // 可能是因为服务器节点不存在导致的失败,所以下一次进行重新获取服务器地址的全新连接
|
|
|
|
- // 2,连不上服务器时
|
|
|
|
- _ws = null;
|
|
|
|
- }
|
|
|
|
- else if (closeStatus == CloseStatusCode.Abnormal) { // 服务器或本机网络不可用时尝试连接时的类型是Abnormal
|
|
|
|
- IncreaseFailCount();
|
|
|
|
- _closeReason = "网络错误";
|
|
|
|
- // 可能是因为服务器节点不存在导致的失败,所以下一次进行重新获取服务器地址的全新连接
|
|
|
|
- // 2,连不上服务器时
|
|
|
|
- _ws = null;
|
|
|
|
- }
|
|
|
|
- else if (closeStatus == CloseStatusCode.Normal) {
|
|
|
|
- _closeReason = e.Reason;
|
|
|
|
- if (e.Reason == WsMessage.ReGetServerAddress) {
|
|
|
|
- _closeReason = "服务器通知客户端重连";
|
|
|
|
- // 放在这里重连以防止在上一个连接关闭之前重连成功从而在界面上留下错误顺序的消息
|
|
|
|
- // 3,收到服务器的WsMessage.ReGetServerAddress类型的消息时
|
|
|
|
- NewWebSocket(webSocket => _ws = webSocket);
|
|
|
|
- }
|
|
|
|
- else {
|
|
|
|
- IncreaseFailCount();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- catch {
|
|
|
|
- }
|
|
|
|
- UpdateNextTrySecondsDelay();
|
|
|
|
- UpdateWsStateAsync($"{_closeCode} {_closeReason}", toOut: false);
|
|
|
|
- #endregion
|
|
|
|
- };
|
|
|
|
- ConnectAsync(ws);
|
|
|
|
- callback?.Invoke(ws);
|
|
|
|
- });
|
|
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ if (message.Type == WsMessage.ReGetServerAddress) {
|
|
|
|
+ ws.CloseAsync(CloseStatusCode.Normal, WsMessage.ReGetServerAddress);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ if (TryGetHandler(message.Type, out Action<Action<WsMessage>, WsMessage> handler)) {
|
|
|
|
+ handler.Invoke(SendAsync, message);
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ NTMinerConsole.DevWarn(() => $"OnMessage Received InvalidType {e.Data}");
|
|
|
|
+ }
|
|
|
|
+ #endregion
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void Ws_OnOpen(object sender, EventArgs e) {
|
|
|
|
+ ResetFailCount();
|
|
|
|
+ UpdateWsStateAsync("连接服务器成功", toOut: false);
|
|
}
|
|
}
|
|
- #endregion
|
|
|
|
|
|
|
|
#region ConnectAsync
|
|
#region ConnectAsync
|
|
private readonly FieldInfo _retryCountForConnectFieldInfo = typeof(WebSocket).GetField("_retryCountForConnect", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
private readonly FieldInfo _retryCountForConnectFieldInfo = typeof(WebSocket).GetField("_retryCountForConnect", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
@@ -364,9 +427,9 @@ namespace NTMiner.Ws {
|
|
_retryCountForConnectFieldInfo.SetValue(ws, 1);
|
|
_retryCountForConnectFieldInfo.SetValue(ws, 1);
|
|
WsUserName userName = new WsUserName {
|
|
WsUserName userName = new WsUserName {
|
|
ClientType = _appType,
|
|
ClientType = _appType,
|
|
- ClientVersion = EntryAssemblyInfo.CurrentVersionStr,
|
|
|
|
- ClientId = NTMinerRegistry.GetClientId(_appType),
|
|
|
|
- UserId = _outerUserId,
|
|
|
|
|
|
+ ClientVersion = EntryAssemblyInfo.CurrentVersionStr,
|
|
|
|
+ ClientId = NTMinerRegistry.GetClientId(_appType),
|
|
|
|
+ UserId = _preOuterUserId,
|
|
IsBinarySupported = true
|
|
IsBinarySupported = true
|
|
};
|
|
};
|
|
string userNameJson = VirtualRoot.JsonSerializer.Serialize(userName);
|
|
string userNameJson = VirtualRoot.JsonSerializer.Serialize(userName);
|
|
@@ -389,6 +452,9 @@ namespace NTMiner.Ws {
|
|
NextTrySecondsDelay = -1;
|
|
NextTrySecondsDelay = -1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// 每失败一次,重试延迟周期增加10秒钟
|
|
|
|
+ /// </summary>
|
|
private void IncreaseFailCount() {
|
|
private void IncreaseFailCount() {
|
|
_failConnCount++;
|
|
_failConnCount++;
|
|
}
|
|
}
|