Esempio n. 1
0
        internal async Task <GenericCommand> SendRequest(GenericCommand request)
        {
            if (IsIdempotentCommand(request))
            {
                GenericCommand sendingReq = requestToResponses.Keys.FirstOrDefault(item => {
                    // TRICK 除了 I 其他字段相等
                    request.I = item.I;
                    return(Equals(request, item));
                });
                if (sendingReq != null)
                {
                    LCLogger.Warn("duplicated request");
                    if (requestToResponses.TryGetValue(sendingReq, out TaskCompletionSource <GenericCommand> waitingTcs))
                    {
                        return(await waitingTcs.Task);
                    }
                    LCLogger.Error($"error request: {request}");
                }
            }

            TaskCompletionSource <GenericCommand> tcs = new TaskCompletionSource <GenericCommand>();

            request.I = requestI++;
            requestToResponses.Add(request, tcs);
            try {
                await SendCommand(request);
            } catch (Exception e) {
                tcs.TrySetException(e);
            }
            return(await tcs.Task);
        }
Esempio n. 2
0
 private void OnMessage(byte[] bytes, int length)
 {
     try {
         GenericCommand command = GenericCommand.Parser.ParseFrom(bytes, 0, length);
         LCLogger.Debug($"{id} <= {FormatCommand(command)}");
         if (command.HasI)
         {
             // 应答
             int requestIndex = command.I;
             if (requestToResponses.TryGetValue(command, out TaskCompletionSource <GenericCommand> tcs))
             {
                 requestToResponses.Remove(command);
                 if (command.HasErrorMessage)
                 {
                     // 错误
                     ErrorCommand error  = command.ErrorMessage;
                     int          code   = error.Code;
                     string       detail = error.Detail;
                     // 包装成异常抛出
                     LCException exception = new LCException(code, detail);
                     tcs.TrySetException(exception);
                 }
                 else
                 {
                     tcs.TrySetResult(command);
                 }
             }
             else
             {
                 LCLogger.Error($"No request for {requestIndex}");
             }
         }
         else
         {
             if (command.Cmd == CommandType.Echo)
             {
                 // 心跳应答
                 heartBeat.Pong();
             }
             else if (command.Cmd == CommandType.Goaway)
             {
                 // 针对连接的消息
                 Reset();
             }
             else
             {
                 // 通知
                 string peerId = command.HasPeerId ? command.PeerId : defaultClientId;
                 if (idToClients.TryGetValue(peerId, out LCIMClient client))
                 {
                     // 通知具体客户端
                     client.HandleNotification(command);
                 }
             }
         }
     } catch (Exception e) {
         LCLogger.Error(e);
     }
 }
Esempio n. 3
0
 private static async Task SaveToLocal()
 {
     try {
         string json = currentUser.ToString();
         await LCCore.PersistenceController.WriteText(USER_DATA, json);
     } catch (Exception e) {
         LCLogger.Error(e.Message);
     }
 }
Esempio n. 4
0
 /// <summary>
 /// Signing in
 /// </summary>
 /// <param name="force">If this is ture (default value), and single device sign-on is enabled, users already logged in on another device with the same tag will be logged out.</param>
 /// <returns></returns>
 public async Task Open(bool force = true)
 {
     try {
         // 打开 Session
         await SessionController.Open(force);
     } catch (Exception e) {
         LCLogger.Error(e);
         // 如果 session 阶段异常,则关闭连接
         throw e;
     }
 }
Esempio n. 5
0
 private void HandleExceptionClose()
 {
     try {
         ws.Abort();
         ws.Dispose();
     } catch (Exception e) {
         LCLogger.Error(e);
     } finally {
         OnClose?.Invoke();
     }
 }
