예제 #1
0
        /// <summary>
        /// UDP 로 전달받은 메세지를 Client 로 알림
        /// </summary>
        /// <param name="userId">알림 받을 UserID</param>
        /// <param name="message">알림 받을 메세지</param>
        internal void SendMessageNotiftyToClient(byte[] messageBytes, string userId)
        {
            FACInfo facInfo = FACContainer.FindFACInfo(userId);

            if (facInfo == null)
            {
                return;
            }

            SocketAsyncEventArgs sendEventArgs = facInfo.sendEventArgs;

            if (sendEventArgs == null)
            {
                return;
            }

            /// 전문 By Pass

            /// 버퍼로 복사
            DataHoldingUserToken sendToken = (DataHoldingUserToken)sendEventArgs.UserToken;

            sendToken.dataToSend = new byte[messageBytes.Length];
            Buffer.BlockCopy(messageBytes, 0, sendToken.dataToSend, 0, messageBytes.Length);

            this.SendMessage(sendEventArgs);
        }
예제 #2
0
        /// <summary>
        /// Login 된 client 정보로 수정
        /// </summary>
        /// <param name="client">client socket (검색용)</param>
        /// <param name="userId">등록할 UserID</param>
        /// <param name="userName">등록할 User 이름</param>
        public static bool Update(Socket client, string userId, string userName)
        {
            lock (FACContainer.LockThis)
            {
                /// 유효성 검사
                EndPoint endPoint = client.RemoteEndPoint;
                FACInfo  facInfo  = FACContainer.FindFACInfo(endPoint);
                if (facInfo == null)
                {
                    return(false);
                }

                /// 정보 수정
                facInfo.USER_ID   = userId;
                facInfo.USER_NAME = userName;

                // Dictionary Map 추가 (검색용)
                if (FACContainer.Instance.FacMapList.ContainsKey(userId) == false)
                {
                    List <EndPoint> mapEndPoint = new List <EndPoint>();
                    mapEndPoint.Add(endPoint);
                    FACContainer.Instance.FacMapList.Add(userId, mapEndPoint);
                }
                else
                {
                    FACContainer.Instance.FacMapList[userId].Add(client.RemoteEndPoint);
                }
                return(true);
            }
        }
예제 #3
0
        /// <summary>
        /// 로그인
        /// </summary>
        private MessageStream MessageLogin(MessageReader reader, SocketAsyncEventArgs receiveSendEventArgs)
        {
            MessageStream response       = null;
            string        user_id        = reader.GetParam(0);
            string        user_pw        = reader.GetParam(1);
            string        user_name      = "";
            string        recv_faxbox_id = "";
            string        send_faxbox_id = "";

            /// ipep.ToString() : 100.100.106.230:2038
            /// ipep.Address.ToString() : 100.100.106.230
            IPEndPoint ipep = (IPEndPoint)receiveSendEventArgs.AcceptSocket.RemoteEndPoint;

            RESULT result = DbModule.Instance.FAS_LoginAgentClient(user_id, user_pw, ipep.ToString(), ref user_name, ref recv_faxbox_id, ref send_faxbox_id);

            if (result == RESULT.F_DB_NOTEXIST_USER_ID ||
                result == RESULT.F_DB_PASSWORD_MISMATCH)
            {
                response = MessageStream.Response(MessagePacketNameEnum.LOGIN, reader.Header, false, "사용자 ID가 없거나 암호가 잘못되었습니다.");
            }
            else if (result == RESULT.SUCCESS ||
                     result == RESULT.F_DB_LOGIN_DUPLICATED)
            {
                /// /CLOSE 전문 발송 (중복 로그인)
                if (result == RESULT.F_DB_LOGIN_DUPLICATED)
                {
                    FACInfo oldFacInfo = FACContainer.FindFACInfo(user_id);
                    if (oldFacInfo != null)
                    {
                        if (this.OnLoginDuplicated != null)
                        {
                            this.OnLoginDuplicated(oldFacInfo);
                        }
                    }
                }

                DataHoldingUserToken receiveSendToken = (receiveSendEventArgs.UserToken as DataHoldingUserToken);

                /// Login 된 Client 로 추가
                FACContainer.Update(receiveSendEventArgs.AcceptSocket, user_id, user_name);

                response = MessageStream.Response(MessagePacketNameEnum.LOGIN, reader.Header, "성공");
                response.AddPrameters(reader.GetParam(0));
                response.AddPrameters(user_name);
                response.AddPrameters(recv_faxbox_id);
                response.AddPrameters(send_faxbox_id);
                response.AddPrameters(((int)Config.CLIENT_ALIVE_INTERVAL).ToString());
                response.AddPrameters(((int)Config.CLIENT_LIMIT_RESPONSE_TIME).ToString());
                response.AddPrameters(Config.HOME_PATH_HTTP);
            }
            else
            {
                response = MessageStream.Response(MessagePacketNameEnum.LOGIN, reader.Header, false, result.ToString());
            }

            return(response);
        }
