//protected DateTime m_connectDT; //public DateTime ConnectDT { get { return m_connectDT; } } //protected DateTime m_activeDT; //public DateTime ActiveDT { get { return m_activeDT; } } public BufferProcessor(AsyncSocketServer asyncSocketServer, SocketUserToken userToken) { m_asyncSocketServer = asyncSocketServer; m_userToken = userToken; m_packageOffset = 0; m_readyPackage = 0; }
public void Add(string UserID, SocketUserToken userToken) { lock (m_table.SyncRoot) { m_table.Add(UserID, userToken); //收集監視器數據之在線數統計-增加 LxTcpServer.SrvMonitor.ServerStatistics.OnlineStatAdd(userToken.BindingUser.Role); } }
protected bool m_sendAsync; //標識是否有發送異步事件 //protected DateTime m_connectDT; //public DateTime ConnectDT { get { return m_connectDT; } } //protected DateTime m_activeDT; //public DateTime ActiveDT { get { return m_activeDT; } } public AsyncSendAgent(AsyncSocketServer asyncSocketServer, SocketUserToken userToken) { m_asyncSocketServer = asyncSocketServer; m_socketUserToken = userToken; m_sendAsync = false; //m_connectDT = DateTime.UtcNow; //m_activeDT = DateTime.UtcNow; }
public void Push(SocketUserToken item) { if (item == null) { throw new ArgumentException("Items added to a SocketUserToken cannot be null"); } lock (m_pool) { m_pool.Push(item); } }
///// <summary> ///// 收集監視器數據之登錄統計開始 ///// </summary> //private void LoginStatStart() //{ // //登錄當前并發數 // ServerStatistics.Concurrent_Login++; // //登錄最高并發數 // if (ServerStatistics.MaxConcurrent_Login < ServerStatistics.Concurrent_Login) // ServerStatistics.MaxConcurrent_Login = ServerStatistics.Concurrent_Login; //} ///// <summary> ///// 收集監視器數據之登錄統計結束 ///// </summary> //private void LoginStatEnd(long ElapsedMilliseconds) //{ // //登錄當前并發數 // ServerStatistics.Concurrent_Login--; // //登錄當前執行時長(ms) // ServerStatistics.DurationTime_Login = (int)ElapsedMilliseconds; // //登錄最大執行時長(ms) // if (ServerStatistics.MaxDurationTime_Login < ServerStatistics.DurationTime_Login) // ServerStatistics.MaxDurationTime_Login = ServerStatistics.DurationTime_Login; //} /// <summary> /// 處理用戶登錄 /// </summary> /// <param name="userToken"></param> /// <returns></returns> private bool ProcessLogin(SocketUserToken userToken) { bool Done = false; Stopwatch sw = new Stopwatch(); try { //收集監視器數據之登錄統計開始 ServerStatistics.LoginStatStart(); //開始計時 sw.Start(); //返回解包后的資料(返回數組 0=type ; 1=終端; 2=群號; 3=時間; 4=消息ID; 5=消息內容; 6=終端姓名) // type = "0" 是PC登錄; type = "WS" 是Web登錄 int logDataLength = 0; object[] ReceiveData = ParseProtocol.ParseLoginData(userToken.ReceiveEventArgs.Buffer, out logDataLength); string DataType = ReceiveData[0].ToString(); string DataTerminal = ReceiveData[1].ToString(); //登錄時群號規則: 普通PC用戶登錄時為空格, 外接應用(APP)登錄時為"APP", 訂閱號登錄時為"SUB", 文件傳輸時為"FILE", 語音通話登錄時為"AUDIO" //移動設備登錄為: iPhone = "IPHONE" // iPad = "IPAD" // Android Phone = "APHONE" // Android Pad = "APAD" string DataGroup = ReceiveData[2].ToString(); //保存用戶設備網卡的Mac地址 string DataTerminalName = ReceiveData[6].ToString(); //手機登錄時,包體就是用戶手機(iOS)的Token. //文件傳送時,包體就是文件發送者|文件接收者. //PC端登錄時,包體就是PC設備名稱. //語音通話時,包體就是語音通話發起者|語音通話接收者. string DataText = ReceiveData[5].ToString(); if (DataType == "0" && DataTerminal.Length > 0) { string UserID = DataTerminal; // 用戶帳號 bool Passed = true; //生成異步發送代理 if (userToken.AsyncSendAgent == null) userToken.AsyncSendAgent = new AsyncSendAgent(this, userToken); switch (DataGroup.ToUpper()) { case "SUB": #region " 訂閱號登錄 " Done = DoSubServiceLogin(UserID, DataTerminalName, DataGroup, DataText, userToken, m_subServiceTokenList); if (!Done) LogHelper.WriteLog("ProcessLogin", "#Error.完成登錄驗證,但服務器拒絕用戶登錄."); #endregion break; case "APP": #region " APP登錄 " try { List<byte[]> msgs = ParseProtocol.ConvertMsgToByte("登錄成功.", "2", UserID, DataTerminalName, DataGroup, Guid.NewGuid().ToString().Replace("-", "").ToUpper(), ""); //鎖住線程,保證本次發送完成後其他線程才能對此用戶發送消息 lock (userToken.ConnectSocket) { foreach (byte[] msg in msgs) { Passed = userToken.AsyncSendAgent.DoSendBuffer(msg); if (!Passed) break; //userToken.ConnectSocket.Send(msg); //SendData(userToken.ConnectSocket, msg); } } } catch (Exception ex) { Passed = false; LogHelper.WriteLog("ProcessLogin()", "發送登錄成功信息時" + (ex != null ? ex.Message : "")); } if (Passed) { //創建連接對象 userToken.BindingUser = new User(); userToken.BindingUser.State = User.UserStates.Online; userToken.BindingUser.Role = User.UserRole.APP; userToken.BindingUser.ID = UserID; //Socket newClient = userToken.ConnectSocket; // 將新連接加入轉發表並創建線程為其服務 // Key-GUID永不重複. //添加到正在執行的列表中 m_appUserTokenList.Add(UserID, userToken); //System.Diagnostics.Debug.WriteLine(UserID + " Logged in at " + DateTime.Now.ToString("HH:mm:ss:fff")); Done = true; } #endregion break; case "FILE": #region " 文件傳送帳號登錄 " Passed = true; if (Passed) { //創建連接對象 userToken.BindingUser = new User(); userToken.BindingUser.State = User.UserStates.Online; userToken.BindingUser.Role = User.UserRole.FileTransfer; userToken.BindingUser.ID = UserID; userToken.BindingUser.Type = "F"; userToken.BindingUser.SenderID = DataText.Split(new char[] { '|' })[0].Trim(); userToken.BindingUser.ReceiverID = DataText.Split(new char[] { '|' })[1].Trim(); //Socket newClient = userToken.ConnectSocket; // 將新連接加入轉發表並創建線程為其服務 // Key-GUID永不重複 m_fileUserTokenList.Add(UserID, userToken); // 如果是接收者(R),會在構造方法里向發送者(S)發允許建立T通道的請求 Done = true; } #endregion break; case "AUDIO": #region " 語音通話帳號登錄 " Passed = true; if (Passed) { //創建連接對象 userToken.BindingUser = new User(); userToken.BindingUser.State = User.UserStates.Online; userToken.BindingUser.Role = User.UserRole.AudioCall; userToken.BindingUser.ID = UserID; //T通道登錄時才有名稱 userToken.BindingUser.Name = DataTerminalName; userToken.BindingUser.Type = "A"; userToken.BindingUser.SenderID = DataText.Split(new char[] { '|' })[0].Trim(); userToken.BindingUser.ReceiverID = DataText.Split(new char[] { '|' })[1].Trim(); //Socket newClient = userToken.ConnectSocket; // 將新連接加入轉發表並創建線程為其服務 // Key-GUID永不重複 m_audioCallTokenList.Add(UserID, userToken); // 如果是接收者(R),會在構造方法里向發送者(S)發允許建立T通道的請求 Done = true; } #endregion break; default: //取消日誌 //LogHelper.WriteLog("ProcessReceive", string.Format("#4.解析出登錄數據:{0} [{1}],準備執行登錄操作.", DataTerminal, userToken.ConnectSocket.RemoteEndPoint)); #region " PC端/Mobile登錄 " Done = DoPcMobileLogin(UserID, DataTerminalName, DataGroup, DataText, userToken, DataGroup.Length > 0 ? m_mobileUserTokenList : m_pcUserTokenList); if (!Done) LogHelper.WriteLog("ProcessLogin", "#Error.完成登錄驗證,但服務器拒絕用戶登錄."); #endregion break; } } else if (DataType == "WS" && DataTerminal.Length > 0) { #region " Web端登錄 " //UserID = DataTerminal; // 用戶帳號 //bool Passed = true; ////Socket curSkt = _ws_transmit_tb[DataTerminal] as Socket; //UserSocket User = _ws_transmit_tb[DataTerminal] as UserSocket; //if (User != null) //{ // Socket curSkt = User.Socket; // #region " 發送離線 " // try // { // byte[] msg = WebConvertMethod.ConvertMsgToByte("00", "7", DataTerminal, DataTerminalName, "", Guid.NewGuid().ToString().Replace("-", "").ToUpper()); // //如果2秒內得不到之前登陸的對象,就直接干掉 // //否則此帳號將永遠無法登錄 // if (Monitor.TryEnter(curSkt, 2000)) // { // try // { // //向用戶本人發送離線 // curSkt.Send(msg); // //SendData(curSkt, msg); // } // catch // { } // finally // { // Monitor.Exit(curSkt); // } // } // else // { // //如果到這里說明之前登陸的對象已經死鎖 // } // //curSkt.Send(msg); // UpdateUserLog(UserID, "0", "1", "因相同帳號再次登錄而強制離線."); // //clsClientLog.WriteLog(clsClientLog.LogType.Error, "WebThreadFunc(object obj):default", "相同帳號異地登陸:" + DataTerminal + " " + DataTerminalName); // //LogHelper.WriteLog("StartUp()", "手機端相同帳號重複登陸:" + DataTerminal + "-" + DataTerminalName); // } // catch (SocketException ex) // { // //clsClientLog.WriteLog(clsClientLog.LogType.Error, "WebThreadFunc(object obj):default", "轉發發送離線消息時:" + ex.Message); // //LogHelper.WriteLog("StartUp()", "轉發發送離線消息時" + (ex != null ? ex.Message : "")); // LogHelper.WriteLog("StartUp()", "轉發手機用戶" + UserID + "離線消息時:" + (ex != null ? ex.Message : "")); // } // try // { // if (curSkt.Connected) // { // curSkt.Shutdown(SocketShutdown.Both); // //curSkt.Disconnect(false); // } // curSkt.Close(); // curSkt = null; // Thread thread = _ws_thread_tb[DataTerminal] as Thread; // if (thread != null) // { // thread.Abort(); // //線程池不會被遍歷,不鎖. // _ws_thread_tb.Remove(DataTerminal); // } // lock (_ws_transmit_tb.SyncRoot) // _ws_transmit_tb.Remove(DataTerminal); // } // catch (SocketException ex) // { // //clsClientLog.WriteLog(clsClientLog.LogType.Error, "StartUp()", "嘗試關閉用戶" + DataTerminal + "線程和Socket連接時出錯." + (ex != null ? ex.Message : "")); // LogHelper.WriteLog("StartUp()", "嘗試關閉手機用戶" + DataTerminal + "線程和Socket連接時出錯." + (ex != null ? ex.Message : "")); // } // #endregion //} //// 驗證是否為唯一用戶 //if (_ws_transmit_tb.Count != 0 && _ws_transmit_tb.ContainsKey(UserID)) //{ // try // { // byte[] msg = WebConvertMethod.ConvertMsgToByte("登錄失敗.", "3", UserID, DataTerminalName, "", Guid.NewGuid().ToString().Replace("-", "").ToUpper()); // lock (newClient) // { // newClient.Send(msg); // //SendData(newClient, msg); // } // LogHelper.WriteLog("StartUp()", "手機端" + DataTerminal + "登錄失敗"); // } // catch (SocketException ex) // { // //clsClientLog.WriteLog(clsClientLog.LogType.Error, "StartUp():驗證是否為唯一用戶", "發送登陸失敗信息時:" + ex.Message); // LogHelper.WriteLog("StartUp()", "發送登陸失敗信息時" + (ex != null ? ex.Message : "")); // } //} //else //{ // // 服務器開放登錄功能 // try // { // byte[] msg = WebConvertMethod.ConvertMsgToByte("登錄成功.", "2", UserID, DataTerminalName, "", Guid.NewGuid().ToString().Replace("-", "").ToUpper()); // //鎖住線程,保證本次發送完成後其他線程才能對此用戶發送消息 // lock (newClient) // { // newClient.Send(msg); // //SendData(newClient, msg); // } // } // catch (Exception ex) // { // Passed = false; // //clsClientLog.WriteLog(clsClientLog.LogType.Error, "StartUp():服務器開放登錄功能", "發送登陸成功信息時:" + ex.Message); // LogHelper.WriteLog("StartUp()", "發送登錄成功信息時" + (ex != null ? ex.Message : "")); // } // if (Passed) // { // //創建連接對象 // User = new UserSocket(); // User.ID = UserID; // User.Socket = newClient; // User.State = UserSocket.UserStates.Online; // using (SQLHelper conn = new SQLHelper(DBInfo)) // { // conn.OpenConnection(); // try // { // #region " 獲得當前用記的所有聯絡人并向他們推送用戶上線消息 " // StateNotifyQueue.Enqueue(new List<object>() { User, "1" }); // //DataTable ContactsList = GetContacts(conn, UserID); // //if (ContactsList.Rows.Count > 0) // //{ // // foreach (DataRow Contact in ContactsList.Rows) // // { // // //加入聯絡人(防呆自己加自己) // // if (User.ID != Contact["contact_id"].ToString()) // // User.Contacts.Add(Contact["contact_id"].ToString()); // // } // // //改為獨立線程來發 // // //如果此聯絡人在線就通知他(她)當前用戶上線了 // // //try // // //{ // // // Thread stateThread = new Thread(new ParameterizedThreadStart(StateNotifyThreadFunc)); // // // stateThread.Start(User); // // //} // // //catch // // //{ } // // //如果此聯絡人在線就通知他(她)當前用戶上線了 // // SendUserState(User); // //} // //ContactsList.Dispose(); // //ContactsList = null; // #endregion // #region " 發送所有未讀訊息(手機版暫時不推未讀取消息) " // //DataTable MsgsList = GetUnreadMsgs(conn, UserID); // //if (MsgsList.Rows.Count > 0) // //{ // // if (newClient != null) // // { // // foreach (DataRow UnreadMsg in MsgsList.Rows) // // { // // // 發給登錄人 // // DateTime MsgDT = DateTime.Parse(UnreadMsg["msg_date"].ToString() + " " + UnreadMsg["msg_time"].ToString()); // // byte[] rtnMsg = WebConvertMethod.ConvertMsgToByte(UnreadMsg["msg_text"].ToString(), // // UnreadMsg["td_no"].ToString().Length > 0 ? "4" : "U", // // UnreadMsg["msg_from"].ToString(), // // UnreadMsg["user_name"].ToString(), // // UnreadMsg["td_no"].ToString(), // // MsgDT, // // UnreadMsg["msg_guid"].ToString()); // // lock (newClient) // // { // // newClient.Send(rtnMsg); // // } // // } // // // 更新為已讀 // // conn.ExecuteSQL("update a set a.msg_state='1' from lrmsgstate a where a.msg_to='" + UserID + "' and a.msg_state='0'"); // // } // //} // //MsgsList.Dispose(); // //MsgsList = null; // #endregion // } // catch (SocketException ex) // { // //test // //clsClientLog.WriteLog(clsClientLog.LogType.Error, "StartUp()", "向PC端" + DataTerminal + "推送未讀信息時出錯." + (ex != null ? ex.Message : "")); // LogHelper.WriteLog("StartUp()", "向Mobile端" + DataTerminal + "推送未讀信息時出錯." + (ex != null ? ex.Message : "")); // } // try // { // //更新用戶狀態 // DoUpdateUserLog(conn, UserID, "0", "1", "登錄"); // DoUpdateUserState(conn, UserID, "1"); //上線中 // } // catch (Exception ex) // { // Passed = false; // LogHelper.WriteLog("StartUp()", "更新Mobile端用戶:" + DataTerminal + "狀態." + (ex != null ? ex.Message : "")); // } // conn.CloseConnection(); // } // if (Passed) // { // // 將新連接加入轉發表並創建線程為其服務 // lock (_ws_transmit_tb.SyncRoot) // { // //_ws_transmit_tb.Add(UserID, newClient); // _ws_transmit_tb.Add(UserID, User); // } // //LogHelper.WriteLog("StartUp()", "手機端" + DataTerminal + "已添加到在線用戶列表"); // try // { // Thread clientThread = new Thread(new ParameterizedThreadStart(WebThreadFunc)); // //加入哈希表 // _ws_thread_tb.Add(UserID, clientThread); // //clientThread.Start(userName); // clientThread.Start(UserID); // //LogHelper.WriteLog("StartUp()", "手機端" + DataTerminal + "-" + DataTerminalName + "登錄成功"); // } // catch (Exception ex) // { // try // { // // 發生錯誤時移除連接 // lock (_ws_transmit_tb.SyncRoot) // { // //_transmit_tb.Add(UserID, newClient); // _ws_transmit_tb.Remove(UserID); // } // } // catch // { } // //clsClientLog.WriteLog(clsClientLog.LogType.Error, "StartUp():開啟新線程", "建立登入用戶新線程時:" + ex.Message); // LogHelper.WriteLog("StartUp()", "建立登入用戶" + DataTerminal + "-" + DataTerminalName + "新線程時:" + (ex != null ? ex.Message : "")); // } // } // } //} //需不需要干掉自己? //Thread.CurrentThread.Abort(); #endregion LogHelper.WriteLog("ProcessLogin", "暫不支持Web客戶端登錄"); Done = false; } else { // 無效訊息-丟棄 Done = false; try { LogHelper.WriteLog("ProcessLogin", "無效請求數據:" + Encoding.UTF8.GetString(userToken.ReceiveEventArgs.Buffer)); } catch { } } } catch (Exception ex) { LogHelper.WriteLog("ProcessLogin", "登錄時出現問題:" + (ex != null ? ex.Message : "")); Done = false; } finally { //結束計時 sw.Stop(); //收集監視器數據之登錄統計結束 ServerStatistics.LoginStatEnd(sw.ElapsedMilliseconds); } //添加到正在執行的列表中 //m_pcSocketUserTokenList.Add(DataTerminal, userToken); return Done; }
/// <summary> /// 關閉客戶端連接 /// </summary> /// <param name="userToken"></param> public void CloseClientSocket(SocketUserToken userToken, bool ProcessUserDisconnected) { if (userToken == null) return; lock (userToken) { //說明這個客戶端Token已經牌關閉狀態了,其他的不要進來. //這里主要是給客戶端主動斷開連接引起的userToken.ReceiveEventArgs.BytesTransferred == 0解發時進入 if (!userToken.Closing) userToken.Closing = true; else return; string curDevice = "客戶端", RemoteEndPoint = ""; try { //處理用戶斷開之後的作業(語音通話會用到) if (ProcessUserDisconnected) userToken.Processor.ProcessUserDisconnected(); //處理當前用戶離線狀態的通知 if (userToken != null && userToken.BindingUser != null) if (userToken.BindingUser.Role == User.UserRole.PC || userToken.BindingUser.Role == User.UserRole.Mobile) ProcessUserOfflineState(userToken); if (userToken == null) return; if (userToken.ConnectSocket == null) return; RemoteEndPoint = userToken.ConnectSocket.RemoteEndPoint.ToString(); } catch { LogHelper.WriteLog("CloseClientSocket", "Part 1"); } try { if (userToken.ConnectSocket.Connected) userToken.ConnectSocket.Shutdown(SocketShutdown.Both); } catch (Exception ex) { LogHelper.WriteLog("CloseClientSocket", string.Format("斷開與客戶端: [{0}] 的連接時出錯:{1}.", userToken.BindingUser.ID + "[" + userToken.ConnectSocket.RemoteEndPoint + "]", (ex != null ? ex.Message : ""))); } try { //如果在S通道里斷開T連接,執行到這句時T連接會觸發CloseClientSocket()方法.所以要lock下,保證本次執行完成再執行T通道的方法. //否則會打架. userToken.ConnectSocket.Close(); //釋放引用,并清理緩存等資源 userToken.ConnectSocket = null; m_maxNumberAcceptedClients.Release(); m_idleUserTokenPool.Push(userToken); } catch { LogHelper.WriteLog("CloseClientSocket", "Part 2"); } try { if (userToken.BindingUser != null) { switch (userToken.BindingUser.Role) { case User.UserRole.PC: curDevice = "PC客戶端"; m_pcUserTokenList.Remove(userToken); break; case User.UserRole.Mobile: curDevice = "Mobile客戶端"; m_mobileUserTokenList.Remove(userToken); break; case User.UserRole.FileTransfer: curDevice = "文件傳送客戶端"; m_fileUserTokenList.Remove(userToken); break; case User.UserRole.APP: curDevice = "APP服務端"; m_appUserTokenList.Remove(userToken); break; case User.UserRole.SubService: curDevice = "訂閱號服務端"; m_subServiceTokenList.Remove(userToken); break; case User.UserRole.AudioCall: curDevice = "語音通話客戶端"; m_audioCallTokenList.Remove(userToken); break; } } } catch { LogHelper.WriteLog("CloseClientSocket", "Part 3"); } //@<" + userToken.BindingUser.ID + " " + userToken.ConnectSocket.RemoteEndPoint + "> " LogHelper.WriteLog("CloseClientSocket", string.Format("斷開與{1}: {0}的連接.", "@<" + userToken.BindingUser.ID + " " + RemoteEndPoint + "> ", curDevice)); try { //這個只能放最後,否則前面的userToken.User.ID為空 ResetUserToken(userToken); } catch { LogHelper.WriteLog("CloseClientSocket", "Part 4"); } } }
private static void ResetUserToken(SocketUserToken userToken) { userToken.Processor = null; //userToken.ReceiveQueue.Clear(); //userToken.RestPacketLength = 0; userToken.FirstPacket = true; userToken.ReceiveBuffer.Clear(); //如果緩衝區超過64KB,則回收它重新分配為4KB,以節省系統內存資源. if (userToken.ReceiveBuffer.Buffer.Length > ServerConst.MaxBufferSize) userToken.ReceiveBuffer = new DynamicBufferManager(ServerConst.InitBufferSize); userToken.BindingUser = null; userToken.Closing = false; userToken.AsyncSendAgent = null; }
/// <summary> /// 處理當前用戶離線狀態的通知 /// </summary> /// <param name="userToken"></param> private void ProcessUserOfflineState(SocketUserToken userToken) { if (userToken == null || userToken.BindingUser == null) return; User curUser = userToken.BindingUser; try { //判斷用戶有沒登錄成功,有就通知聯絡人用戶狀態變更q if (m_pcUserTokenList.ContainsKey(curUser.ID)) { //目前只寫對PC端用戶,應該還要考慮其他用戶 //通知在線的聯絡人當前用戶改變了狀態 curUser.SetUserState("6"); using (SQLHelper conn = new SQLHelper(DBInfo)) { conn.OpenConnection(); try { DoUpdateUserLog(conn, curUser.ID, "1", ((int)curUser.Mobile).ToString(), "客戶端登出", ((IPEndPoint)userToken.ConnectSocket.RemoteEndPoint).Address.ToString(), "", "", null); //如果手機端沒在線就通知聯絡人此人離線了 SocketUserToken MobileUser = m_mobileUserTokenList[curUser.ID] as SocketUserToken; if (MobileUser == null) { //StateNotifyQueue.Enqueue(new List<object>() { userToken, ((int)curUser.State).ToString() }); //ID,狀態,簽名,聯絡人清單,頭像數據 if (curUser.Contacts.Count > 0) StateNotifyQueue.Enqueue(new List<object>() { curUser.ID, ((int)curUser.State).ToString(), curUser.Signature, curUser.Contacts, null }); DoUpdateUserState(conn, curUser.ID, "6"); //離線 } conn.CloseConnection(); } catch { conn.CloseConnection(); } } } } catch (Exception ex) { LogHelper.WriteLog("ProcessUserOfflineState", "通知聯絡人用戶:" + curUser.ID + "離線時出錯:" + (ex != null ? ex.Message : "")); } ////通知在線的聯絡人當前用戶改變了狀態 //curUser.SetUserState("6"); //using (SQLHelper conn = new SQLHelper(DBInfo)) //{ // conn.OpenConnection(); // try // { // DoUpdateUserLog(conn, curUser.ID, "1", "0", "PC端登出"); // //如果手機端沒在線就通知聯絡人此人離線了 // UserSocket WebUser = _ws_transmit_tb[curUser.ID] as UserSocket; // if (WebUser == null) // { // WebUser = _mobile_transmit_tb[curUser.ID] as UserSocket; // if (WebUser == null) // { // StateNotifyQueue.Enqueue(new List<object>() { curUser, ((int)curUser.State).ToString() }); // DoUpdateUserState(conn, curUser.ID, "6"); //離線 // } // } // conn.CloseConnection(); // } // catch // { // conn.CloseConnection(); // } //} }
public void CloseClientSocket(SocketUserToken userToken) { CloseClientSocket(userToken, true); }
/// <summary> /// 訂閱號服務端登錄 /// </summary> /// <param name="UserID"></param> /// <param name="DataTerminalName"></param> /// <param name="DataGroup"></param> /// <param name="DataText"></param> /// <param name="userToken"></param> /// <param name="UserTokenList"></param> /// <returns></returns> private bool DoSubServiceLogin(string UserID, string DataTerminalName, string DataGroup, string DataText, SocketUserToken userToken, SocketUserTokenList UserTokenList) { bool Passed = true, Done = false; SocketUserToken ExistsUser = UserTokenList["#" + UserID + "#"]; if (ExistsUser != null) { Socket curSkt = ExistsUser.ConnectSocket; string curIPAddress = ExistsUser.ConnectSocket.RemoteEndPoint.ToString(); #region " 發送離線 " try { List<byte[]> msgs = ParseProtocol.ConvertMsgToByte("00", "7", UserID, DataTerminalName, "", Guid.NewGuid().ToString().Replace("-", "").ToUpper(), ""); //如果2秒內得不到之前登陸的對象,就直接干掉 //否則此帳號將永遠無法登錄 if (Monitor.TryEnter(curSkt, 2000)) { try { //向用戶本人發送離線 foreach (byte[] msg in msgs) { //2016/05/19 test ExistsUser.AsyncSendAgent.DoSendBuffer(msg); //curSkt.Send(msg); //SendData(curSkt, msg); } } catch (SocketException ex) { LogHelper.WriteLog("DoSubServiceLogin()", "執行向" + DataGroup + "用戶: " + UserID + " [" + curIPAddress + "] 發送強制離線[不允許自動重連]消息時:" + (ex != null ? ex.Message : "")); } finally { Monitor.Exit(curSkt); } } else { //如果到這里說明之前登陸的對象已經死鎖 LogHelper.WriteLog("DoSubServiceLogin()", "嘗試使用Monitor.TryEnter鎖定" + DataGroup + "用戶: " + UserID + " [" + curIPAddress + "] 時失敗."); } } catch (SocketException ex) { LogHelper.WriteLog("DoSubServiceLogin()", "向" + DataGroup + "用戶: " + UserID + " [" + curIPAddress + "] 發送離線[不允許自動重連]消息時:" + (ex != null ? ex.Message : "")); } try { //運行到這里時,有可能客戶端收到7-00之後主動斷開連接,則會觸發CloseClientSocket. if (!ExistsUser.Closing) { //ExistsUser.Closing = true; CloseClientSocket(ExistsUser); } LogHelper.WriteLog("DoSubServiceLogin", string.Format("因相同帳號再次登錄,強制先登錄的用戶{0} [{1}] 下線.", UserID, curIPAddress)); UpdateUserLog(UserID, "1", "0", "因相同帳號再次登陸而強制離線.", "", null); } catch (SocketException ex) { LogHelper.WriteLog("DoSubServiceLogin()", "嘗試關閉" + DataGroup + "用戶: " + UserID + " [" + curIPAddress + "] 線程和Socket連接時出錯." + (ex != null ? ex.Message : "")); } #endregion } // 驗證是否為唯一用戶 if (UserTokenList.ContainsKey("#" + UserID + "#")) { //到這里說明前面強制離線沒有成功 try { List<byte[]> msgs = ParseProtocol.ConvertMsgToByte("登錄失敗.", "3", UserID, DataTerminalName, "", Guid.NewGuid().ToString().Replace("-", "").ToUpper(), ""); //鎖住線程,保證本次發送完成後其他線程才能對此用戶發送消息 lock (userToken.ConnectSocket) { foreach (byte[] msg in msgs) { //同步發送 //2016/05/19 test userToken.AsyncSendAgent.DoSendBuffer(msg); //userToken.ConnectSocket.Send(msg); //SendData(userToken.ConnectSocket, msg); } } LogHelper.WriteLog("DoSubServiceLogin()", DataGroup + "用戶:" + UserID + "登錄失敗"); } catch (SocketException ex) { LogHelper.WriteLog("DoSubServiceLogin()", "發送登陸失敗信息時" + (ex != null ? ex.Message : "")); } } else { // 服務器開放登錄功能 try { List<byte[]> msgs = ParseProtocol.ConvertMsgToByte("登錄成功.", "2", UserID, DataTerminalName, DataGroup, Guid.NewGuid().ToString().Replace("-", "").ToUpper(), ""); //鎖住線程,保證本次發送完成後其他線程才能對此用戶發送消息 lock (userToken.ConnectSocket) { foreach (byte[] msg in msgs) { //2016/05/19 test userToken.AsyncSendAgent.DoSendBuffer(msg); //userToken.ConnectSocket.Send(msg); //SendData(userToken.ConnectSocket, msg); } } } catch (Exception ex) { Passed = false; LogHelper.WriteLog("DoSubServiceLogin()", "發送登錄成功信息時" + (ex != null ? ex.Message : "")); } if (Passed) { //設置對象狀態 userToken.BindingUser = new User(); userToken.BindingUser.State = User.UserStates.Online; userToken.BindingUser.Role = User.UserRole.SubService; userToken.BindingUser.Mobile = User.UserMobileType.None; userToken.BindingUser.ID = UserID; //Mac userToken.BindingUser.MacAddr = DataTerminalName; if (Passed) { //添加到正在執行的列表中 if (UserTokenList.ContainsKey("#" + UserID + "#")) { ExistsUser = UserTokenList["#" + UserID + "#"]; if (ExistsUser != null) { try { LogHelper.WriteLog("DoSubServiceLogin", string.Format("加入在線人員列表時發現已有同名帳號在里面,強制用戶{0} [{1}] 下線.", UserID, ExistsUser.ConnectSocket.RemoteEndPoint)); if (!ExistsUser.Closing) { //ExistsUser.Closing = true; CloseClientSocket(ExistsUser); } } catch (SocketException ex) { LogHelper.WriteLog("DoSubServiceLogin()", "嘗試關閉" + DataGroup + "用戶" + UserID + "線程和Socket連接時出錯." + (ex != null ? ex.Message : "")); } } //LogHelper.WriteLog("ProcessLogin()", "#5.加入在線人員列表時發現已有同名帳號在里面. " + UserID + " [" + userToken.ConnectSocket.RemoteEndPoint + "]"); } UserTokenList.Add("#" + UserID + "#", userToken); //取消日志 //LogHelper.WriteLog("ProcessLogin()", "#6.已加入到在線人員列表. " + UserID + " [" + userToken.ConnectSocket.RemoteEndPoint + "]"); //System.Diagnostics.Debug.WriteLine(UserID + " Logged in at " + DateTime.Now.ToString("HH:mm:ss:fff")); Done = true; } } } return Done; }
private bool DoPcMobileLogin(string UserID, string DataTerminalName, string DataGroup, string DataText, SocketUserToken userToken, SocketUserTokenList UserTokenList) { bool Passed = true, Done = false; SocketUserToken ExistsUser = UserTokenList[UserID]; if (ExistsUser != null) { Socket curSkt = ExistsUser.ConnectSocket; string curIPAddress = ExistsUser.ConnectSocket.RemoteEndPoint.ToString(); #region " 發送離線 " try { List<byte[]> msgs = ParseProtocol.ConvertMsgToByte("00", "7", UserID, DataTerminalName, "", Guid.NewGuid().ToString().Replace("-", "").ToUpper(), ""); //如果2秒內得不到之前登陸的對象,就直接干掉 //否則此帳號將永遠無法登錄 if (Monitor.TryEnter(curSkt, 2000)) { try { //向用戶本人發送離線 foreach (byte[] msg in msgs) { //2016/05/19 test ExistsUser.AsyncSendAgent.DoSendBuffer(msg); //curSkt.Send(msg); //SendData(curSkt, msg); } } catch (SocketException ex) { LogHelper.WriteLog("DoPcMobileLogin()", "執行向" + DataGroup + "用戶: " + UserID + " [" + curIPAddress + "] 發送強制離線[不允許自動重連]消息時:" + (ex != null ? ex.Message : "")); } finally { Monitor.Exit(curSkt); } } else { //如果到這里說明之前登陸的對象已經死鎖 LogHelper.WriteLog("DoPcMobileLogin()", "嘗試使用Monitor.TryEnter鎖定" + DataGroup + "用戶: " + UserID + " [" + curIPAddress + "] 時失敗."); } } catch (SocketException ex) { LogHelper.WriteLog("DoPcMobileLogin()", "向" + DataGroup + "用戶: " + UserID + " [" + curIPAddress + "] 發送離線[不允許自動重連]消息時:" + (ex != null ? ex.Message : "")); } try { //運行到這里時,有可能客戶端收到7-00之後主動斷開連接,則會觸發CloseClientSocket. if (!ExistsUser.Closing) { //ExistsUser.Closing = true; CloseClientSocket(ExistsUser); } LogHelper.WriteLog("DoPcMobileLogin", string.Format("因相同帳號再次登錄,強制先登錄的用戶{0} [{1}] 下線.", UserID, curIPAddress)); UpdateUserLog(UserID, "1", "0", "因相同帳號再次登陸而強制離線.", "", null); } catch (SocketException ex) { LogHelper.WriteLog("DoPcMobileLogin()", "嘗試關閉" + DataGroup + "用戶: " + UserID + " [" + curIPAddress + "] 線程和Socket連接時出錯." + (ex != null ? ex.Message : "")); } #endregion } // 驗證是否為唯一用戶 if (UserTokenList.ContainsKey(UserID)) { //到這里說明前面強制離線沒有成功 try { List<byte[]> msgs = ParseProtocol.ConvertMsgToByte("登錄失敗.", "3", UserID, DataTerminalName, "", Guid.NewGuid().ToString().Replace("-", "").ToUpper(), ""); //鎖住線程,保證本次發送完成後其他線程才能對此用戶發送消息 lock (userToken.ConnectSocket) { foreach (byte[] msg in msgs) { //同步發送 //2016/05/19 test userToken.AsyncSendAgent.DoSendBuffer(msg); //userToken.ConnectSocket.Send(msg); //SendData(userToken.ConnectSocket, msg); } } LogHelper.WriteLog("ProcessLogin()", DataGroup + "用戶:" + UserID + "登錄失敗"); } catch (SocketException ex) { LogHelper.WriteLog("ProcessLogin()", "發送登陸失敗信息時" + (ex != null ? ex.Message : "")); } } else { // 服務器開放登錄功能 try { List<byte[]> msgs = ParseProtocol.ConvertMsgToByte("登錄成功.", "2", UserID, DataTerminalName, DataGroup, Guid.NewGuid().ToString().Replace("-", "").ToUpper(), ""); //鎖住線程,保證本次發送完成後其他線程才能對此用戶發送消息 lock (userToken.ConnectSocket) { foreach (byte[] msg in msgs) { //2016/05/19 test userToken.AsyncSendAgent.DoSendBuffer(msg); //userToken.ConnectSocket.Send(msg); //SendData(userToken.ConnectSocket, msg); } } } catch (Exception ex) { Passed = false; LogHelper.WriteLog("ProcessLogin()", "發送登錄成功信息時" + (ex != null ? ex.Message : "")); } if (Passed) { //設置對象狀態 userToken.BindingUser = new User(); userToken.BindingUser.State = User.UserStates.Online; userToken.BindingUser.Role = (DataGroup.Length > 0 ? User.UserRole.Mobile : User.UserRole.PC); //移動設備登錄為: //iPhone = "IPHONE" //iPad = "IPAD" //Android Phone = "APHONE" //Android Pad = "APAD" switch (DataGroup.ToUpper()) { case "IPHONE": userToken.BindingUser.Mobile = User.UserMobileType.iPhone; //iToken userToken.BindingUser.iToken = DataText; break; case "IPAD": userToken.BindingUser.Mobile = User.UserMobileType.iPad; //iToken userToken.BindingUser.iToken = DataText; break; case "APHONE": userToken.BindingUser.Mobile = User.UserMobileType.AndroidPhone; break; case "APAD": userToken.BindingUser.Mobile = User.UserMobileType.AndroidPad; break; default: //默認PC userToken.BindingUser.Mobile = User.UserMobileType.None; break; } userToken.BindingUser.ID = UserID; //Mac userToken.BindingUser.MacAddr = DataTerminalName; //IP userToken.BindingUser.IP = ((IPEndPoint)userToken.ConnectSocket.RemoteEndPoint).Address.ToString(); //設備名稱 userToken.BindingUser.DevName = DataText; Socket newClient = userToken.ConnectSocket; string UserRegion = ""; using (SQLHelper conn = new SQLHelper(DBInfo)) { conn.OpenConnection(); try { //取消日誌 //LogHelper.WriteLog("ProcessLogin()", "#5.登錄成功,準備截入聯絡人信息及推送未讀消息. " + UserID + " [" + userToken.ConnectSocket.RemoteEndPoint + "]"); #region " 載入聯絡人清單(聯絡人變更時應更新) " DataTable ContactsList = GetContacts(conn, UserID); if (ContactsList.Rows.Count > 0) { foreach (DataRow Contact in ContactsList.Rows) { //加入聯絡人(防呆自己加自己) if (UserID != Contact["contact_id"].ToString()) userToken.BindingUser.Contacts.Add(Contact["contact_id"].ToString()); } } ContactsList = null; #endregion #region " 當PC端和Mobile端未登錄的情況下才向其聯絡人推送用戶上線消息 " if (!m_pcUserTokenList.ContainsKey(UserID) && !m_mobileUserTokenList.ContainsKey(UserID)) //ID,狀態,簽名,聯絡人清單,頭像數據 if (userToken.BindingUser.Contacts.Count > 0) StateNotifyQueue.Enqueue(new List<object>() { userToken.BindingUser.ID, "1", userToken.BindingUser.Signature, userToken.BindingUser.Contacts, null }); #endregion #region " 發送所有未讀訊息和群命令 " #region " 訊息 " DataTable MsgsList = GetUnreadMsgs(conn, UserID, "M"); if (MsgsList.Rows.Count > 0) { if (newClient != null) { if (MsgsList.Rows.Count > 0) { long MaxMsgNO = 0; foreach (DataRow UnreadMsg in MsgsList.Rows) { // 記錄最大MsgNO if (MaxMsgNO < long.Parse(UnreadMsg["msg_no"].ToString())) MaxMsgNO = long.Parse(UnreadMsg["msg_no"].ToString()); // 發給登錄人 DateTime MsgDT = DateTime.Parse(UnreadMsg["msg_date"].ToString() + " " + UnreadMsg["msg_time"].ToString()); // 判斷個人消息還是群消息 // 消息子類型(msg_doctype)(文字消息/文件消息) // 0 = File // 1 = Text Msg // 2 = offline File // 3 = 截圖 // 4 = email // 5 = 抖動 // 6 = 音頻 List<byte[]> rtnMsgs = null; switch (UnreadMsg["msg_doctype"].ToString()) { case "0": rtnMsgs = ParseProtocol.ConvertMsgToByte(UnreadMsg["msg_text"].ToString(), "P", UnreadMsg["msg_from"].ToString(), UnreadMsg["user_name"].ToString(), "S", MsgDT, UnreadMsg["msg_guid"].ToString(), UnreadMsg["msg_no"].ToString(), ""); break; case "2": rtnMsgs = ParseProtocol.ConvertMsgToByte(UnreadMsg["msg_text"].ToString(), "P", UnreadMsg["msg_from"].ToString(), UnreadMsg["user_name"].ToString(), "O", MsgDT, UnreadMsg["msg_guid"].ToString(), UnreadMsg["msg_no"].ToString(), ""); break; case "3": rtnMsgs = ParseProtocol.ConvertMsgToByte((byte[])UnreadMsg["msg_img"], "I", UnreadMsg["msg_from"].ToString(), UnreadMsg["user_name"].ToString(), UnreadMsg["td_no"].ToString(), MsgDT, UnreadMsg["msg_guid"].ToString(), UnreadMsg["msg_no"].ToString(), ""); break; case "4": rtnMsgs = ParseProtocol.ConvertMsgToByte(UnreadMsg["msg_text"].ToString(), "M", UnreadMsg["msg_from"].ToString(), UnreadMsg["user_name"].ToString(), UnreadMsg["td_no"].ToString(), MsgDT, UnreadMsg["msg_guid"].ToString(), UnreadMsg["msg_no"].ToString(), ""); break; case "5": rtnMsgs = ParseProtocol.ConvertMsgToByte(UnreadMsg["msg_text"].ToString(), "Q", UnreadMsg["msg_from"].ToString(), UnreadMsg["user_name"].ToString(), UnreadMsg["td_no"].ToString(), MsgDT, UnreadMsg["msg_guid"].ToString(), UnreadMsg["msg_no"].ToString(), ""); break; case "6": rtnMsgs = ParseProtocol.ConvertMsgToByte("Voice", "R", UnreadMsg["msg_from"].ToString(), UnreadMsg["user_name"].ToString(), UnreadMsg["td_no"].ToString(), MsgDT, UnreadMsg["msg_guid"].ToString(), UnreadMsg["msg_no"].ToString(), ""); break; default: //1 string DataType = ""; if (UnreadMsg["msg_type"].ToString() == "0") { //小助手系統消息 DataType = "5"; } else { DataType = UnreadMsg["td_no"].ToString().Length > 0 ? "4" : (UnreadMsg["msg_doctype"].ToString() == "0" ? "P" : "U"); } rtnMsgs = ParseProtocol.ConvertMsgToByte(UnreadMsg["msg_text"].ToString(), DataType, UnreadMsg["msg_from"].ToString(), UnreadMsg["user_name"].ToString(), UnreadMsg["td_no"].ToString(), MsgDT, UnreadMsg["msg_guid"].ToString(), UnreadMsg["msg_no"].ToString(), ""); break; } //if (UnreadMsg["msg_doctype"].ToString() == "3") //{ // rtnMsgs = ParseProtocol.ConvertMsgToByte((byte[])UnreadMsg["msg_img"], // "I", // UnreadMsg["msg_from"].ToString(), // UnreadMsg["user_name"].ToString(), // UnreadMsg["td_no"].ToString(), // MsgDT, // UnreadMsg["msg_guid"].ToString(), // UnreadMsg["msg_no"].ToString(), // ""); //} //else //{ // rtnMsgs = ParseProtocol.ConvertMsgToByte(UnreadMsg["msg_text"].ToString(), // UnreadMsg["td_no"].ToString().Length > 0 ? "4" : (UnreadMsg["msg_doctype"].ToString() == "0" ? "P" : "U"), // UnreadMsg["msg_from"].ToString(), // UnreadMsg["user_name"].ToString(), // UnreadMsg["td_no"].ToString(), // MsgDT, // UnreadMsg["msg_guid"].ToString(), // UnreadMsg["msg_no"].ToString(), // ""); //} //鎖住線程,保證本次發送完成後其他線程才能對此用戶發送消息 lock (newClient) { foreach (byte[] msg in rtnMsgs) { //2016/05/19 test userToken.AsyncSendAgent.DoSendBuffer(msg); //newClient.Send(msg); //SendData(newClient, msg); } } } // 更新為已讀 try { MarkReadHistoryMsgQueue.Enqueue("update a set a.msg_state='1' from lrmsgstate a where a.msg_no<=" + MaxMsgNO.ToString() + " and a.msg_to='" + UserID + "' and a.msg_state='0'"); } catch (Exception ex) { LogHelper.WriteLog("ProcessLogin()", "標識歷史信息為已讀時:" + (ex != null ? ex.Message : "")); } } } //Test LogHelper.WriteLog("ProcessLogin()", "共向" + DataGroup + "用戶:" + userToken.BindingUser.ID + "推送" + MsgsList.Rows.Count.ToString() + "條歷史未讀消息."); } #endregion #region " 系統消息 " MsgsList = GetUnreadMsgs(conn, UserID, "S"); if (MsgsList.Rows.Count > 0) { if (newClient != null) { if (MsgsList.Rows.Count > 0) { long MaxMsgNO = 0; foreach (DataRow UnreadMsg in MsgsList.Rows) { // 記錄最大MsgNO if (MaxMsgNO < long.Parse(UnreadMsg["sys_id"].ToString())) MaxMsgNO = long.Parse(UnreadMsg["sys_id"].ToString()); // 發給登錄人 DateTime MsgDT = DateTime.Parse(UnreadMsg["sys_date"].ToString() + " " + UnreadMsg["sys_time"].ToString()); // 判斷個人消息還是群消息 List<byte[]> rtnMsgs = ParseProtocol.ConvertMsgToByte(UnreadMsg["sys_remk"].ToString(), UnreadMsg["sys_type"].ToString() == "1" ? "D" : "*", UnreadMsg["sys_type"].ToString(), UnreadMsg["sys_objtype"].ToString(), "*", MsgDT, UnreadMsg["sys_objid"].ToString(), UnreadMsg["sys_id"].ToString(), ""); //鎖住線程,保證本次發送完成後其他線程才能對此用戶發送消息 lock (newClient) { foreach (byte[] msg in rtnMsgs) { //2016/05/19 test userToken.AsyncSendAgent.DoSendBuffer(msg); //newClient.Send(msg); //SendData(newClient, msg); } } } // 更新為已讀 try { MarkReadHistoryMsgQueue.Enqueue("update a set a.sysmsg_state='1' from lrsysmsgstate a where a.sys_id<=" + MaxMsgNO.ToString() + " and a.sysmsg_to='" + UserID + "' and a.sysmsg_state='0'"); //conn.ExecuteSQL("update a set a.msg_state='1' from lrmsgstate a where a.msg_no<=" + MaxMsgNO.ToString() + " and a.msg_to='" + UserID + "' and a.msg_state='0'"); } catch (Exception ex) { LogHelper.WriteLog("ProcessLogin()", "標識歷史系統消息為已讀時:" + (ex != null ? ex.Message : "")); } } } //Test //LogHelper.WriteLog("ProcessLogin()", "共向用戶:" + User.ID + "推送" + MsgsList.Rows.Count.ToString() + "條歷史命令."); } #endregion MsgsList.Dispose(); MsgsList = null; #endregion //得到用戶區域代號 try { UserRegion = conn.OpenDataTable("select user_region from lrtduser (nolock) where user_no='" + UserID + "'", CommandType.Text).Rows[0][0].ToString(); } catch { UserRegion = ""; } } catch (SocketException ex) { //test //clsClientLog.WriteLog(clsClientLog.LogType.Error, "StartUp()", "向PC端" + DataTerminal + "推送未讀信息時出錯." + (ex != null ? ex.Message : "")); LogHelper.WriteLog("ProcessLogin()", "向" + DataGroup + "用戶:" + UserID + "推送未讀信息和系統消息時出錯." + (ex != null ? ex.Message : "")); } //更新用戶狀態 try { DoUpdateUserLog(conn, UserID, "0", ((int)userToken.BindingUser.Mobile).ToString(), DataGroup + "登錄", ((IPEndPoint)userToken.ConnectSocket.RemoteEndPoint).Address.ToString(), DataText, DataTerminalName, null); DoUpdateUserState(conn, UserID, "1"); //上線中 } catch (Exception ex) { Passed = false; LogHelper.WriteLog("ProcessLogin()", "更新" + DataGroup + "用戶:" + UserID + "狀態." + (ex != null ? ex.Message : "")); } conn.CloseConnection(); } //只推PC端用戶 if (Passed && DataGroup.Length == 0) { #region " 向PC客戶端推送平臺數據(在線人數等) " try { List<byte[]> MsgToPC = ParseProtocol.ConvertMsgToByte(string.Format(SysMessage, ActiveUsersCount, OnlineUsersCount, RegisteredUsersCount, TodayMessagesCount, TotalMessagesCount), "9", "", "", "", "", ""); //鎖住線程,保證本次發送完成後其他線程才能對此用戶發送消息 lock (newClient) { foreach (byte[] msg in MsgToPC) { //2016/05/19 test userToken.AsyncSendAgent.DoSendBuffer(msg); //newClient.Send(msg); //SendData(newClient, msg); } } } catch (SocketException ex) { //這個沒必要斷開 //Passed = false; //clsClientLog.WriteLog(clsClientLog.LogType.Error, "StartUp()", "向PC端" + DataTerminal + "推送未讀信息時出錯." + (ex != null ? ex.Message : "")); LogHelper.WriteLog("ProcessLogin()", "向PC端" + UserID + "推送平臺數據(在線人數等)." + (ex != null ? ex.Message : "")); } #endregion #region " 向PC客戶端推送系統公告 " try { if (SysNotices.Count > 0 && UserRegion.Length > 0 && (SysNotices.ContainsKey(UserRegion) || SysNotices.ContainsKey("ALL"))) { List<NoticeMsg> nMsgs; // 全服廣播 object AllNotices = SysNotices["ALL"]; if (AllNotices != null) { nMsgs = (List<NoticeMsg>)AllNotices; List<byte[]> MsgNotice; foreach (NoticeMsg nMsg in nMsgs) { MsgNotice = ParseProtocol.ConvertMsgToByte(nMsg.MsgText, "B", "1", nMsg.MsgSeq, "ALL", "", ""); //鎖住線程,保證本次發送完成後其他線程才能對此用戶發送消息 lock (newClient) { foreach (byte[] msg in MsgNotice) { //2016/05/19 test userToken.AsyncSendAgent.DoSendBuffer(msg); //newClient.Send(msg); //SendData(newClient, msg); } } } } // 區域廣播 object RegionNotices = SysNotices[UserRegion]; if (RegionNotices != null) { nMsgs = (List<NoticeMsg>)RegionNotices; List<byte[]> MsgNotice; foreach (NoticeMsg nMsg in nMsgs) { MsgNotice = ParseProtocol.ConvertMsgToByte(nMsg.MsgText, "B", "1", nMsg.MsgSeq, UserRegion, "", ""); //鎖住線程,保證本次發送完成後其他線程才能對此用戶發送消息 lock (newClient) { foreach (byte[] msg in MsgNotice) { //2016/05/19 test userToken.AsyncSendAgent.DoSendBuffer(msg); //newClient.Send(msg); //SendData(newClient, msg); } } } } } } catch (SocketException ex) { //這個沒必要斷開 //Passed = false; //clsClientLog.WriteLog(clsClientLog.LogType.Error, "StartUp()", "向PC端" + DataTerminal + "推送未讀信息時出錯." + (ex != null ? ex.Message : "")); LogHelper.WriteLog("ProcessLogin()", "向PC端" + UserID + "推送系統公告." + (ex != null ? ex.Message : "")); } #endregion } if (Passed) { //添加到正在執行的列表中 if (UserTokenList.ContainsKey(UserID)) { ExistsUser = UserTokenList[UserID]; if (ExistsUser != null) { try { LogHelper.WriteLog("DoPcMobileLogin", string.Format("加入在線人員列表時發現已有同名帳號在里面,強制用戶{0} [{1}] 下線.", UserID, ExistsUser.ConnectSocket.RemoteEndPoint)); if (!ExistsUser.Closing) { //ExistsUser.Closing = true; CloseClientSocket(ExistsUser); } } catch (SocketException ex) { LogHelper.WriteLog("ProcessLogin()", "嘗試關閉" + DataGroup + "用戶" + UserID + "線程和Socket連接時出錯." + (ex != null ? ex.Message : "")); } } //LogHelper.WriteLog("ProcessLogin()", "#5.加入在線人員列表時發現已有同名帳號在里面. " + UserID + " [" + userToken.ConnectSocket.RemoteEndPoint + "]"); } UserTokenList.Add(UserID, userToken); //取消日志 //LogHelper.WriteLog("ProcessLogin()", "#6.已加入到在線人員列表. " + UserID + " [" + userToken.ConnectSocket.RemoteEndPoint + "]"); //System.Diagnostics.Debug.WriteLine(UserID + " Logged in at " + DateTime.Now.ToString("HH:mm:ss:fff")); Done = true; } } } return Done; }
public AudioCallProcessor(AsyncSocketServer asyncSocketServer, SocketUserToken userToken) : base(asyncSocketServer, userToken) { this.ApplyChannelTofSender(); }
/// <summary> /// 處理命令 /// </summary> /// <returns></returns> public override bool ProcessCommand(SocketUserToken userToken) { //開始解包 DynamicBufferManager receiveBuffer = userToken.ReceiveBuffer; string curUserID = userToken.BindingUser.ID; string curDevice = (userToken.BindingUser.Role == User.UserRole.PC ? "PC客戶端" : "Mobile客戶端"); try { //返回解包后的資料(返回數組 0=type ; 1=終端; 2=群號; 3=時間; 4=消息ID; 5=消息內容 6=終端姓名 7=消息系統編號 8=設置漫遊) int msgDataLength = 0; object[] ReceiveData = ParseProtocol.UnpackMsgPackage(receiveBuffer.Buffer, out msgDataLength); string DataType = ReceiveData[0].ToString(); //如果數據類別為空就踢出 if (DataType == "") { LogHelper.WriteLog("PcProcessor.ProcessCommand", "讀取" + curDevice + "用戶: @<" + curUserID + " " + userToken.ConnectSocket.RemoteEndPoint + "> 數據時連接異常斷開."); //主動關閉連接 //userToken.Closing = true; return false; } string DataTerminal = ReceiveData[1].ToString(); string DataGroup = ReceiveData[2].ToString(); string DataGUID = ReceiveData[4].ToString(); string DataBody = ((DataType != "I" && DataType != "R") ? ReceiveData[5].ToString() : "N/A"); // 非屏幕截圖時才有值 byte[] DataImage = ((DataType == "I" || DataType == "R") ? (byte[])ReceiveData[5] : null); // 屏幕截圖時才有值 string DataTerminalName = ReceiveData[6].ToString(); //Added by Donnie on 2016/04/27 //如果是換頭像也不會有值 if (DataType == "S" && DataTerminalName == "8") { DataBody = "N/A"; DataImage = (byte[])ReceiveData[5]; } // 分類處理 switch (DataType) { case "1": #region " 用戶離線 " LogHelper.WriteLog("PcProcessor.ProcessCommand", curDevice + "用戶: @<" + curUserID + " " + userToken.ConnectSocket.RemoteEndPoint + "> 離線."); #endregion //主動關閉連接 //userToken.Closing = true; return false; case "5": #region " 群(異動)系統訊息 " base.SendGroupSysMessage(DataTerminal, DataGroup, DataGUID, DataBody); #endregion break; case "6": #region " 心跳包 " base.DoAlive(DataTerminal, DataBody, DataType, DataGUID); #endregion break; case "7": #region " 控制臺消息(7-07/7-08) " base.ProcessConsoleCommands(DataBody); #endregion break; case "8": #region " 發送在線用戶ID清單 " base.PushOnlineUserList(DataGroup, DataType, DataGUID); #endregion break; case "B": #region " 系統廣播/公告 - 管理員專用 " try { //DataTerminal:命令(1=播放;0=停止) //DataTerminalName: 項次 //DataGroup: 區域(ALL=全服播放;代號=單區播放) //DataBody: 公告內容 //using (SQLHelper conn = new SQLHelper(m_asyncSocketServer.DBInfo)) //{ // conn.OpenConnection(); // try // { // if (conn.OpenDataTable("select uid from lrbroadcast (nolock) where region_id = '" + DataGroup + "' and bdc_seq = '" + DataTerminalName + "'", CommandType.Text).Rows.Count > 0) // { // //有資料 // conn.ExecuteSQL("update a set a.bdc_cmd='" + DataTerminal + "',a.bdc_text=N'" + DataBody + "' from lrbroadcast a where a.region_id = '" + DataGroup + "' and a.bdc_seq = '" + DataTerminalName + "'"); // } // else // { // //沒資料 // if (DataTerminal == "1") // { // conn.ExecuteSQL("insert into lrbroadcast (region_id,bdc_seq,bdc_text,bdc_cmd) values('" + DataGroup + "','" + DataTerminalName + "',N'" + DataBody + "','" + DataTerminal + "')"); // } // } // conn.CloseConnection(); // } // catch // { // conn.CloseConnection(); // } //} ////1.更新公告內容和區域 //lock (base.m_asyncSocketServer.SysNotices.SyncRoot) //{ // if (base.m_asyncSocketServer.SysNotices.ContainsKey(DataGroup)) // { // //ALL 也會存里面 // List<NoticeMsg> nMsgs = (List<NoticeMsg>)base.m_asyncSocketServer.SysNotices[DataGroup]; // NoticeMsg nMsg = nMsgs.Find(delegate(NoticeMsg _msg) { return _msg.MsgSeq == DataTerminalName; }); // if (nMsg != null) // { // if (DataTerminal == "1") // { // // 替換現有的公告 // nMsg.MsgText = DataBody; // } // else // { // // 移除現有的公告 // nMsgs.Remove(nMsg); // if (nMsgs.Count == 0) // { // // 當前區域沒有公告了,將區域移除 // base.m_asyncSocketServer.SysNotices.Remove(DataGroup); // } // } // } // else // { // if (DataTerminal == "1") // { // // 新公告 // nMsgs.Add(new NoticeMsg() { MsgSeq = DataTerminalName, MsgText = DataBody }); // } // } // } // else // { // if (DataTerminal == "1") // { // //新公告 // base.m_asyncSocketServer.SysNotices.Add(DataGroup, new List<NoticeMsg>() { new NoticeMsg() { MsgSeq = DataTerminalName, MsgText = DataBody } }); // } // } //} //2.向區域內的用戶播放 base.SendSysNotice(DataTerminal, DataTerminalName, DataGroup, DataBody); } catch (Exception ex) { LogHelper.WriteLog("PcProcessor.ProcessCoommand", "發送服務器廣播時:" + (ex != null ? ex.Message : "")); } #endregion break; case "H": #region " 發送服務器健康報告 " try { //[#1]:120 [PC端用戶] //[#2]:12 [移動端用戶] //[#3]:10 [訂閱號服務端] //[#4]:12/50 [空閒UserToken] //[#5]:10 [APP用戶] //[#6]:2 [文件傳送用戶] //[#7]:0 [歷史消息標記已讀隊列] //[#8]:89 [用戶狀態推送隊列] //[#9]:0/10 [空間狀態推送線程] //[#10]:10 [設備數據更新隊列] //[#11]:10 [語音通話客戶] DataBody = ""; //[PC端用戶] DataBody += "[#1]:" + m_asyncSocketServer.PcUserTokenList.Count.ToString() + "|"; //[移動端用戶] DataBody += "[#2]:" + m_asyncSocketServer.MobileUserTokenList.Count.ToString() + "|"; //[訂閱號服務端] DataBody += "[#3]:" + m_asyncSocketServer.SubServiceTokenList.Count.ToString() + "|"; //[空閒UserToken] DataBody += "[#4]:" + m_asyncSocketServer.IdleUserTokenPool.Count.ToString() + "/" + m_asyncSocketServer.MaxServerLoading.ToString() + "|"; //[APP用戶] DataBody += "[#5]:" + m_asyncSocketServer.AppUserTokenList.Count.ToString() + "|"; //[文件傳送用戶] DataBody += "[#6]:" + m_asyncSocketServer.FileUserTokenList.Count.ToString() + "|"; //[歷史消息標記已讀隊列] DataBody += "[#7]:" + m_asyncSocketServer.MarkReadHistoryMsgQueue.Count.ToString() + "|"; //[用戶狀態推送隊列] DataBody += "[#8]:" + m_asyncSocketServer.StateNotifyQueue.Count.ToString() + "|"; //[空間狀態推送線程] DataBody += "[#9]:" + m_asyncSocketServer.StateNofityThreadsQueue.Count.ToString() + "/" + m_asyncSocketServer.MaxStateNotifyThreads.ToString() + "|"; //[設備數據更新隊列] DataBody += "[#10]:" + m_asyncSocketServer.VDataQueue.Count.ToString() + "|"; //[語音通話客戶] DataBody += "[#11]:" + m_asyncSocketServer.AudioCallTokenList.Count.ToString(); List<byte[]> msgs = ParseProtocol.ConvertMsgToByte(DataBody, DataType, DataTerminal, DataTerminalName, "", DataGUID); //鎖住線程,保證本次發送完成後其他線程才能對此用戶發送消息 lock (userToken.ConnectSocket) { foreach (byte[] msg in msgs) { //2016/05/19 test userToken.AsyncSendAgent.DoSendBuffer(msg); //userToken.ConnectSocket.Send(msg); //base.SendData(userToken.ConnectSocket, msg); } } //clsClientLog.WriteLog(clsClientLog.LogType.Info, "ThreadFunc(object obj):default", "成功向: " + _receiver + " 發送信息: " + _packetBuff); } catch (Exception ex) { //clsClientLog.WriteLog(clsClientLog.LogType.Error, "ThreadFunc(object obj):default", "發送在線用戶ID清單時:" + ex.Message); LogHelper.WriteLog("PcProcessor.ProcessCoommand", "發送服務器健康報告時時:" + (ex != null ? ex.Message : "")); } #endregion break; case "C": #region " 群命令 " // 抓出群成員 using (SQLHelper conn = new SQLHelper(m_asyncSocketServer.DBInfo)) { conn.OpenConnection(); try { // 記錄到數據庫中 //DateTime MsgDT = DateTime.Now; string MsgNO = this.InsertCmd(conn, DataTerminal, DataBody, DataGroup, DataGUID); if (MsgNO.Length > 0) { // 向管理員發的命令不轉發 if (DataGroup.Length > 0) { //系統將命令轉發給群成員(以發送者的帳號) base.SendGroupCommand(DataTerminal, DataGroup, DataBody, DataTerminalName, conn, DataGUID, MsgNO); } // 轉發完再發回執 // 向發送者發送回執7-03 // 回執給PC端里,必須用DataGUID,因為PC端本地生成的GUID要和這個DataGUID匹配 base.PushFeedback(DataGUID, MsgNO); } conn.CloseConnection(); } catch { conn.CloseConnection(); } } #endregion break; case "D": #region " 撤回消息 " //0001D發送人帳號(50b)接收對象類別(U=人;G=群)(60b)接收人/接收群(50b)GMT時間(20b)要撤回消息的GUID(32b)訊息流水號(20b) using (SQLHelper conn = new SQLHelper(m_asyncSocketServer.DBInfo)) { conn.OpenConnection(); try { // 記錄到數據庫中(1=撤回,M=消息lrmsg) string MsgNO = this.InsertSysMsg(conn, DataTerminal, DataBody, "1", "M", (DataTerminalName == "G" ? DataGroup : ""), (DataTerminalName == "U" ? DataGroup : ""), DataGUID); if (MsgNO.Length > 0) { //系統將訊息轉發給群成員(以發送者的帳號) base.SendGroupSysBroadcast("D", "1", "M", DataTerminal, DataGroup, DataBody, DataTerminalName, conn, DataGUID, MsgNO); // 轉發完再發回執 // 向發送者發送回執7-03 base.PushFeedback(DataGUID, MsgNO); } conn.CloseConnection(); } catch { conn.CloseConnection(); } } #endregion break; case "G": case "L": #region " 群組訊息(G=Push / L=Pull) " base.SendGroupBroadcast(DataTerminal, DataGroup, DataBody, DataTerminalName, DataGUID, DataType == "G"); #endregion break; case "I": #region " 轉發屏幕截圖 " //DataTerminal是接收人帳號(對人) //DataTerminal是發送人帳號(對群) //DataTerminalName是發送人姓名 base.SendImage(DataTerminal, DataGroup, DataImage, DataTerminalName, DataGUID); #endregion break; case "U": //人對人信息 case "T": //測試工具使用 #region " 用戶訊息 " //別搞暈了 //DataTerminal是接收人帳號 //DataTerminalName是發送人姓名 using (SQLHelper conn = new SQLHelper(m_asyncSocketServer.DBInfo)) { conn.OpenConnection(); try { // 記錄到數據庫中 //DateTime MsgDT = DateTime.Now; //T=測試用(超哥神器) string MsgNO = this.InsertMsg(conn, curUserID, DataBody, "", DataTerminal, "1", DataGUID, DataType == "T", true); if (MsgNO.Length > 0) { //開始發送 base.SendP2PMessage(curUserID, DataTerminalName, DataTerminal, DataBody, conn, DataGUID, MsgNO, DataType); // 轉發完再發回執 // 向發送者發送回執7-03 base.PushFeedback(DataGUID, MsgNO); } conn.CloseConnection(); } catch { conn.CloseConnection(); } } #endregion break; case "Q": //抖動 #region " 抖動 " //DataTerminal是接收人帳號 //DataTerminalName是發送人姓名 using (SQLHelper conn = new SQLHelper(m_asyncSocketServer.DBInfo)) { conn.OpenConnection(); try { // 記錄到數據庫中 //DateTime MsgDT = DateTime.Now; string MsgNO = this.InsertMsg(conn, curUserID, DataBody, "", DataTerminal, "1", "", "5", "0", DataGUID, false, true); if (MsgNO.Length > 0) { //開始發送 base.SendP2PMessage(curUserID, DataTerminalName, DataTerminal, DataBody, conn, DataGUID, MsgNO, DataType); // 轉發完再發回執 // 向發送者發送回執7-03 base.PushFeedback(DataGUID, MsgNO); } conn.CloseConnection(); } catch { conn.CloseConnection(); } } #endregion break; case "R": #region " 轉發錄音 " //DataTerminal是接收人帳號(對人) //DataTerminal是發送人帳號(對群) //DataTerminalName是發送人姓名 //ReceiveData[7].ToString()是語音時長 base.SendVoice(DataTerminal, DataGroup, DataImage, DataTerminalName, DataGUID, ReceiveData[7].ToString()); #endregion break; case "S": #region " 聯絡人狀態變更 " //通知在線的聯絡人當前用戶改變了狀態 //0=空閒|1=上線|2=離開|3=忙碌|4=不要打擾|5=隱身|6=離線|7=簽名|8=換頭像 userToken.BindingUser.SetUserState(DataTerminalName); //特殊處理簽名操作 if (DataTerminalName == "7") { //User.ChangeSignature = true; userToken.BindingUser.Signature = DataBody; //signature m_asyncSocketServer.UpdateUserLog(curUserID, "2", "0", userToken.BindingUser.Signature, "", null); } else if (DataTerminalName == "8") { //Added by Donnie on 2016/04/27 // 變更頭像的處理 m_asyncSocketServer.UpdateUserLog(curUserID, "3", "0", "更新頭像", "", DataImage); } //Added by Donnie on 2016/04/27 //ID,狀態,簽名,聯絡人清單,頭像數據 if (userToken.BindingUser.Contacts.Count > 0) m_asyncSocketServer.StateNotifyQueue.Enqueue(new List<object>() { curUserID, DataTerminalName, userToken.BindingUser.Signature, userToken.BindingUser.Contacts, DataImage }); //Added by Donnie on 2016/04/27 //簽名各頭像變更不會影響在線狀態 if (DataTerminalName != "7" && DataTerminalName != "8") m_asyncSocketServer.UpdateUserState(curUserID, DataTerminalName); //state #endregion break; case "P": #region " 請求發送文件和同意接受文件 " //發起發送文件請求: 0019P接收人帳號(50b)發起人姓名(60b)S(50b)GMT時間(20b)文檔流的GUID(32b)訊息流水號(20b) //接收人同意請求: 0019P發起人帳號(50b)接收人姓名(60b)R(50b)GMT時間(20b)文檔流的GUID(32b)訊息流水號(20b) //包體: 文件名|大小|文件完整路徑 //接收方同意接收的請求等到接收方T通道建立完成再轉發給發送方 if (DataGroup != "R" && DataGroup.Length > 0) { switch (DataGroup) { case "Q": //斷開T通道的對象 //0019P發起人帳號(50b)S(60b)Q(50b)GMT時間(20b)文檔流的GUID(32b)訊息流水號(20b) //0019P接收人帳號(50b)R(60b)Q(50b)GMT時間(20b)文檔流的GUID(32b)訊息流水號(20b) if (DataTerminalName == "R") break; //不再主動斷開接收端(R)T通道連接,由客戶端自主斷開. string TID = DataGUID + "-" + DataTerminalName; //LogHelper.WriteLog("ThreadFunc", "準備斷開:" + TID + "的連接..."); SocketUserToken TSocket = m_asyncSocketServer.FileUserTokenList[TID] as SocketUserToken; if (TSocket != null) { //LogHelper.WriteLog("PcProcessor.ProcessCommand", "從S通道斷開:" + TID + "."); try { //主動關閉連接 //TSocket.Closing = true; m_asyncSocketServer.CloseClientSocket(TSocket); } catch { LogHelper.WriteLog("CloseClientSocket", "無法從S通道斷開:" + TID + ",可能已從T通道斷開."); } } else { LogHelper.WriteLog("PcProcessor.ProcessCommand", "沒有找到:" + TID + "."); } break; default: string MsgNO = base.ProcessTransferFileRequest(curUserID, DataType, DataTerminal, DataGroup, DataGUID, DataBody, DataTerminalName); // 向發送者發送回執7-03 base.PushFeedback(DataGUID, MsgNO); break; } } #endregion break; case "K": #region " 踢人7-06 " //0010K踢出目標帳號(50b)空格(60)空格(60)時間(20b)GUID(32b)訊息流水號(20b) //包體:P/M (P=PC端;M=移動端) //向被踢者發送回執7-06 SocketUserToken KickTarget = m_asyncSocketServer.PcUserTokenList[DataTerminal] as SocketUserToken; if (DataBody == "M") { KickTarget = m_asyncSocketServer.MobileUserTokenList[DataTerminal] as SocketUserToken; //if (KickTarget == null) //{ // KickTarget = _mobile_transmit_tb[DataTerminal] as UserSocket; //} } if (KickTarget != null) { Socket KickTargetSkt = KickTarget.ConnectSocket; bool ForceKickOut = DataGroup == "F"; //為F表示從服務器端強制斷開 try { //回執給PC端里,必須用DataGUID,因為PC端本地生成的GUID要和這個DataGUID匹配 List<byte[]> msgs = ParseProtocol.ConvertMsgToByte("06", "7", "", "", "", DataGUID, ""); //鎖住線程,保證本次發送完成後其他線程才能對此用戶發送消息 lock (KickTargetSkt) { if (KickTargetSkt.Connected) { foreach (byte[] msg in msgs) { //2016/05/19 test KickTarget.AsyncSendAgent.DoSendBuffer(msg); //KickTargetSkt.Send(msg); //base.SendData(KickTargetSkt, msg); } } } } catch (SocketException ex) { //客戶端異常,強踢 ForceKickOut = true; //clsClientLog.WriteLog(clsClientLog.LogType.Error, "ThreadFunc(object obj):default", "發送消息回執時:" + ex.Message); LogHelper.WriteLog("PcProcessor.ProcessCommand", "發送消息回執時:" + (ex != null ? ex.Message : "")); } #region " 用戶在線就讓其自動登出,否則直接踢出 " //開踢 if (ForceKickOut) { //主動關閉連接 //KickTarget.Closing = true; m_asyncSocketServer.CloseClientSocket(KickTarget); } #endregion } else { //Kick FileTransferID KickTarget = m_asyncSocketServer.FileUserTokenList[DataTerminal] as SocketUserToken; if (KickTarget != null) { //主動關閉連接 //KickTarget.Closing = true; m_asyncSocketServer.CloseClientSocket(KickTarget); } } //通知管理員執行成功7-03 base.PushFeedback(DataGUID, ""); #endregion break; case "W": #region " 訂閱號消息 " if (DataGroup.Length > 0) { //用戶向訂閱號發命令 SendRequestToSubService(DataTerminal, DataGroup, DataBody, DataTerminalName, DataGUID, true); } #endregion break; case "X": #region " 請求和接受語音通話 " //發起請求: 0019X接收人帳號(50b)發起人姓名(60b)S(50b)GMT時間(20b)語音流的GUID(32b)訊息流水號(20b)空格(1b) //接受請求: 0019X發起人帳號(50b)接收人姓名(60b)R(50b)GMT時間(20b)語音流的GUID(32b)訊息流水號(20b)空格(1b) //包體: 內容暫時沒用 //接收方同意接收的請求等到接收方T通道建立完成再轉發給發送方 if (DataGroup.Length > 0) { switch (DataGroup) { case "R": //不做任何動作 break; case "A": //斷開T通道的對象 //0019X接收人帳號(50b)發起人姓名(60b)A(50b)GMT時間(20b)語音流的GUID(32b)訊息流水號(20b)空格(1b) //0019X發起人帳號(50b)接收人姓名(60b)A(50b)GMT時間(20b)語音流的GUID(32b)訊息流水號(20b)空格(1b) base.PushMessage("", ParseProtocol.ConvertMsgToByte(DataBody, DataType, curUserID, "AudioUser", DataGroup, DataGUID, "-1"), DataTerminal, m_asyncSocketServer.PcUserTokenList, "ProcessCommand-X"); break; default: base.ProcessAudioCallRequest(curUserID, DataType, DataTerminal, DataGroup, DataGUID, DataBody, DataTerminalName); break; } } #endregion break; case "Z": #region " 記錄登錄流量和耗時數據 " //DataBody包體格式: //類別A;流量(Byte);耗時(ms);開始(當地)日期(yyyy/MM/dd);開始(當地)時間(HH:mm:ss.fff);結束(當地)日期(yyyy/MM/dd);結束(當地)時間(HH:mm:ss.fff)|類別B;流量(Byte);耗時(ms);開始(當地)日期(yyyy/MM/dd);開始(當地)時間(HH:mm:ss.fff);結束(當地)日期(yyyy/MM/dd);結束(當地)時間(HH:mm:ss.fff) //邏輯: //根據帳號+IP抓到lrlxlog里此用戶最大UID作業log_uid base.SaveLoginStatData(userToken, DataTerminal, DataBody); #endregion break; } //System.Diagnostics.Debug.WriteLine("清除:" + receiveBuffer.DataCount.ToString() + "個Byte."); //把已處理的數據從緩存里清除 //receiveBuffer.Clear(receiveBuffer.DataCount); receiveBuffer.Clear(msgDataLength); //扣除已發完的包 base.m_readyPackage--; //發完一個包后,如果DataCount>0說明有粘包數據,嘗試發送出去,否則會影響後面數據的發送效率. //如果粘包不是一個完整的數據包就返回去接收數據 //while (receiveBuffer.DataCount > 0) //{ // try // { // ProcessCommand(userToken); // } // catch // { // // 繼續收 // return true; // } //} if (base.m_readyPackage > 0) return ProcessCommand(userToken); else return true; //System.Diagnostics.Debug.WriteLine("剩餘:" + receiveBuffer.DataCount.ToString() + "個Byte."); //test //System.Diagnostics.Debug.WriteLine(DateTime.Now.ToLongTimeString() + " - " + DataType); //return true; } catch (Exception ex) { LogHelper.WriteLog("PcProcessor.ProcessCommand", string.Format("執行" + curDevice + "用戶:{0}命令時時出錯:{1}.", "@<" + userToken.BindingUser.ID + " " + userToken.ConnectSocket.RemoteEndPoint + "> ", (ex != null ? ex.Message : ""))); //主動關閉連接 //userToken.Closing = true; return false; } }
/// <summary> /// 保存登錄流量和耗時數據 /// </summary> /// <param name="userToken"></param> /// <param name="DataTerminal"></param> /// <param name="DataBody"></param> protected void SaveLoginStatData(SocketUserToken userToken, string DataTerminal, string DataBody) { //DataBody包體格式: //類別A;流量(Byte);耗時(ms);開始(當地)日期(yyyy/MM/dd);開始(當地)時間(HH:mm:ss.fff);結束(當地)日期(yyyy/MM/dd);結束(當地)時間(HH:mm:ss.fff)|類別B;流量(Byte);耗時(ms);開始(當地)日期(yyyy/MM/dd);開始(當地)時間(HH:mm:ss.fff);結束(當地)日期(yyyy/MM/dd);結束(當地)時間(HH:mm:ss.fff) //邏輯: //根據帳號+IP抓到lrlxlog里此用戶最大UID作業log_uid string[] StatData = DataBody.Split('|'); if (StatData.Length > 0) { DateTime curDate = DateTime.Now; string Today = curDate.ToString("yyyy/MM/dd"); string Yesterday = curDate.AddDays(-1).ToString("yyyy/MM/dd"); //防止登錄時剛好跨天 using (SQLHelper conn = new SQLHelper(m_asyncSocketServer.DBInfo)) { conn.OpenConnection(); try { conn._Transaction = conn._Connection.BeginTransaction(); DataTable recTmp = conn.OpenDataTable("select MAX(uid) as log_uid from lrlxlog (nolock) " + "\r\n" + "where log_date between '" + Yesterday + "' and '" + Today + "' and user_no='" + DataTerminal + "' and log_ip='" + ((IPEndPoint)userToken.ConnectSocket.RemoteEndPoint).Address.ToString() + "'", CommandType.Text); if (recTmp.Rows.Count > 0) { long log_uid = long.Parse(recTmp.Rows[0]["log_uid"].ToString()); string[] statValues; foreach (string stat in StatData) { statValues = stat.Split(';'); //[0]:類別 //[1]:流量(Byte) //[2]:耗時(ms) //[3]:開始(當地)日期(yyyy/MM/dd) //[4]:開始(當地)時間(HH:mm:ss.fff) //[5]:結束(當地)日期(yyyy/MM/dd) //[6]:結束(當地)時間(HH:mm:ss.fff) if (statValues.Length == 7) { conn.ExecuteSQL("insert into lrlxstat (log_uid,stat_type,data_size,duration_time,stat_sdate,stat_stime,stat_edate,stat_etime,ie_ymd,ie_time,ie_user)" + "\r\n" + "values(" + log_uid + ",'" + statValues[0] + "'," + statValues[1] + "," + statValues[2] + ",'" + statValues[3] + "','" + statValues[4] + "','" + statValues[5] + "','" + statValues[6] + "','" + curDate.ToString("yyyy/MM/dd") + "','" + curDate.ToString("HH:mm:ss") + "','" + DataTerminal + "')"); } } } conn._Transaction.Commit(); conn.CloseConnection(); } catch { conn._Transaction.Rollback(); conn.CloseConnection(); } } } }
public FileProcessor(AsyncSocketServer asyncSocketServer, SocketUserToken userToken) : base(asyncSocketServer, userToken) { TransferDone = false; this.ApplyChannelTofSender(); }
///// <summary> ///// 收集監視器數據之命令統計開始 ///// </summary> //private void CommandStatStart() //{ // //命令當前并發數 // ServerStatistics.Concurrent_Commands++; // //命令最高并發數 // if (ServerStatistics.MaxConcurrent_Commands < ServerStatistics.Concurrent_Commands) // ServerStatistics.MaxConcurrent_Commands = ServerStatistics.Concurrent_Commands; //} ///// <summary> ///// 收集監視器數據之命令統計結束 ///// </summary> //private void CommandStatEnd(long ElapsedMilliseconds) //{ // //命令當前并發數 // ServerStatistics.Concurrent_Commands--; // //命令當前執行時長(ms) // ServerStatistics.DurationTime_Command = (int)ElapsedMilliseconds; // //命令最大執行時長(ms) // if (ServerStatistics.MaxDurationTime_Command < ServerStatistics.DurationTime_Command) // ServerStatistics.MaxDurationTime_Command = ServerStatistics.DurationTime_Command; //} /// <summary> /// 掃描所有可以發送的完整包 /// </summary> /// <param name="userToken"></param> /// <param name="receiveBuffer"></param> /// <returns></returns> private bool PreparePackages(SocketUserToken userToken, DynamicBufferManager receiveBuffer) { try { //error = false; //Test //System.Diagnostics.Debug.WriteLine("調用:PreparePackages..."); //System.Text.Encoding.UTF8.GetString(LxTcpServer.Protocol.ParseProtocol.GetByteBuffer(receiveBuffer.Buffer, 0, receiveBuffer.DataCount)).TrimEnd('\0') bool CommandResult = false; Stopwatch sw = new Stopwatch(); //判斷緩存中剩餘的數據是否足夠計算完整包 if (receiveBuffer.DataCount - m_packageOffset < MSGINFOHEAD.BodyLength) { if (m_readyPackage > 0) { //已經收集到所有可發送的完整包 //m_doProcessCommand = true; //return true; userToken.FirstPacket = true; m_packageOffset = 0; //收集監視器數據之命令統計開始 ServerStatistics.CommandStatStart(); //開始計時 sw.Start(); //開始發送 CommandResult = ProcessCommand(userToken); //結束計時 sw.Stop(); //收集監視器數據之命令統計結束 ServerStatistics.CommandStatEnd(sw.ElapsedMilliseconds); //重置計時器 sw.Reset(); return CommandResult; } else { //繼續收 return true; } } int DataLen = 0; // #Mark 5# <-不要刪除這個標記!! //1.包體長度(16進制轉10進制) byte[] DataLenBuffer = ParseProtocol.GetByteBuffer(receiveBuffer.Buffer, m_packageOffset, MSGINFOHEAD.BodyLength); try { DataLen = ParseProtocol.Convert16To10(Encoding.UTF8.GetString(DataLenBuffer)); if (DataLen == 0) { //如果類別不是S(簽名為空),就說明數據錯誤. if (Encoding.UTF8.GetString(receiveBuffer.Buffer, m_packageOffset + MSGINFOHEAD.BodyLength, 1) != "S") { //視為異常 DataLen = -1; } } } catch { //異常 DataLen = -1; } //如果是用戶修改簽名為空白,此處就是0. //if (DataLen == 0) if (DataLen < 0) { //if (userToken.BindingUser != null && userToken.ConnectSocket != null && userToken.ConnectSocket.Connected) //{ // #region " 沒找到出錯原因,暫時特殊處理一下 " // try // { // if (m_packageOffset == 1026 && !userToken.FirstPacket && receiveBuffer.DataCount == 1056) // { // //先針對1026 - 1056的錯誤特別處理一下 // //錯誤描述:客戶端:D026533信息異常,包頭: 的長度小於0. PackageOffset:1026.FirstPackage:False. // // 完整信息(Bytes:1056):00016D026533 // //修正 PackageOffset 和 FirstPackage // m_packageOffset = 0; // userToken.FirstPacket = true; // DataLen = 1; // string msg = Encoding.UTF8.GetString(receiveBuffer.Buffer, 0, receiveBuffer.DataCount); // LogHelper.WriteLog("BaseProcessor.PreparePackages", "發生1026-1056錯誤,數據已修正." + "\r\n完整信息(緩存Bytes:" + receiveBuffer.DataCount.ToString() + ",取出數據實際長度:" + msg.Length.ToString() + "):" + msg); // } // else if (m_packageOffset == 5121 && !userToken.FirstPacket && receiveBuffer.DataCount == 5131) // { // //先針對5121 - 5131的錯誤特別處理一下 ==>> 這個問題已經解了:因為未對簽名為空的情況進行處理,數據總長應該加上尾包長度,再去移除) <<== // //錯誤描述:客戶端:D037198信息異常,包頭: 的長度小於0. PackageOffset:5121.FirstPackage:False. // // 完整信息(Bytes:5131):100016D037198 // //將第一位無效數據移除 // receiveBuffer.Clear(1); // //修正 PackageOffset 和 FirstPackage // m_packageOffset = 0; // userToken.FirstPacket = true; // DataLen = 1; // string msg = Encoding.UTF8.GetString(receiveBuffer.Buffer, 0, receiveBuffer.DataCount); // LogHelper.WriteLog("BaseProcessor.PreparePackages", "發生5121-5131錯誤,數據已修正." + "\r\n完整信息(緩存Bytes:" + receiveBuffer.DataCount.ToString() + ",取出數據實際長度:" + msg.Length.ToString() + "):" + msg); // } // } // catch(Exception ex) // { // LogHelper.WriteLog("BaseProcessor.PreparePackages", "嘗試修正數據時出錯:" + ex.Message); // return false; // } // #endregion //} //else //{ if (userToken.BindingUser != null) LogHelper.WriteLog("BaseProcessor.PreparePackages", "客戶端:" + userToken.BindingUser.ID + "信息異常,包頭:" + Encoding.UTF8.GetString(DataLenBuffer) + "的長度小於0. PackageOffset:" + m_packageOffset.ToString() + ".FirstPackage:" + userToken.FirstPacket.ToString() + ".\r\n完整信息(Bytes:" + receiveBuffer.DataCount.ToString() + "):" + Encoding.UTF8.GetString(receiveBuffer.Buffer, 0, receiveBuffer.DataCount)); //.TrimEnd('\0') else LogHelper.WriteLog("BaseProcessor.PreparePackages", "UserToken.BindingUser為空.信息異常,包頭:" + Encoding.UTF8.GetString(DataLenBuffer) + "的長度小於0.");//.TrimEnd('\0') return false; //} } //$Mark 1$ <-不要刪除這個標記!! //包頭+包體+包尾長度比當前已經收到的長度大.(需要加上包頭和包尾的長度) if (ParseProtocol.GetHeadPacketLength(userToken.FirstPacket) + DataLen + MSGINFOEND.EofLength + m_packageOffset > receiveBuffer.DataCount) { if (m_readyPackage > 0) { //已經收集到所有可發送的完整包 //m_doProcessCommand = true; //return true; userToken.FirstPacket = true; m_packageOffset = 0; //收集監視器數據之命令統計開始 ServerStatistics.CommandStatStart(); //開始計時 sw.Start(); //開始發送 CommandResult = ProcessCommand(userToken); //結束計時 sw.Stop(); //收集監視器數據之命令統計結束 ServerStatistics.CommandStatEnd(sw.ElapsedMilliseconds); //重置計時器 sw.Reset(); return CommandResult; } else { //如果有續包就讀取完成再解包 //==================================================================================== //====== 以下是分析為什麼偶爾出現心跳包解包錯誤,而m_packageOffset永遠是6的問題 ======= //心跳標準長1024+1+1,如果此時receiveBuffer.DataCount沒收足夠(假設只收了1024).就會運行到這里. //此時如果把FirstPacket標記為false并再次接收,就會進入 #Mark 1# 處. //按下來m_packageOffset會被賦值為4+1+1,那麼tailData必然不是1,就進入 #Mark 2# //因為之前一個完整包都沒收到,所以m_readyPackage是0,必然進入 #Mark 3# //接下來按FirstPacket為false計算數據包標準長并與receiveBuffer.DataCount比效后,必然進入 #Mark 4# //至此,m_packageOffset變成了6,執行 #Mark 5# 時就報錯了. //==================================================================================== //userToken.FirstPacket = false; //解決方案: if (ParseProtocol.GetHeadPacketLength(userToken.FirstPacket) + DataLen + MSGINFOEND.EofLength > receiveBuffer.DataCount) { //目前收到的包還不夠一個標準包長,那就視為是首包 userToken.FirstPacket = true; } else { //目前收到的包超過了一個標準包長,那就視為是續包 userToken.FirstPacket = false; } return true; } } else { // #Mark 1# <-不要刪除這個標記!! //收取完整,開始分析是否是完全數據(尾包為1) //記錄下一個包的起始位置 m_packageOffset += ParseProtocol.GetHeadPacketLength(userToken.FirstPacket) + DataLen + MSGINFOEND.EofLength; string tailData = Encoding.UTF8.GetString(userToken.ReceiveBuffer.Buffer, m_packageOffset - MSGINFOEND.EofLength, MSGINFOEND.EofLength).TrimEnd('\0'); if (tailData == "1") { //當前包收取結束 //Test //System.Diagnostics.Debug.WriteLine("發出:" + m_packageLength.ToString()); //System.Diagnostics.Debug.WriteLine("Buffer Total:" + receiveBuffer.DataCount.ToString()); //收好了(至少有一個完整的包可以發送),開始解包并處理命令 userToken.FirstPacket = true; //累計待發送的包個數(由ProcessCommand清除) m_readyPackage++; //後面達不到最小包長度就放棄掃描 DataLen = 1; if (ParseProtocol.GetHeadPacketLength(userToken.FirstPacket) + DataLen + MSGINFOEND.EofLength + m_packageOffset > receiveBuffer.DataCount) { //m_doProcessCommand = true; m_packageOffset = 0; //收集監視器數據之命令統計開始 ServerStatistics.CommandStatStart(); //開始計時 sw.Start(); //開始發送 CommandResult = ProcessCommand(userToken); //結束計時 sw.Stop(); //收集監視器數據之命令統計結束 ServerStatistics.CommandStatEnd(sw.ElapsedMilliseconds); //重置計時器 sw.Reset(); return CommandResult; } else { //Test //LogHelper.WriteLog("BaseProcessor.PreparePackages", "尾包值為1(位置:" + (m_packageOffset - 1).ToString() + "),FirstPacket:" + userToken.FirstPacket.ToString() + ",下次將從" + m_packageOffset.ToString() + "開始檢查首包長度.\r\n完整信息(Bytes:" + receiveBuffer.DataCount.ToString() + "):" + Encoding.UTF8.GetString(receiveBuffer.Buffer, 0, receiveBuffer.DataCount).TrimEnd('\0')); //再掃描一次(不用再去接收) return PreparePackages(userToken, receiveBuffer); } } else { // #Mark 2# <-不要刪除這個標記!! //接下來的一個包一定是續包 userToken.FirstPacket = false; if (m_readyPackage > 0) { //雖然是續包,已經收集到所有可發送的完整包,剩下來的數據0位置就是首包 userToken.FirstPacket = true;//<-- 2016/04/20 19:51 被1029問題坑得發瘋的時候加的,之前這句沒加... //=============================== 以下是1029錯誤的故事 =============================== //如果這里不賦為true,那麼m_packageOffset被置0后執行ProcessCommand,完成后執行到 #Mark 1#, //然后m_packageOffset會被設置為1029(4+1024+1), 隨後來到 #Mark 2#, 最後 #Mark 3#. 無論此處走哪邊, //都會重新進入PreparePackages,並且此時m_packageOffset為1029,userToken.FirstPacket為false. //但Buffer剩下的卻是一個以首包開始完整數據,結果崩壞! //==================================================================================== m_packageOffset = 0; //收集監視器數據之命令統計開始 ServerStatistics.CommandStatStart(); //開始計時 sw.Start(); //開始發送 CommandResult = ProcessCommand(userToken); //結束計時 sw.Stop(); //收集監視器數據之命令統計結束 ServerStatistics.CommandStatEnd(sw.ElapsedMilliseconds); //重置計時器 sw.Reset(); return CommandResult; } else { // #Mark 3# <-不要刪除這個標記!! //如果剩餘已接收數據還可以檢查一次就直接比較 DataLen = 1; if (ParseProtocol.GetHeadPacketLength(userToken.FirstPacket) + DataLen + MSGINFOEND.EofLength + m_packageOffset <= receiveBuffer.DataCount) { // #Mark 4# <-不要刪除這個標記!! return PreparePackages(userToken, receiveBuffer); } else { //繼續接收數據(有效位置就從m_packageOffset開始) return true; } } } } } catch { //error = true; //主動關閉連接 //userToken.Closing = true; return false; } }
/// <summary> /// 處理命令 /// </summary> /// <returns>True=繼續投遞;False=異常</returns> public virtual bool ProcessCommand(SocketUserToken userToken) { //主動關閉連接 //userToken.Closing = true; return false; }
public override bool ProcessCommand(SocketUserToken userToken) { DynamicBufferManager receiveBuffer = userToken.ReceiveBuffer; string curUserID = userToken.BindingUser.ID; //傳送文件包數據 SocketUserToken Receiver = m_asyncSocketServer.FileUserTokenList[DataTerminal] as SocketUserToken; if (PackageCount == 0 && LastPackageLength == 0) { receiveBuffer.Clear(); //全部發送完成,等待S通知發出斷開命令 TransferDone = true; return TransferDone; } //包體長度比當前已經收到的長度大.20KB或尾包大小 int DataLength = PackageCount > 0 ? PackageLength : LastPackageLength; if (DataLength > receiveBuffer.DataCount) { //不足一個包的長度,繼續接收. return true; } else { //足夠一個包或多個包長度,進行分析 //轉發 //T通道不用鎖住線程 try { //只發一個包的大小 base.SendData(Receiver.ConnectSocket, receiveBuffer.Buffer, DataLength); TotalSize -= DataLength; //把已處理的數據從緩存里清除 receiveBuffer.Clear(DataLength); if (PackageCount > 0) PackageCount--; //收好一個包了,包數減1 else LastPackageLength = 0; //尾包扣除 //檢查是否發完 if (PackageCount <= 0 && LastPackageLength == 0 || TotalSize == 0) { receiveBuffer.Clear(); //後面沒有數據了,應該斷開兩邊的連接. string GUID = curUserID.Substring(0, curUserID.Length - 2); bool sent = false; //向發送人和接收人發信息(S通道) #region " Receiver " //好像接收者接收完成后會自己斷掉T通道.明天問問小陽. //如果是這樣那就不需要再用S通道通知接收者斷開T通道了 -> 已確認不需要通知接收者斷開T //S通道 //sent = base.PushMessage("", ParseProtocol.ConvertMsgToByte("Complete", "P", userToken.BindingUser.ReceiverID, "", "C", GUID, ""), // userToken.BindingUser.ReceiverID, // m_asyncSocketServer.PcUserTokenList, // "FileProcessor.ProcessReceive").Length > 0; //if (!sent) //{ // //斷開T通道的Receiver // string RID = curUserID.Substring(0, curUserID.Length - 1) + "R"; // SocketUserToken RUser = m_asyncSocketServer.FileUserTokenList[RID] as SocketUserToken; // m_asyncSocketServer.CloseClientSocket(RUser); // LogHelper.WriteLog("FileThreadFunc", "文件傳送完成,強行斷開接收端" + userToken.BindingUser.ReceiverID + "的連接."); //} //else //{ // LogHelper.WriteLog("FileThreadFunc", "文件傳送完成,通知接收端" + userToken.BindingUser.ReceiverID + "斷開連接."); //} #endregion #region " Sender " //S通道 sent = base.PushMessage("", ParseProtocol.ConvertMsgToByte("Complete", "P", userToken.BindingUser.SenderID, "", "C", GUID, ""), userToken.BindingUser.SenderID, m_asyncSocketServer.PcUserTokenList, "FileProcessor.ProcessReceive").Length > 0; if (!sent) { //斷開T通道的Sender LogHelper.WriteLog("FileProcessor.ProcessCommand", "文件傳送完成,強行斷開發送者" + userToken.BindingUser.SenderID + "的連接."); //主動關閉連接 //userToken.Closing = true; return false; } else { LogHelper.WriteLog("FileProcessor.ProcessCommand", "文件傳送完成,通知發送者" + userToken.BindingUser.SenderID + "斷開連接."); } #endregion return true; } else { //如果已收到的數據里還有完整包,在這里就直接發出.不需要再去接收 //否則返回去繼續收數據 return ProcessCommand(userToken); } } catch (Exception ex) { LogHelper.WriteLog("FileProcessor.ProcessCommand", "向接收端:" + DataTerminal + "發送文件整包數據時出錯." + (ex != null ? ex.Message : "")); //異常 return CloseBothSide(curUserID); } } }
/// <summary> /// 處理命令 /// </summary> /// <returns></returns> public override bool ProcessCommand(SocketUserToken userToken) { //開始解包 DynamicBufferManager receiveBuffer = userToken.ReceiveBuffer; string curUserID = userToken.BindingUser.ID; try { //返回解包后的資料(返回數組 0=type ; 1=終端; 2=群號; 3=時間; 4=消息ID; 5=消息內容 6=終端姓名 7=消息系統編號 8=設置漫遊) int msgDataLength = 0; object[] ReceiveData = ParseProtocol.UnpackMsgPackage(receiveBuffer.Buffer, out msgDataLength); string DataType = ReceiveData[0].ToString(); //如果數據類別為空就踢出 if (DataType == "") { LogHelper.WriteLog("SubSrvProcessor.ProcessCommand", "讀取用戶: @<" + curUserID + " " + userToken.ConnectSocket.RemoteEndPoint + "> 數據時連接異常斷開."); //主動關閉連接 //userToken.Closing = true; return false; } string DataTerminal = ReceiveData[1].ToString(); string DataGroup = ReceiveData[2].ToString(); string DataGUID = ReceiveData[4].ToString(); string DataBody = (DataType != "I" ? ReceiveData[5].ToString() : "N/A"); // 非屏幕截圖時才有值 byte[] DataImage = (DataType == "I" ? (byte[])ReceiveData[5] : null); // 屏幕截圖時才有值 string DataTerminalName = ReceiveData[6].ToString(); //Test //System.Diagnostics.Debug.WriteLine("轉發:\r\n" + DataBody); // 分類處理 switch (DataType) { case "1": #region " 用戶離線 " //LogHelper.WriteLog("AppProcessor.ProcessCommand", "用戶:" + curUserID + "離線."); LogHelper.WriteLog("SubSrvProcessor.ProcessCommand", "用戶: @<" + curUserID + " " + userToken.ConnectSocket.RemoteEndPoint + "> 離線."); #endregion //主動關閉連接 //userToken.Closing = true; return false; case "6": #region " 心跳包 " base.DoAlive(DataTerminal, DataBody, DataType, DataGUID); #endregion break; case "W": #region " 轉發訂閱號返回的結果給用戶 " if (DataGroup.Length > 0) { //訂閱號返回數據 //DataTerminal:接收人 SendSubServiceFeedbackToUser(DataTerminal, DataGroup, DataBody, DataGUID); } #endregion break; } //把已處理的數據從緩存里清除 //receiveBuffer.Clear(receiveBuffer.DataCount); receiveBuffer.Clear(msgDataLength); //扣除已發完的包 base.m_readyPackage--; //發完一個包后,如果DataCount>0說明有粘包數據,嘗試發送出去,否則會影響後面數據的發送效率. //如果粘包不是一個完整的數據包就返回去接收數據 if (base.m_readyPackage > 0) return ProcessCommand(userToken); else return true; } catch (Exception ex) { LogHelper.WriteLog("SubSrvProcessor.ProcessCommand", string.Format("執行用戶:{0}命令時時出錯:{1}.", curUserID, (ex != null ? ex.Message : ""))); //主動關閉連接 userToken.Closing = true; return false; } }
public SubSrvProcessor(AsyncSocketServer asyncSocketServer, SocketUserToken userToken) : base(asyncSocketServer, userToken) { }
public void Remove(SocketUserToken userToken) { lock (m_table.SyncRoot) { string UserID = userToken.BindingUser.ID; //訂閱號ID特殊處理 if (userToken.BindingUser.Role == User.UserRole.SubService) UserID = "#" + userToken.BindingUser.ID + "#"; m_table.Remove(UserID); //收集監視器數據之在線數統計-減少 LxTcpServer.SrvMonitor.ServerStatistics.OnlineStatRemove(userToken.BindingUser.Role); } }
public AsyncSocketServer() { SocketUserToken userToken; //服務器允許的最大連接數 m_maxServerLoading = int.Parse((string)ConfigurationManager.AppSettings["MaxServerLoading"]); ServerStatistics.MaxServerLoading = m_maxServerLoading; //推送廣播間隔時間 PushDataDelay = int.Parse((string)ConfigurationManager.AppSettings["PushDataDelay"]); //處理用戶在線狀態推送的線程數量 MaxStateNotifyThreads = int.Parse((string)ConfigurationManager.AppSettings["MaxStateNotifyThreads"]); ServerStatistics.MaxUserState_PushQueue = MaxStateNotifyThreads; //抓DB信息 string DBAlias = (string)ConfigurationManager.AppSettings["DB"]; //縮略圖寬度 ServerConst.Thumbnail_Width = int.Parse(ConfigurationManager.AppSettings["Thumbnail_Width"]); //縮略圖寬度 ServerConst.Thumbnail_Height = int.Parse(ConfigurationManager.AppSettings["Thumbnail_Height"]); //縮略圖寬度 ServerConst.Thumbnail_Quality = int.Parse(ConfigurationManager.AppSettings["Thumbnail_Quality"]); //同時開放多少個語音通話客戶端登錄 ServerConst.AudioCall_Channels = int.Parse(ConfigurationManager.AppSettings["AudioCall_Channels"]); ServerStatistics.AudioCall_Channels = ServerConst.AudioCall_Channels; m_receiveBufferSize = ServerConst.ReceiveBufferSize; m_idleUserTokenPool = new SocketUserTokenPool(m_maxServerLoading); m_pcUserTokenList = new SocketUserTokenList(); m_mobileUserTokenList = new SocketUserTokenList(); m_fileUserTokenList = new SocketUserTokenList(); m_appUserTokenList = new SocketUserTokenList(); m_subServiceTokenList = new SocketUserTokenList(); m_audioCallTokenList = new SocketUserTokenList(); m_maxNumberAcceptedClients = new Semaphore(m_maxServerLoading, m_maxServerLoading); //按照連接數建立UserToken for (int i = 0; i < m_maxServerLoading; i++) { userToken = new SocketUserToken(m_receiveBufferSize); userToken.ReceiveEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed); userToken.SendEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed); m_idleUserTokenPool.Push(userToken); } this.CheckOS(); this.DBInfo = RegHelper.GetDBInfo(DBAlias, RegURL); }
public override bool ProcessCommand(SocketUserToken userToken) { DynamicBufferManager receiveBuffer = userToken.ReceiveBuffer; string curUserID = userToken.BindingUser.ID; int totalData = 0; bool Sent = false; //傳送文件包數據 SocketUserToken Receiver = m_asyncSocketServer.AudioCallTokenList[DataTerminal] as SocketUserToken; //轉發 //T通道不用鎖住線程 try { if (Receiver != null && Receiver.ConnectSocket != null && Receiver.ConnectSocket.Connected) { totalData = base.SendData(Receiver.ConnectSocket, receiveBuffer.Buffer, receiveBuffer.DataCount); //System.Diagnostics.Debug.WriteLine(totalData); if (totalData == receiveBuffer.DataCount) Sent = true; else Sent = false; } else { Sent = false; } //把已處理的數據從緩存里清除 receiveBuffer.Clear(receiveBuffer.DataCount); if (Sent) { return Sent; } else { LogHelper.WriteLog("AudioCallProcessor.ProcessCommand", "找不到接收端:" + DataTerminal + "或接收者已斷開網絡"); //異常 //return CloseBothSideOfAudioCall(curUserID); return false; } } catch (Exception ex) { LogHelper.WriteLog("AudioCallProcessor.ProcessCommand", "向接收端:" + DataTerminal + "發送語音整包數據時出錯." + (ex != null ? ex.Message : "")); //異常 //return CloseBothSideOfAudioCall(curUserID); return false; } }
/// <summary> /// 處理命令 /// </summary> /// <returns></returns> public override bool ProcessCommand(SocketUserToken userToken) { //開始解包 DynamicBufferManager receiveBuffer = userToken.ReceiveBuffer; string curUserID = userToken.BindingUser.ID; try { //返回解包后的資料(返回數組 0=type ; 1=終端; 2=群號; 3=時間; 4=消息ID; 5=消息內容 6=終端姓名 7=消息系統編號 8=設置漫遊) int msgDataLength = 0; object[] ReceiveData = ParseProtocol.UnpackMsgPackage(receiveBuffer.Buffer, out msgDataLength); string DataType = ReceiveData[0].ToString(); //如果數據類別為空就踢出 if (DataType == "") { //LogHelper.WriteLog("AppProcessor.ProcessCommand", "讀取用戶:" + curUserID + "數據時連接異常斷開."); LogHelper.WriteLog("AppProcessor.ProcessCommand", "讀取用戶: @<" + curUserID + " " + userToken.ConnectSocket.RemoteEndPoint + "> 數據時連接異常斷開."); //主動關閉連接 //userToken.Closing = true; return false; } string DataTerminal = ReceiveData[1].ToString(); string DataGroup = ReceiveData[2].ToString(); string DataGUID = ReceiveData[4].ToString(); string DataBody = (DataType != "I" ? ReceiveData[5].ToString() : "N/A"); // 非屏幕截圖時才有值 byte[] DataImage = (DataType == "I" ? (byte[])ReceiveData[5] : null); // 屏幕截圖時才有值 string DataTerminalName = ReceiveData[6].ToString(); //Test //System.Diagnostics.Debug.WriteLine("轉發:\r\n" + DataBody); // 分類處理 switch (DataType) { case "1": #region " 用戶離線 " //LogHelper.WriteLog("AppProcessor.ProcessCommand", "用戶:" + curUserID + "離線."); LogHelper.WriteLog("AppProcessor.ProcessCommand", "用戶: @<" + curUserID + " " + userToken.ConnectSocket.RemoteEndPoint + "> 離線."); #endregion //主動關閉連接 //userToken.Closing = true; return false; case "5": #region " 群(異動)系統訊息 " base.SendGroupSysMessage(DataTerminal, DataGroup, DataGUID, DataBody); #endregion break; case "6": //Test //System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss") + "-> 收到心跳:" + DataBody); #region " 心跳包 " base.DoAlive(DataTerminal, DataBody, DataType, DataGUID); #endregion break; case "C": #region " 群命令 " // 抓出群成員 using (SQLHelper conn = new SQLHelper(m_asyncSocketServer.DBInfo)) { conn.OpenConnection(); try { // 記錄到數據庫中 //DateTime MsgDT = DateTime.Now; string MsgNO = this.InsertCmd(conn, DataTerminal, DataBody, DataGroup, DataGUID); if (MsgNO.Length > 0) { // 向管理員發的命令不轉發 if (DataGroup.Length > 0) { //系統將命令轉發給群成員(以發送者的帳號) base.SendGroupCommand(DataTerminal, DataGroup, DataBody, DataTerminalName, conn, DataGUID, MsgNO); } // 轉發完再發回執 // 向發送者發送回執7-03 // 回執給PC端里,必須用DataGUID,因為PC端本地生成的GUID要和這個DataGUID匹配 base.PushFeedback(DataGUID, MsgNO); } conn.CloseConnection(); } catch { conn.CloseConnection(); } } #endregion break; case "G": case "L": #region " 群組訊息(G=Push / L=Pull) " base.SendGroupBroadcast(DataTerminal, DataGroup, DataBody, DataTerminalName, DataGUID, DataType == "G"); //// 抓出群成員 //using (SQLHelper conn = new SQLHelper(m_asyncSocketServer.DBInfo)) //{ // conn.OpenConnection(); // try // { // // 記錄到數據庫中 // // DataType == "L"時, DataTerminalName為Pull消息子分類 // string MsgNO = this.InsertMsg(conn, DataTerminal, DataBody, DataGroup, "", "1", (DataType == "G" ? "" : DataTerminalName), DataGUID, false, DataType == "G"); // if (MsgNO.Length > 0) // { // // 系統將訊息轉發給群成員(以發送者的帳號) // base.SendGroupBroadcast(DataTerminal, DataGroup, DataBody, DataTerminalName, conn, DataGUID, MsgNO, DataType == "G"); // // 轉發完再發回執 // // 向發送者發送回執7-03 // base.PushFeedback(DataGUID, MsgNO); // } // conn.CloseConnection(); // } // catch // { // conn.CloseConnection(); // } //} #endregion break; case "A": #region " 外接APP向人發信息 " //DataTerminal是發送人帳號 //DataTerminalName是發送人姓名 //DataGroup是接收人帳號 this.SendAppMessage(curUserID, DataTerminal, DataTerminalName, DataGroup, DataBody, DataGUID); #endregion break; case "D": #region " 撤回消息 " //0001D發送人帳號(50b)接收對象類別(U=人;G=群)(60b)接收人/接收群(50b)GMT時間(20b)要撤回消息的GUID(32b)訊息流水號(20b) using (SQLHelper conn = new SQLHelper(m_asyncSocketServer.DBInfo)) { conn.OpenConnection(); try { // 記錄到數據庫中(1=撤回,M=消息lrmsg) string MsgNO = this.InsertSysMsg(conn, DataTerminal, DataBody, "1", "M", (DataTerminalName == "G" ? DataGroup : ""), (DataTerminalName == "U" ? DataGroup : ""), DataGUID); if (MsgNO.Length > 0) { //系統將訊息轉發給群成員(以發送者的帳號) base.SendGroupSysBroadcast("D", "1", "M", DataTerminal, DataGroup, DataBody, DataTerminalName, conn, DataGUID, MsgNO); // 轉發完再發回執 // 向發送者發送回執7-03 base.PushFeedback(DataGUID, MsgNO); } conn.CloseConnection(); } catch { conn.CloseConnection(); } } #endregion break; case "M": #region " 郵件訊息 " //別搞暈了 //DataTerminal是發送人 //DataTerminalName是發送人姓名 //DataGroup是接收人 using (SQLHelper conn = new SQLHelper(m_asyncSocketServer.DBInfo)) { conn.OpenConnection(); try { bool passed = true; string errorCode = ""; //不能自己發給自己 if (DataTerminal == DataGroup) { passed = false; errorCode = "04"; } if (passed) { //1.檢查發送人帳號是否有效(數據庫) if (conn.OpenDataTable("select uid from lrtduser (nolock) where user_no='" + DataTerminal + "'", CommandType.Text).Rows.Count == 0) { passed = false; errorCode = "04"; } //2.檢查接收人帳號是否有效(數據庫) if (conn.OpenDataTable("select uid from lrtduser (nolock) where user_no='" + DataGroup + "'", CommandType.Text).Rows.Count == 0) { passed = false; errorCode = "05"; } } //通過 if (passed) { //3.記錄到數據庫中 string MsgNO = this.InsertMsg(conn, DataTerminal, DataBody, "", DataGroup, "1", "", "4", "0", DataGUID, false, true); if (MsgNO.Length > 0) { //開始發送 base.SendP2PMessage(DataTerminal, DataTerminalName, DataGroup, DataBody, conn, DataGUID, MsgNO, DataType); // 轉發完再發回執 // 向發送者發送回執7-03 base.PushFeedback(DataGUID, MsgNO); } } else { #region " 通知Mail發送人有問題 " // 轉發完再發回執 // 向發送者發送回執7-04 或 7-05 base.PushMessage("", ParseProtocol.ConvertMsgToByte(errorCode, "7", "", "", "", DataGUID, ""), curUserID, m_asyncSocketServer.AppUserTokenList, "SendMail"); #endregion } conn.CloseConnection(); } catch { conn.CloseConnection(); } } #endregion break; case "P": #region " APP發離線文件(郵件附件) " if (DataGroup.Length > 0) { //不需要子類別:O, 只要是APP接入都認為是O.這樣把群號省出來放'發送人賬號' string MsgNO = base.ProcessTransferFileRequest(DataGroup, DataType, DataTerminal, "O", DataGUID, DataBody, DataTerminalName); // 向發送者發送回執7-03 base.PushFeedback(DataGUID, MsgNO); } #endregion break; case "V": #region " 第三方設備數據顯示(不存數據庫) " this.Send3rdPartDeviceData(DataGroup, DataGUID, DataBody); #endregion break; case "W": #region " 訂閱號消息 " if (DataGroup.Length > 0) { //用戶向訂閱號發命令 SendRequestToSubService(DataTerminal, DataGroup, DataBody, DataTerminalName, DataGUID, false); } #endregion break; } //把已處理的數據從緩存里清除 //receiveBuffer.Clear(receiveBuffer.DataCount); receiveBuffer.Clear(msgDataLength); //扣除已發完的包 base.m_readyPackage--; //發完一個包后,如果DataCount>0說明有粘包數據,嘗試發送出去,否則會影響後面數據的發送效率. //如果粘包不是一個完整的數據包就返回去接收數據 if (base.m_readyPackage > 0) return ProcessCommand(userToken); else return true; } catch (Exception ex) { LogHelper.WriteLog("AppProcessor.ProcessCommand", string.Format("執行用戶:{0}命令時時出錯:{1}.", curUserID, (ex != null ? ex.Message : ""))); //主動關閉連接 userToken.Closing = true; return false; } }