Esempio n. 6
0
        private async Task StartReceive()
        {
            byte[] recvBuffer = new byte[RECV_BUFFER_SIZE];
            byte[] msgBuffer  = new byte[MSG_BUFFER_SIZE];
            int    offset     = 0;

            try {
                while (ws.State == WebSocketState.Open)
                {
                    WebSocketReceiveResult result = await ws.ReceiveAsync(new ArraySegment <byte>(recvBuffer), default);

                    if (result.MessageType == WebSocketMessageType.Close)
                    {
                        LCLogger.Debug($"Receive Closed: {result.CloseStatus}");
                        if (ws.State == WebSocketState.CloseReceived)
                        {
                            // 如果是服务端主动关闭,则挥手关闭,并认为是断线
                            try {
                                Task closeTask = ws.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, default);
                                await Task.WhenAny(closeTask, Task.Delay(CLOSE_TIMEOUT));
                            } catch (Exception e) {
                                LCLogger.Error(e);
                            } finally {
                                HandleExceptionClose();
                            }
                        }
                    }
                    else
                    {
                        // 拼合 WebSocket Message
                        int length = result.Count;
                        if (offset + length > msgBuffer.Length)
                        {
                            // 反序列化数组大小不够,则以 2x 扩充
                            byte[] newBuffer = new byte[msgBuffer.Length * 2];
                            Array.Copy(msgBuffer, newBuffer, msgBuffer.Length);
                            msgBuffer = newBuffer;
                        }
                        Array.Copy(recvBuffer, 0, msgBuffer, offset, length);
                        offset += length;
                        if (result.EndOfMessage)
                        {
                            OnMessage?.Invoke(msgBuffer, offset);
                            offset = 0;
                        }
                    }
                }
            } catch (Exception e) {
                // 客户端网络异常
                LCLogger.Error(e);
                HandleExceptionClose();
            }
        }
 private void OnClientMessage(byte[] bytes, int length)
 {
     try {
         string json = Encoding.UTF8.GetString(bytes, 0, length);
         Dictionary <string, object> msg = JsonConvert.DeserializeObject <Dictionary <string, object> >(json,
                                                                                                        LCJsonConverter.Default);
         LCLogger.Debug($"{id} <= {json}");
         if (msg.TryGetValue("i", out object i))
         {
             int requestIndex = Convert.ToInt32(i);
             if (responses.TryGetValue(requestIndex, out TaskCompletionSource <Dictionary <string, object> > tcs))
             {
                 if (msg.TryGetValue("error", out object error))
                 {
                     // 错误
                     if (error is Dictionary <string, object> dict)
                     {
                         int    code   = Convert.ToInt32(dict["code"]);
                         string detail = dict["detail"] as string;
                         tcs.SetException(new LCException(code, detail));
                     }
                     else
                     {
                         tcs.SetException(new Exception(error as string));
                     }
                 }
                 else
                 {
                     tcs.SetResult(msg);
                 }
                 responses.Remove(requestIndex);
             }
             else
             {
                 LCLogger.Error($"No request for {requestIndex}");
             }
         }
         else
         {
             if (json == "{}")
             {
                 heartBeat.Pong();
             }
             else
             {
                 // 通知
                 OnNotification?.Invoke(msg);
             }
         }
     } catch (Exception e) {
         LCLogger.Error(e);
     }
 }
Esempio n. 8
0
        protected virtual void SendPing()
        {
            // 发送 ping 包
            GenericCommand command = new GenericCommand {
                Cmd    = CommandType.Echo,
                AppId  = LCCore.AppId,
                PeerId = connection.id
            };

            try {
                _ = connection.SendCommand(command);
            } catch (Exception e) {
                LCLogger.Error(e.Message);
            }
        }
Esempio n. 9
0
        LCException HandleErrorResponse(HttpStatusCode statusCode, string responseContent)
        {
            int    code    = (int)statusCode;
            string message = responseContent;

            try {
                // 尝试获取 LeanCloud 返回错误信息
                Dictionary <string, object> error = JsonConvert.DeserializeObject <Dictionary <string, object> >(responseContent,
                                                                                                                 LCJsonConverter.Default);
                code    = (int)error["code"];
                message = error["error"].ToString();
            } catch (Exception e) {
                LCLogger.Error(e);
            }
            return(new LCException(code, message));
        }