예제 #4
0
        /// <summary>
        /// 지정시간동안 Client 요청이 없으면 강제 종료 처리
        /// </summary>
        private void AliveCheckSockets_ThreadEntry()
        {
            Dictionary <EndPoint, FACInfo> acceptedList;

            //bool isalive = true;
            while (true)
            {
                Thread.Sleep(1000 * 10);

                lock (FACContainer.LockThis)
                {
                    acceptedList = new Dictionary <EndPoint, FACInfo>(FACContainer.AcceptedList);
                }

                foreach (KeyValuePair <EndPoint, FACInfo> kvPair in acceptedList)
                {
                    try
                    {
                        FACInfo info = FACContainer.FindFACInfo(kvPair.Key);
                        if (info == null)
                        {
                            continue;
                        }

                        if (Config.SERVER_LIMIT_RESPONSE_TIME > 0 &&
                            DateTime.Now.Subtract(info.LastRequestTime).TotalSeconds > Config.SERVER_LIMIT_RESPONSE_TIME)
                        {
                            // 종료 처리
                            SocketListener.Log.WRN(string.Format("AliveCheckSockets_ThreadEntry() : Arrive timeout. {0}/{1}"
                                                                 , DateTime.Now.Subtract(info.LastRequestTime).TotalSeconds
                                                                 , Config.SERVER_LIMIT_RESPONSE_TIME));

                            // 이미 삭제 되었을때 Exception 발생
                            if (info.Socket != null)
                            {
                                SocketListener.Log.WRN(string.Format("{0}|Socket Disconnect.", kvPair.Key.ToString()));
                                try { info.Socket.Disconnect(false); }
                                catch { }
                            }

                            FACContainer.Remove(info.recvEventArgs);
                            FACContainer.AcceptedList.Remove(kvPair.Key);
                        }
                    }
                    catch (Exception ex)
                    {
                        SocketListener.Log.ERR(string.Format("{0}|AliveCheckSockets_ThreadEntry() {1}\r\n{2}", kvPair.Key.ToString(), ex.Message, ex.StackTrace));
                        continue;
                    }
                }
            }
        }
예제 #5
0
        /// <summary>
        /// UserID 로 접속한 client 찾기
        /// </summary>
        /// <param name="e">UserID</param>
        /// <returns>FACInfo</returns>
        public static FACInfo FindFACInfo(string userId)
        {
            lock (FACContainer.LockThis)
            {
                if (FACContainer.Instance.FacMapList.ContainsKey(userId) == false)
                {
                    return(null);
                }

                // 첫번째 항목을 가져온다
                return(FACContainer.FindFACInfo(FACContainer.Instance.FacMapList[userId][0]));
            }
        }
예제 #6
0
        /// <summary>
        /// SAEA 객체로 접속한 client 찾기
        /// </summary>
        /// <param name="e">Receive, Send SAEA 객체</param>
        /// <returns>FACInfo</returns>
        public static FACInfo FindFACInfo(SocketAsyncEventArgs e)
        {
            try
            {
                DataHoldingUserToken dToken = e.UserToken as DataHoldingUserToken;
                if (dToken == null)
                {
                    return(null);
                }

                EndPoint endPoint = dToken.IpEndPt;
                return(FACContainer.FindFACInfo(endPoint));
            }
            catch
            {
                return(null);
            }

            ////EndPoint endPoint = e.AcceptSocket.RemoteEndPoint;
            ////return FACContainer.FindFACInfo(endPoint);

            ////FACInfo fInfo = null;
            ////IPEndPoint ep = null;
            //try
            //{
            //    DataHoldingUserToken dToken = e.UserToken as DataHoldingUserToken;
            //    if (dToken != null)
            //    {
            //        ep = dToken.IpEndPt;
            //    }
            //    else
            //    {
            //        AcceptOpUserToken aToken = e.UserToken as AcceptOpUserToken;
            //        ep = aToken.IpEndPt;
            //    }

            //    EndPoint endPoint = ep;//e.AcceptSocket.RemoteEndPoint;
            //    fInfo = FACContainer.FindFACInfo(endPoint);
            //}
            //catch
            //{
            //    fInfo = null;
            //}

            //return fInfo;
        }
