* LCRTMRouter.cs:

* LCRTMServer.cs:
* LCWebSocketConnection.cs:

* LCIMClient.cs: chore: 基础断线重连功能
oneRain 2020-04-02 18:15:16 +08:00
parent 66f3a479b4
commit a11da59ec5
4 changed files with 69 additions and 15 deletions

View File

@ -12,11 +12,11 @@ namespace LeanCloud.Realtime.Internal.Router {
internal LCRTMRouter() {
}
internal async Task<string> GetServer() {
internal async Task<LCRTMServer> GetServer() {
if (rtmServer == null || !rtmServer.IsValid) {
await Fetch();
}
return rtmServer.Server;
return rtmServer;
}
internal void Reset() {

View File

@ -14,7 +14,7 @@ namespace LeanCloud.Realtime.Internal.Router {
}
[JsonProperty("server")]
internal string Server {
internal string Primary {
get; set;
}

View File

@ -10,7 +10,7 @@ using Google.Protobuf;
namespace LeanCloud.Realtime.Internal.WebSocket {
internal class LCWebSocketConnection {
private const int KEEP_ALIVE_INTERVAL = 10;
private const int KEEP_ALIVE_INTERVAL = 1;
// .net standard 2.0 好像在拼合 Frame 时有 bug所以将这个值调整大一些
private const int RECV_BUFFER_SIZE = 1024 * 5;
@ -32,7 +32,11 @@ namespace LeanCloud.Realtime.Internal.WebSocket {
get; set;
}
internal Action<int, string> OnClose {
internal Action<int, string> OnDisconnect {
get; set;
}
internal Func<Task> OnReconnect {
get; set;
}
@ -44,13 +48,42 @@ namespace LeanCloud.Realtime.Internal.WebSocket {
}
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.Options.AddSubProtocol("lc.protobuf2.3");
ws.Options.KeepAliveInterval = TimeSpan.FromSeconds(KEEP_ALIVE_INTERVAL);
await ws.ConnectAsync(new Uri(rtmServer), default);
_ = StartReceive();
await ws.ConnectAsync(new Uri(server), default);
}
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) {
@ -61,9 +94,9 @@ namespace LeanCloud.Realtime.Internal.WebSocket {
ArraySegment<byte> bytes = new ArraySegment<byte>(request.ToByteArray());
try {
ws.SendAsync(bytes, WebSocketMessageType.Binary, true, default);
} catch (Exception) {
} catch (Exception e) {
// TODO 发送消息异常
LCLogger.Error(e.Message);
}
return tcs.Task;
}
@ -81,7 +114,13 @@ namespace LeanCloud.Realtime.Internal.WebSocket {
do {
result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), default);
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;
}
// 拼合 WebSocket Frame
@ -99,10 +138,18 @@ namespace LeanCloud.Realtime.Internal.WebSocket {
LCLogger.Error(e.Message);
}
}
} catch (Exception e) {
// 连接断开
} catch (WebSocketException e) {
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();
}
}
}

View File

@ -224,7 +224,9 @@ namespace LeanCloud.Realtime {
GoAwayController = new LCIMGoAwayController(this);
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) {
if (ConversationDict.TryGetValue(convId, out LCIMConversation conversation)) {
return conversation;