Esempio n. 10
0
 public async Task Close()
 {
     LCLogger.Debug("Closing WebSocket");
     OnMessage = null;
     OnClose   = null;
     try {
         // 发送关闭帧可能会很久,所以增加超时
         // 主动挥手关闭,不会再收到 Close Frame
         Task closeTask = ws.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, default);
         Task delayTask = Task.Delay(CLOSE_TIMEOUT);
         await Task.WhenAny(closeTask, delayTask);
     } catch (Exception e) {
         LCLogger.Error(e);
     } finally {
         ws.Abort();
         ws.Dispose();
         LCLogger.Debug("Closed WebSocket");
     }
 }
        public async Task Connect()
        {
            try {
                LCRTMServer rtmServer = await router.GetServer();

                try {
                    LCLogger.Debug($"Primary Server");
                    await client.Connect(rtmServer.Primary, SUB_PROTOCOL);
                } catch (Exception e) {
                    LCLogger.Error(e);
                    LCLogger.Debug($"Secondary Server");
                    await client.Connect(rtmServer.Secondary, SUB_PROTOCOL);
                }
                // 启动心跳
                heartBeat.Start();
            } catch (Exception e) {
                throw e;
            }
        }
Esempio n. 12
0
        /// <summary>
        /// Gets the currently logged in LCUser with a valid session, from
        /// memory or disk if necessary.
        /// </summary>
        /// <returns></returns>
        public static async Task <LCUser> GetCurrent()
        {
            if (currentUser != null)
            {
                return(currentUser);
            }

            string data = await LCCore.PersistenceController.ReadText(USER_DATA);

            if (!string.IsNullOrEmpty(data))
            {
                try {
                    currentUser = ParseObject(data) as LCUser;
                } catch (Exception e) {
                    LCLogger.Error(e);
                    await LCCore.PersistenceController.Delete(USER_DATA);
                }
            }
            return(currentUser);
        }
Esempio n. 13
0
        public async Task Send(byte[] data,
                               WebSocketMessageType messageType = WebSocketMessageType.Binary)
        {
            ArraySegment <byte> bytes = new ArraySegment <byte>(data);

            if (ws.State == WebSocketState.Open)
            {
                try {
                    await ws.SendAsync(bytes, messageType, true, default);
                } catch (Exception e) {
                    LCLogger.Error(e);
                    throw e;
                }
            }
            else
            {
                string message = $"Error Websocket state: {ws.State}";
                LCLogger.Error(message);
                throw new Exception(message);
            }
        }
Esempio n. 14
0
        private async Task Reconnect()
        {
            while (true)
            {
                int reconnectCount = 0;
                // 重连策略
                while (reconnectCount < MAX_RECONNECT_TIMES)
                {
                    try {
                        LCLogger.Debug($"Reconnecting... {reconnectCount}");
                        await Connect();

                        break;
                    } catch (Exception e) {
                        reconnectCount++;
                        LCLogger.Error(e);
                        LCLogger.Debug($"Reconnect after {RECONNECT_INTERVAL}ms");
                        await Task.Delay(RECONNECT_INTERVAL);
                    }
                }
                if (reconnectCount < MAX_RECONNECT_TIMES)
                {
                    // 重连成功
                    LCLogger.Debug("Reconnected");
                    ws.OnMessage = OnMessage;
                    ws.OnClose   = OnDisconnect;
                    foreach (LCIMClient client in idToClients.Values)
                    {
                        client.HandleReconnected();
                    }
                    break;
                }
                else
                {
                    // 重置 Router,继续尝试重连
                    router = new LCRTMRouter();
                }
            }
        }
Esempio n. 15
0
        internal async Task ConnectInternal()
        {
            state = State.Connecting;
            try {
                LCRTMServer rtmServer = await router.GetServer();

                try {
                    LCLogger.Debug($"Primary Server");
                    await ws.Connect(rtmServer.Primary, SUB_PROTOCOL);
                } catch (Exception e) {
                    LCLogger.Error(e);
                    LCLogger.Debug($"Secondary Server");
                    await ws.Connect(rtmServer.Secondary, SUB_PROTOCOL);
                }
                // 启动心跳
                heartBeat.Start();
                state = State.Open;
            } catch (Exception e) {
                state = State.Closed;
                throw e;
            }
        }