예제 #7
0
        /// <summary>
        /// 로그아웃
        /// </summary>
        private MessageStream MessageLogout(MessageReader reader, SocketAsyncEventArgs receiveSendEventArgs)
        {
            RESULT result = RESULT.EMPTY;

            /// 등록된 user_id 와 IP endpoint 가 같은지 확인한다.
            /// 다르다면 중복 로그인으로 로그아웃을 요청한 것이므로
            /// 디비 처리를 하지 않는다.
            string   user_id      = reader.GetParam(0);
            EndPoint endPoint     = receiveSendEventArgs.AcceptSocket.RemoteEndPoint;
            EndPoint endPointComp = FACContainer.FindFACInfo(user_id).RemoteEndPoint;

            if (endPointComp == null)
            {
                result = DbModule.Instance.FAS_LogoutAgentClient(reader.GetParam(0));
            }
            else
            {
                if (endPoint.ToString() == endPointComp.ToString())
                {
                    result = DbModule.Instance.FAS_LogoutAgentClient(reader.GetParam(0));
                }
                else
                {
                    result = RESULT.SUCCESS;
                }
            }

            /// 성공 실패 처리
            if (result == RESULT.SUCCESS)
            {
                return(MessageStream.Response(MessagePacketNameEnum.LOGOT, reader.Header, "성공", reader.GetParam(0)));
            }
            else
            {
                return(MessageStream.Response(MessagePacketNameEnum.LOGOT, reader.Header, false, "로그아웃 중 오류가 발생하였습니다."));
            }
        }
예제 #8
0
        /// <summary>
        /// Shutdown -> Close
        /// </summary>
        private bool CloseClientSocket(SocketAsyncEventArgs e)
        {
            try
            {
                SocketListener.Log.MSG(e, "Start Close FaxAgent.");
                DataHoldingUserToken receiveSendToken = e.UserToken as DataHoldingUserToken;
                if (receiveSendToken.dataToReceive != null)
                {
                    receiveSendToken.CreateNewDataHolder();
                }

                // 로그아웃 처리
                FACInfo facInfo = FACContainer.FindFACInfo(e);
                if (facInfo != null)
                {
                    // DB Logout 처리
                    if (FACContainer.LoginedList.Count > 0 && facInfo.USER_ID != "")
                    {
                        if (DbModule.Instance.FAS_LogoutAgentClient(facInfo.USER_ID) != Btfax.CommonLib.RESULT.SUCCESS)
                        {
                            SocketListener.Log.WRN(e, "Database Logout process failure");
                        }
                        else
                        {
                            SocketListener.Log.MSG(e, "Database Logout process success");
                        }
                    }
                }

                // socket close
                try     { e.AcceptSocket.Shutdown(SocketShutdown.Both); }
                catch { }

                if (e.AcceptSocket != null)
                {
                    try {
                        e.AcceptSocket.Close();
                        e.AcceptSocket = null;
                    }
                    catch { };
                }

                if (facInfo != null)
                {
                    this.poolOfRecvEventArgs.Push(facInfo.recvEventArgs);
                    this.poolOfSendEventArgs.Push(facInfo.sendEventArgs);
                }
                else
                {
                    this.CreateNewSaeaForRecvSendOne();
                    SocketListener.Log.MSG(string.Format("CreateNewSaeaForRecvSendOne this.poolOfRecvEventArgs:{0}, this.poolOfSendEventArgs.Push{1}"
                                                         , this.poolOfRecvEventArgs.Count
                                                         , this.poolOfSendEventArgs.Count));
                }

                FACContainer.Remove(e);

                // Accept count 감소
                Interlocked.Decrement(ref this.numberOfAcceptedSockets);
                this.theMaxConnectionsEnforcer.Release();

                SocketListener.Log.MSG(e, string.Format("Close FaxAgent success."));
                this.DisplayConnectionInfo();
                return(true);
            }
            catch (Exception ex)
            {
                // Accept count 감소
                Interlocked.Decrement(ref this.numberOfAcceptedSockets);
                this.theMaxConnectionsEnforcer.Release();

                SocketListener.Log.ERR(string.Format("CloseClientSocket : {0}\r\n{1}", ex.Message, ex.StackTrace));
                return(false);
            }
        }
