* LCRTMRouter.cs:
* LCRTMServer.cs: * LCWebSocketConnection.cs: * LCIMClient.cs: chore: 基础断线重连功能
parent
66f3a479b4
commit
a11da59ec5
|
@ -12,11 +12,11 @@ namespace LeanCloud.Realtime.Internal.Router {
|
||||||
internal LCRTMRouter() {
|
internal LCRTMRouter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<string> GetServer() {
|
internal async Task<LCRTMServer> GetServer() {
|
||||||
if (rtmServer == null || !rtmServer.IsValid) {
|
if (rtmServer == null || !rtmServer.IsValid) {
|
||||||
await Fetch();
|
await Fetch();
|
||||||
}
|
}
|
||||||
return rtmServer.Server;
|
return rtmServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Reset() {
|
internal void Reset() {
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace LeanCloud.Realtime.Internal.Router {
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonProperty("server")]
|
[JsonProperty("server")]
|
||||||
internal string Server {
|
internal string Primary {
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ using Google.Protobuf;
|
||||||
|
|
||||||
namespace LeanCloud.Realtime.Internal.WebSocket {
|
namespace LeanCloud.Realtime.Internal.WebSocket {
|
||||||
internal class LCWebSocketConnection {
|
internal class LCWebSocketConnection {
|
||||||
private const int KEEP_ALIVE_INTERVAL = 10;
|
private const int KEEP_ALIVE_INTERVAL = 1;
|
||||||
// .net standard 2.0 好像在拼合 Frame 时有 bug,所以将这个值调整大一些
|
// .net standard 2.0 好像在拼合 Frame 时有 bug,所以将这个值调整大一些
|
||||||
private const int RECV_BUFFER_SIZE = 1024 * 5;
|
private const int RECV_BUFFER_SIZE = 1024 * 5;
|
||||||
|
|
||||||
|
@ -32,7 +32,11 @@ namespace LeanCloud.Realtime.Internal.WebSocket {
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Action<int, string> OnClose {
|
internal Action<int, string> OnDisconnect {
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Func<Task> OnReconnect {
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,13 +48,42 @@ namespace LeanCloud.Realtime.Internal.WebSocket {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task Connect() {
|
internal async Task Connect() {
|
||||||
string rtmServer = await Router.GetServer();
|
// TODO 可完善策略
|
||||||
|
LCRTMServer rtmServer = await Router.GetServer();
|
||||||
|
try {
|
||||||
|
LCLogger.Debug($"Connect Primary Server: {rtmServer.Primary}");
|
||||||
|
await Connect(rtmServer.Primary);
|
||||||
|
LCLogger.Debug("Connected Primary Server");
|
||||||
|
} catch (Exception e) {
|
||||||
|
LCLogger.Error(e.Message);
|
||||||
|
LCLogger.Debug($"Connect Secondary Server: {rtmServer.Secondary}");
|
||||||
|
await Connect(rtmServer.Secondary);
|
||||||
|
LCLogger.Debug($"Connected Secondary Server");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接收
|
||||||
|
_ = StartReceive();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Connect(string server) {
|
||||||
ws = new ClientWebSocket();
|
ws = new ClientWebSocket();
|
||||||
ws.Options.AddSubProtocol("lc.protobuf2.3");
|
ws.Options.AddSubProtocol("lc.protobuf2.3");
|
||||||
ws.Options.KeepAliveInterval = TimeSpan.FromSeconds(KEEP_ALIVE_INTERVAL);
|
ws.Options.KeepAliveInterval = TimeSpan.FromSeconds(KEEP_ALIVE_INTERVAL);
|
||||||
await ws.ConnectAsync(new Uri(rtmServer), default);
|
await ws.ConnectAsync(new Uri(server), default);
|
||||||
_ = StartReceive();
|
}
|
||||||
|
|
||||||
|
private async Task Reconnect() {
|
||||||
|
// TODO 重连策略
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
await Connect();
|
||||||
|
break;
|
||||||
|
} catch (Exception e) {
|
||||||
|
LCLogger.Error(e.Message);
|
||||||
|
await Task.Delay(1000 * 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OnReconnect?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Task<GenericCommand> SendRequest(GenericCommand request) {
|
internal Task<GenericCommand> SendRequest(GenericCommand request) {
|
||||||
|
@ -61,9 +94,9 @@ namespace LeanCloud.Realtime.Internal.WebSocket {
|
||||||
ArraySegment<byte> bytes = new ArraySegment<byte>(request.ToByteArray());
|
ArraySegment<byte> bytes = new ArraySegment<byte>(request.ToByteArray());
|
||||||
try {
|
try {
|
||||||
ws.SendAsync(bytes, WebSocketMessageType.Binary, true, default);
|
ws.SendAsync(bytes, WebSocketMessageType.Binary, true, default);
|
||||||
} catch (Exception) {
|
} catch (Exception e) {
|
||||||
// TODO 发送消息异常
|
// TODO 发送消息异常
|
||||||
|
LCLogger.Error(e.Message);
|
||||||
}
|
}
|
||||||
return tcs.Task;
|
return tcs.Task;
|
||||||
}
|
}
|
||||||
|
@ -81,7 +114,13 @@ namespace LeanCloud.Realtime.Internal.WebSocket {
|
||||||
do {
|
do {
|
||||||
result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), default);
|
result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), default);
|
||||||
if (result.MessageType == WebSocketMessageType.Close) {
|
if (result.MessageType == WebSocketMessageType.Close) {
|
||||||
OnClose?.Invoke(-1, null);
|
LCLogger.Debug($"Receive Closed: {result.CloseStatusDescription}");
|
||||||
|
try {
|
||||||
|
await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "", default);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
LCLogger.Error(ex.Message);
|
||||||
|
}
|
||||||
|
OnDisconnect?.Invoke(-1, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 拼合 WebSocket Frame
|
// 拼合 WebSocket Frame
|
||||||
|
@ -99,10 +138,18 @@ namespace LeanCloud.Realtime.Internal.WebSocket {
|
||||||
LCLogger.Error(e.Message);
|
LCLogger.Error(e.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (WebSocketException e) {
|
||||||
// 连接断开
|
|
||||||
LCLogger.Error(e.Message);
|
LCLogger.Error(e.Message);
|
||||||
await ws.CloseAsync(WebSocketCloseStatus.EndpointUnavailable, "read error", default);
|
LCLogger.Debug($"WebSocket State: {ws.State}");
|
||||||
|
try {
|
||||||
|
ws.Abort();
|
||||||
|
ws.Dispose();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
LCLogger.Error(ex.Message);
|
||||||
|
} finally {
|
||||||
|
// 触发重连
|
||||||
|
await Reconnect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -224,7 +224,9 @@ namespace LeanCloud.Realtime {
|
||||||
GoAwayController = new LCIMGoAwayController(this);
|
GoAwayController = new LCIMGoAwayController(this);
|
||||||
|
|
||||||
Connection = new LCWebSocketConnection(Id) {
|
Connection = new LCWebSocketConnection(Id) {
|
||||||
OnNotification = OnNotification
|
OnNotification = OnNotification,
|
||||||
|
OnDisconnect = OnDisconnect,
|
||||||
|
OnReconnect = OnReconnect
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,6 +389,11 @@ namespace LeanCloud.Realtime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task OnReconnect() {
|
||||||
|
// 打开 Session
|
||||||
|
await SessionController.Open();
|
||||||
|
}
|
||||||
|
|
||||||
internal async Task<LCIMConversation> GetOrQueryConversation(string convId) {
|
internal async Task<LCIMConversation> GetOrQueryConversation(string convId) {
|
||||||
if (ConversationDict.TryGetValue(convId, out LCIMConversation conversation)) {
|
if (ConversationDict.TryGetValue(convId, out LCIMConversation conversation)) {
|
||||||
return conversation;
|
return conversation;
|
||||||
|
|
Loading…
Reference in New Issue