Esempio n. 16
0
        async Task <LCAppServer> FetchAppServer()
        {
            // 判断节点地区
            if (!IsInternalApp(appId))
            {
                // 国内节点必须配置自定义域名
                throw new Exception("Please init with your server url.");
            }
            // 向 App Router 请求地址
            if (appServer == null || !appServer.IsValid)
            {
                try {
                    HttpRequestMessage request = new HttpRequestMessage {
                        RequestUri = new Uri($"https://app-router.com/2/route?appId={appId}"),
                        Method     = HttpMethod.Get
                    };
                    HttpClient client = new HttpClient();
                    LCHttpUtils.PrintRequest(client, request);
                    HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);

                    request.Dispose();

                    string resultString = await response.Content.ReadAsStringAsync();

                    response.Dispose();
                    LCHttpUtils.PrintResponse(response, resultString);

                    Dictionary <string, object> data = JsonConvert.DeserializeObject <Dictionary <string, object> >(resultString);
                    appServer = new LCAppServer(data);
                } catch (Exception e) {
                    LCLogger.Error(e);
                    // 拉取服务地址失败后,使用国际节点的默认服务地址
                    appServer = LCAppServer.GetInternalFallbackAppServer(appId);
                }
            }
            return(appServer);
        }
        private async Task Reconnect()
        {
            while (true)
            {
                int reconnectCount = 0;
                // 重连策略
                while (reconnectCount < MAX_RECONNECT_TIMES)
                {
                    try {
                        LCLogger.Debug($"Reconnecting... {reconnectCount}");
                        await Connect();

                        break;
                    } catch (Exception e) {
                        reconnectCount++;
                        LCLogger.Error(e);
                        LCLogger.Debug($"Reconnect after {RECONNECT_INTERVAL}ms");
                        await Task.Delay(RECONNECT_INTERVAL);
                    }
                }
                if (reconnectCount < MAX_RECONNECT_TIMES)
                {
                    // 重连成功
                    LCLogger.Debug("Reconnected");
                    client.OnMessage = OnClientMessage;
                    client.OnClose   = OnClientDisconnect;
                    OnReconnected?.Invoke();
                    break;
                }
                else
                {
                    // 重置 Router,继续尝试重连
                    router = new LCRTMRouter();
                }
            }
        }
Esempio n. 18
0
        internal async Task <ReadOnlyCollection <LCIMConversation> > Find(LCIMConversationQuery query)
        {
            GenericCommand command = new GenericCommand {
                Cmd    = CommandType.Conv,
                Op     = OpType.Query,
                AppId  = LCCore.AppId,
                PeerId = Client.Id,
            };
            ConvCommand convMessage = new ConvCommand();

            string where = query.Condition.BuildWhere();
            if (!string.IsNullOrEmpty(where))
            {
                try {
                    convMessage.Where = new JsonObjectMessage {
                        Data = where
                    };
                } catch (Exception e) {
                    LCLogger.Error(e);
                }
            }
            int flag = 0;

            if (query.Compact)
            {
                flag += LCIMConversationQuery.CompactFlag;
            }
            if (query.WithLastMessageRefreshed)
            {
                flag += LCIMConversationQuery.WithLastMessageFlag;
            }
            if (flag > 0)
            {
                convMessage.Flag = flag;
            }
            convMessage.Skip  = query.Condition.Skip;
            convMessage.Limit = query.Condition.Limit;
            string orders = query.Condition.BuildOrders();

            if (!string.IsNullOrEmpty(orders))
            {
                convMessage.Sort = orders;
            }
            command.ConvMessage = convMessage;
            GenericCommand response = await Connection.SendRequest(command);

            JsonObjectMessage results = response.ConvMessage.Results;
            List <object>     convs   = JsonConvert.DeserializeObject <List <object> >(results.Data,
                                                                                       LCJsonConverter.Default);

            return(convs.Select(item => {
                Dictionary <string, object> conv = item as Dictionary <string, object>;
                string convId = conv["objectId"] as string;
                if (!Client.ConversationDict.TryGetValue(convId, out LCIMConversation conversation))
                {
                    // 解析是哪种类型的对话
                    if (conv.TryGetValue("tr", out object transient) && (bool)transient == true)
                    {
                        conversation = new LCIMChatRoom(Client);
                    }
                    else if (conv.ContainsKey("tempConv") && conv.ContainsKey("tempConvTTL"))
                    {
                        conversation = new LCIMTemporaryConversation(Client);
                    }
                    else if (conv.TryGetValue("sys", out object sys) && (bool)sys == true)
                    {
                        conversation = new LCIMServiceConversation(Client);
                    }
                    else
                    {
                        conversation = new LCIMConversation(Client);
                    }
                    Client.ConversationDict[convId] = conversation;
                }
                conversation.MergeFrom(conv);
                return conversation;
            }).ToList().AsReadOnly());
        }