예제 #9
0
        private void ProcessSend(SocketAsyncEventArgs sendEventArgs)
        {
            int nRemainingCnt = 0;

            // 이벤트 검사
            if (!ValidationCheckSocketAsyncEventArgs("ProcessSend", sendEventArgs))
            {
                return;
            }

            DataHoldingUserToken sendToken = sendEventArgs.UserToken as DataHoldingUserToken;

            nRemainingCnt = sendToken.sendBytesRemainingCount;
            if (nRemainingCnt <= 0)
            {
                SocketListener.Log.TRC(sendEventArgs, string.Format("ProcessSend : Invalid DataHoldingUserToken sendBytesRemainingCount count({0}).", sendToken.sendBytesRemainingCount));
                return;
            }

            FACInfo facInfo = FACContainer.FindFACInfo(sendEventArgs);

            if (facInfo == null)
            {
                SocketListener.Log.WRN(sendEventArgs, "ProcessSend : Not found FaxAgentInfo.");
                return;
            }

            facInfo.LastResponseTime = DateTime.Now;

            LOG_LEVEL logLv      = LOG_LEVEL.MSG;
            string    packetName = m_Encoding.GetString(sendToken.dataToSend, 34, 5);

            if (packetName == MessagePacketNameEnum.ALIVE.ToString())
            {
                logLv = LOG_LEVEL.TRC;
            }
            else
            {
                logLv = LOG_LEVEL.MSG;
            }

            // 패킷 로그
            SocketListener.Log.LogWrite(logLv,
                                        "SEND    ",
                                        sendEventArgs.AcceptSocket.RemoteEndPoint.ToString(),
                                        sendEventArgs.AcceptSocket.Handle.ToInt32(),
                                        sendToken.TokenId,
                                        sendToken.dataToSend.Length,
                                        m_Encoding.GetString(sendToken.dataToSend));

            sendToken.sendBytesRemainingCount = sendToken.sendBytesRemainingCount - sendEventArgs.BytesTransferred;

            sendToken.bytesSentAlreadyCount += sendEventArgs.BytesTransferred;

            // send async
            StartSend(sendEventArgs);

            if (packetName == MessagePacketNameEnum.LOGIN.ToString())
            {
                this.DisplayConnectionInfo();
            }
        }
