* 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 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() {

View File

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

View File

@ -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();
}
} }
} }

View File

@ -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;