예제 #10
0
        private void ProcessReceive(SocketAsyncEventArgs recvEventArgs)
        {
            DataHoldingUserToken recvToken = recvEventArgs.UserToken as DataHoldingUserToken;

            // 이벤트 검사
            if (!ValidationCheckSocketAsyncEventArgs("ProcessReceive", recvEventArgs))
            {
                recvToken.Reset();
                this.CloseClientSocket(recvEventArgs);
                return;
            }

            try
            {
                if (recvEventArgs.BytesTransferred == 0)
                {
                    SocketListener.Log.WRN(recvEventArgs, "RecvEventArgs.BytesTransferred is zero. !!");
                    recvToken.Reset();
                    this.CloseClientSocket(recvEventArgs);
                    return;
                }

                FACInfo facInfo = FACContainer.FindFACInfo(recvEventArgs);
                if (facInfo == null)
                {
                    SocketListener.Log.ERR(recvEventArgs, "Not found facinfo.");
                    recvToken.Reset();
                    return;
                }

                facInfo.LastRequestTime = DateTime.Now;

                Int32 remainingBytesToProcess = recvEventArgs.BytesTransferred;

                if (recvToken.receivedPrefixBytesDoneCount < this.socketListenerSettings.ReceivePrefixLength)
                {
                    /// 공통 전문 첫 컬럼 (전문길이) 읽기
                    remainingBytesToProcess = prefixHandler.HandlePrefix(recvEventArgs, recvToken, remainingBytesToProcess);
                    if (remainingBytesToProcess == 0)
                    {
                        SocketListener.Log.WRN(recvEventArgs, "RemainingBytesToProcess is zero. !!");
                        StartReceive(recvEventArgs);
                        return;
                    }
                }

                // 전문길이만큼 전문 읽기
                bool incomingTcpMessageIsReady = messageHandler.HandleMessage(recvEventArgs, recvToken, remainingBytesToProcess);
                if (incomingTcpMessageIsReady == true)
                {
                    SocketAsyncEventArgs sendEventArgs = facInfo.sendEventArgs;
                    if (recvEventArgs == null)
                    {
                        recvToken.Reset();
                        //CloseClientSocket(recvEventArgs);
                        return;
                    }

                    LOG_LEVEL logLv      = LOG_LEVEL.MSG;
                    string    packetName = m_Encoding.GetString(recvToken.dataToReceive, 34, 5);
                    if (packetName == MessagePacketNameEnum.ALIVE.ToString())
                    {
                        logLv = LOG_LEVEL.TRC;
                    }
                    else
                    {
                        logLv = LOG_LEVEL.MSG;
                    }

                    // 패킷 로그
                    SocketListener.Log.LogWrite(logLv,
                                                "RECEIVE ",
                                                recvEventArgs.AcceptSocket.RemoteEndPoint.ToString(),
                                                recvEventArgs.AcceptSocket.Handle.ToInt32(),
                                                recvToken.TokenId,
                                                recvToken.dataToReceive.Length,
                                                m_Encoding.GetString(recvToken.dataToReceive));

                    // 응답 전문 보내기
                    this.SendMessageToClient(sendEventArgs);
                }
                else
                {
                    recvToken.receiveMessageOffset     = recvToken.bufferOffsetReceive;
                    recvToken.recPrefixBytesDoneThisOp = 0;
                }

                StartReceive(recvEventArgs);
            }
            catch (Exception ex)
            {
                SocketListener.Log.ERR(recvEventArgs, string.Format("ProcessReceive : {0}", ex.Message));
                recvToken.Reset();
            }
        }
예제 #11
0
        private void ProcessAccept(SocketAsyncEventArgs acceptEventArgs)
        {
            // 이벤트 검사
            if (!ValidationCheckSocketAsyncEventArgs("ProcessAccept", acceptEventArgs))
            {
                this.StartAccept();
                this.HandleBadAccept(acceptEventArgs);
                return;
            }

            // 최대 접속자 수 설정
            Int32 max = this.maxSimultaneousClientsThatWereConnected;
            Int32 numberOfConnectedSockets = Interlocked.Increment(ref this.numberOfAcceptedSockets);

            if (numberOfConnectedSockets > max)
            {
                Interlocked.Increment(ref this.maxSimultaneousClientsThatWereConnected);
            }

            // 다음 소켓 이벤트 대기 처리 : acceptEventArgs -> recvEventArgs, sendEventArgs
            StartAccept();

            SocketListener.Log.MSG(acceptEventArgs, string.Format("Start accept processing !! poolOfAcceptEventArgs:{0}, poolOfRecvEventArgs:{1}, poolOfSendEventArgs:{2}"
                                                                  , this.poolOfAcceptEventArgs.Count
                                                                  , this.poolOfRecvEventArgs.Count
                                                                  , this.poolOfSendEventArgs.Count));

            try
            {
                // Accept 된 Client 로 추가 : acceptEventArgs -> recvEventArgs, sendEventArgs
                if (this.poolOfRecvEventArgs.Count <= 1 || this.poolOfSendEventArgs.Count <= 1)
                {
                    this.CreateNewSaeaForRecvSendOne();
                }

                AcceptOpUserToken    aToken        = acceptEventArgs.UserToken as AcceptOpUserToken;
                SocketAsyncEventArgs recvEventArgs = null;
                SocketAsyncEventArgs sendEventArgs = null;

                while (true)
                {
                    recvEventArgs = this.poolOfRecvEventArgs.Pop();
                    sendEventArgs = this.poolOfSendEventArgs.Pop();

                    if (recvEventArgs.Buffer == null || sendEventArgs.Buffer == null)
                    {
                        DataHoldingUserToken dToken = recvEventArgs.UserToken as DataHoldingUserToken;
                        if (dToken != null)
                        {
                            this.theBufferManager.SetBuffer(dToken.BufferOffsetRecv, recvEventArgs);
                            SocketListener.Log.WRN(string.Format("Receive SocketAsyncEventArgs buffer is null. reset buffer. Reuse later!! token:{0}, token_offset:{1}, recv_offset:{2}", dToken.TokenId, dToken.BufferOffsetRecv, recvEventArgs.Offset));
                        }

                        dToken = sendEventArgs.UserToken as DataHoldingUserToken;
                        if (dToken != null)
                        {
                            this.theBufferManager.SetBuffer(dToken.BufferOffsetSend, sendEventArgs);
                            SocketListener.Log.WRN(string.Format("Send SocketAsyncEventArgs buffer is null. reset buffer. Reuse later!! token:{0}, token_offset:{1}, send_offset:{2}", dToken.TokenId, dToken.BufferOffsetSend, sendEventArgs.Offset));
                        }

                        this.poolOfRecvEventArgs.Push(recvEventArgs);
                        this.poolOfSendEventArgs.Push(sendEventArgs);
                        continue;
                    }

                    break;
                }

                InitSocketAsyncEventArgs(ref recvEventArgs);
                InitSocketAsyncEventArgs(ref sendEventArgs);

                SocketListener.Log.MSG(string.Format("Start accept processing !! SocketAsyncEventArgs buffer offset. recv_offset:{0}, send_offset:{1}", recvEventArgs.Offset, sendEventArgs.Offset));

                (recvEventArgs.UserToken as DataHoldingUserToken).CreateSessionId();
                (recvEventArgs.UserToken as DataHoldingUserToken).RemoteIp   = aToken.RemoteIp;
                (recvEventArgs.UserToken as DataHoldingUserToken).RemotePort = aToken.RemotePort;
                (recvEventArgs.UserToken as DataHoldingUserToken).IpEndPt    = aToken.IpEndPt;

                (sendEventArgs.UserToken as DataHoldingUserToken).CreateSessionId();
                (sendEventArgs.UserToken as DataHoldingUserToken).RemoteIp   = aToken.RemoteIp;
                (sendEventArgs.UserToken as DataHoldingUserToken).RemotePort = aToken.RemotePort;
                (sendEventArgs.UserToken as DataHoldingUserToken).IpEndPt    = aToken.IpEndPt;


                recvEventArgs.AcceptSocket = acceptEventArgs.AcceptSocket;
                //recvEventArgs.SocketError = acceptEventArgs.SocketError;

                sendEventArgs.AcceptSocket = acceptEventArgs.AcceptSocket;
                //sendEventArgs.SocketError = sendEventArgs.SocketError;

                (recvEventArgs.UserToken as DataHoldingUserToken).socketHandleNumber = (Int32)recvEventArgs.AcceptSocket.Handle;
                (sendEventArgs.UserToken as DataHoldingUserToken).socketHandleNumber = (Int32)sendEventArgs.AcceptSocket.Handle;

                // 이벤트 반납
                aToken.RemoteIp              = "";
                aToken.RemotePort            = 0;
                aToken.IpEndPt               = null;
                acceptEventArgs.AcceptSocket = null;
                this.poolOfAcceptEventArgs.Push(acceptEventArgs);

                string strMsg;
                FACContainer.AddEx(recvEventArgs.AcceptSocket, recvEventArgs, sendEventArgs, out strMsg);
                if (!string.IsNullOrEmpty(strMsg))
                {
                    SocketListener.Log.WRN(recvEventArgs, string.Format("FACContainer AddEx - {0}!!", strMsg));
                }

                this.DisplayConnectionInfo();

                /// Accept 알림 보내기
                SendMessageToClient(sendEventArgs, MessagePacketNameEnum.ACCPT);

                /// 수신 대기
                StartReceive(recvEventArgs);
            }
            catch (Exception ex)
            {
                // REMOVE - KIMCG : 20140827
                // Exception 발생시 재귀 호출로인해 Accept pool 에서 이벤트가 빠진다. -> 추후 이벤트 부족으로 작업불가함.
                // StartAccept();
                // HandleBadAccept(acceptEventArgs);
                // REMOVE - END
                SocketListener.Log.ERR(string.Format("ProcessAccept - message:{0}\r\nstac_trace:{1}\r\nsource:{2}", ex.Message, ex.StackTrace, ex.Source));
            }
        }