Ejemplo n.º 1
0
        private void ThreadLoop(object o)
        {
            try
            {
                while (IsOnline)
                {
                    if (!mLowClient.Connected)
                    {
                        Debug.Message($"클라이언트 {IP} 소켓의 Connected 가 false 이므로 연결을 종료합니다.");
                        Disconnect();
                        break;
                    }

                    // 서버만 이 코드 실행
                    if (DoesSendPing && !FileTransfering)
                    {
                        // 아래 두 변수의 동사 (Send, Receive)의 주체는 서버임.
                        TimeSpan noPingSendSpan     = DateTime.Now - mLastPingReceive;
                        TimeSpan noPingReceiveSpan  = DateTime.Now - mLastPingSend;
                        bool     waitingForResponse = DateTime.Compare(mLastPingReceive, mLastPingSend) < 0;

                        // 핑을 보내야 할 때
                        if (noPingSendSpan > PING_SPAN && !waitingForResponse)
                        {
                            SendPing();
                        }
                        // 핑 타임아웃
                        else if (noPingReceiveSpan > NO_RESPONSE_SPAN && waitingForResponse)
                        {
                            Debug.Error($"클라이언트 {IP}가 핑에 응답하지 않습니다. 연결을 종료합니다.");
                            Disconnect();
                            break;
                        }
                    }

                    // 여기서 수신된 데이터(상대가 보낸 핑 포함)를 처리
                    if (mLowClient.Available > 0)
                    {
                        Packet packet = ReceivePacket();

                        switch (packet.Type)
                        {
                        case PacketType.Message:
                            HandleMessage(packet.DataLength);
                            break;

                        case PacketType.Ping:
                            HandlePing();
                            break;

                        case PacketType.FileSendStart:
                            #region FileSendStart
                            if (mFileReceiving)
                            {
                                throw new InvalidOperationException("파일을 받는 도중 새로운 파일 전송 시작을 알리는 패킷이 도착하였습니다.");
                            }

                            mFileReceiving = true;
                            OnPropertyChanged("FileTransfering");
                            byte[] infoBuffer = ReceiveCompletely(packet.DataLength);

                            TcpFileInfo fileInfo = TcpFileInfo.Deserialize(infoBuffer);
                            mFileReceivingNotifier = new TcpTransferNotifier(fileInfo.Length);
                            var e = new FileReceivingStartedEventArgs(fileInfo, mFileReceivingNotifier);
                            FileReceivingStarted?.Invoke(this, e);

                            if (e.Stream == null || !e.Stream.CanWrite)
                            {
                                throw new InvalidOperationException("파일 수신 시작 이벤트에서 설정한 스트림이 null 이거나 쓸 수 없습니다.");
                            }

                            mFileReceiveStream = e.Stream;
                            mFileReceivingInfo = fileInfo;
                            #endregion
                            break;

                        case PacketType.FileSending:
                            #region FileSending
                            if (!mFileReceiving)
                            {
                                throw new InvalidOperationException("파일을 받는 중이 아닌 시점에 파일 전송 패킷이 도착하였습니다.");
                            }

                            byte[] buffer    = ReceiveCompletely(packet.DataLength);
                            byte[] decrypted = mAES.Decrypt(buffer);

                            mFileReceiveStream.Write(decrypted, 0, decrypted.Length);

                            mFileReceivingNotifier.Add(decrypted.Length);
                            #endregion
                            break;

                        case PacketType.FileSendEnd:
                            #region FileSendEnd
                            if (!mFileReceiving)
                            {
                                throw new InvalidOperationException("파일을 받는 중이 아닌 시점에 파일 전송 종료 패킷이 도착하였습니다.");
                            }

                            mFileReceiving = false;
                            OnPropertyChanged("FileTransfering");
                            FileReceivingEnd?.Invoke(this, new FileReceivingEndEventArgs(mFileReceivingInfo, mFileReceiveStream));

                            mFileReceivingNotifier = null;
                            mFileReceiveStream     = null;
                            mFileReceivingInfo     = null;
                            #endregion
                            break;

                        case PacketType.RequestLogin:
                            #region RequestLogin
                            // 이미 로그인한 상태에서 다시 로그인을 요청해도 예외처리하지 않음.
                            // 신분을 재확인할때 이 기능을 그대로 다시 사용하기 위함임.
                            TcpLoginInfo   info   = TcpLoginInfo.Deserialize(mAES.Decrypt(ReceiveCompletely(packet.DataLength)));
                            TcpLoginResult result = DatabaseManager.MatchLoginInfo(info);
                            Debug.Message($"{IP}에서 로그인을 요청하였습니다. ID : {info.ID}");

                            LoginAccepted = result.Success;

                            OnPropertyChanged("LoginAccepted");

                            if (LoginAccepted)
                            {
                                AccountID   = result.ID;
                                AccountName = result.Name;
                                AccountType = result.Type;

                                OnPropertyChanged("AccountID", "AccountName", "AccountType");

                                Debug.Message($"{IP}의 로그인이 성공하였습니다. ID : {info.ID}");
                            }
                            else
                            {
                                Debug.Message($"{IP}의 로그인이 실패하였습니다. ID : {info.ID}");
                            }

                            byte[] resultBuffer    = result.Serialize();
                            byte[] resultEncrypted = mAES.Encrypt(resultBuffer);
                            Packet reply           = new Packet(PacketType.LoginResult, resultEncrypted.Length);

                            mNetStream.Write(reply.ToBytes(), 0, Packet.SIZE);
                            mNetStream.Write(resultEncrypted, 0, resultEncrypted.Length);
                            #endregion
                            break;

                        case PacketType.LoginResult:
                            #region LoginResult
                            TcpLoginResult loginResult = TcpLoginResult.Deserialize(mAES.Decrypt(ReceiveCompletely(packet.DataLength)));
                            LoginAccepted = loginResult.Success;

                            OnPropertyChanged("LoginAccepted");

                            if (LoginAccepted)
                            {
                                AccountID   = loginResult.ID;
                                AccountName = loginResult.Name;
                                AccountType = loginResult.Type;

                                OnPropertyChanged("AccountID", "AccountName", "AccountType");
                            }

                            ReceivedLoginResult?.Invoke(this, new ReceivedLoginResultEventArgs(loginResult));
                            #endregion
                            break;

                        case PacketType.RequestDataSet:
                            #region RequestDataSet
                            Debug.Message($"{IP}에서 데이터베이스 접근 시도.");
                            TcpDataSetRequirement requirement = TcpDataSetRequirement.Deserialize(mAES.Decrypt(ReceiveCompletely(packet.DataLength)));
                            TcpDataSetResult      dataSetResult;
                            Debug.Message($"상세 SQL 인수 목록 : {{{(requirement == null ? "null" : string.Join(", ", requirement.Columns))}}}, {requirement.TableName ?? "null"}, {requirement.Where ?? "null"}");

                            DataSet dataSet   = null;
                            string  exMessage = null;

                            try
                            {
                                dataSet = DatabaseManager.DBSelect(requirement);
                            }
                            catch (Exception ex)
                            {
                                exMessage = ex.Message;
                            }

                            // 성공
                            if (exMessage == null)
                            {
                                dataSetResult = new TcpDataSetResult(dataSet, requirement);
                                Debug.Message("데이터베이스에서 데이터 Select 성공.");
                            }
                            // 실패
                            else
                            {
                                dataSetResult = new TcpDataSetResult(exMessage, requirement);
                                Debug.Message($"데이터베이스에서 데이터 Select 실패. : {exMessage}");
                            }

                            byte[] dataSetResultBuffer    = dataSetResult.Serialize();
                            byte[] dataSetResultEncrypted = mAES.Encrypt(dataSetResultBuffer);
                            Packet dataSetResultPacket    = new Packet(PacketType.DataSetResult, dataSetResultEncrypted.Length);

                            mNetStream.Write(dataSetResultPacket.ToBytes(), 0, Packet.SIZE);
                            mNetStream.Write(dataSetResultEncrypted, 0, dataSetResultEncrypted.Length);

                            Debug.Message($"데이터베이스에서 얻은 데이터를 {IP}에 전송하였습니다.");
                            #endregion
                            break;

                        case PacketType.DataSetResult:
                            #region DataSetResult
                            TcpDataSetResult receivedResult = TcpDataSetResult.Deserialize(mAES.Decrypt(ReceiveCompletely(packet.DataLength)));

                            ReceivedDataSetResult?.Invoke(this, new ReceivedDataSetResultEventArgs(receivedResult));
                            #endregion
                            break;

                        case PacketType.DataInsert:
                            #region DataInsert
                            Debug.Message($"{IP}로부터 Insert 명령을 받았습니다.");
                            TcpDataInsert dataInsert = TcpDataInsert.Deserialize(mAES.Decrypt(ReceiveCompletely(packet.DataLength)));

                            DatabaseManager.DBInsert(dataInsert);
                            Debug.Message($"{IP}의 Insert 명령을 수행하였습니다.");

                            byte[] dataInsertBuffer    = dataInsert.Serialize();
                            byte[] dataInsertEncrypted = mAES.Encrypt(dataInsertBuffer);
                            Packet dataInsertPacket    = new Packet(PacketType.DataInsertCompleted, dataInsertEncrypted.Length);

                            mNetStream.Write(dataInsertPacket.ToBytes(), 0, Packet.SIZE);
                            mNetStream.Write(dataInsertEncrypted, 0, dataInsertEncrypted.Length);
                            #endregion
                            break;

                        case PacketType.DataInsertCompleted:
                            #region DataInsertCompleted
                            TcpDataInsert dataInsertLagacy = TcpDataInsert.Deserialize(mAES.Decrypt(ReceiveCompletely(packet.DataLength)));
                            InsertedData?.Invoke(this, new InsertedDataEventArgs(dataInsertLagacy));
                            #endregion
                            break;

                        case PacketType.DataUpdate:
                            #region DataUpdate
                            Debug.Message($"{IP}로부터 Update 명령을 받았습니다.");
                            TcpDataUpdate dataUpdate = TcpDataUpdate.Deserialize(mAES.Decrypt(ReceiveCompletely(packet.DataLength)));

                            DatabaseManager.DBUpdate(dataUpdate);
                            Debug.Message($"{IP}의 Update 명령을 수행하였습니다.");

                            /*byte[] dataUpdateBuffer = dataUpdate.Serialize();
                             * byte[] dataUpdateEncrypted = mAES.Encrypt(dataUpdateBuffer);
                             * Packet dataUpdatePacket = new Packet(PacketType.DataUpdateCompleted, dataUpdateEncrypted.Length);
                             *
                             * mNetStream.Write(dataUpdatePacket.ToBytes(), 0, Packet.SIZE);
                             * mNetStream.Write(dataUpdateEncrypted, 0, dataUpdateEncrypted.Length);*/
                            #endregion
                            break;

                        case PacketType.Register:
                            #region Register
                            Debug.Message($"{IP}로부터 회원가입 요청을 수신하였습니다.");
                            TcpAccountInfo accountInfo = TcpAccountInfo.Deserialize(mAES.Decrypt(ReceiveCompletely(packet.DataLength)));

                            bool regSuccess = DatabaseManager.TryToRegister(accountInfo, false);

                            if (regSuccess)
                            {
                                Debug.Message($"회원가입 요청을 정상적으로 처리했습니다.");
                            }
                            else
                            {
                                Debug.Message("이미 있는 ID 이므로 요청이 거부되었습니다.");
                            }

                            TcpRegisterResult regResult = new TcpRegisterResult(regSuccess, accountInfo);

                            byte[] regResultBuffer    = regResult.Serialize();
                            byte[] regResultEncrypted = mAES.Encrypt(regResultBuffer);
                            Packet regResultPacket    = new Packet(PacketType.RegisterResult, regResultEncrypted.Length);

                            mNetStream.Write(regResultPacket.ToBytes(), 0, Packet.SIZE);
                            mNetStream.Write(regResultEncrypted, 0, regResultEncrypted.Length);
                            #endregion
                            break;

                        case PacketType.RegisterResult:
                            #region RegisterResult
                            TcpRegisterResult regResultResponse = TcpRegisterResult.Deserialize(mAES.Decrypt(ReceiveCompletely(packet.DataLength)));
                            ReceivedRegisterResult?.Invoke(this, new ReceivedRegisterResultEventArgs(regResultResponse));
                            #endregion
                            break;

                        case PacketType.DataDelete:
                            #region DataDelete
                            Debug.Message($"{IP}로부터 Delete 명령을 받았습니다.");
                            TcpDataDelete dataDelete = TcpDataDelete.Deserialize(mAES.Decrypt(ReceiveCompletely(packet.DataLength)));

                            Debug.Message($"상세 SQL 인수 목록 : {dataDelete.TableName ?? "null"}, {dataDelete.Where ?? "null"}");

                            DatabaseManager.DBDelete(dataDelete);
                            Debug.Message($"{IP}의 Delete 명령을 수행하였습니다.");
                            #endregion
                            break;

                        case PacketType.Disconnect:
                            if (DoesSendPing)
                            {
                                Debug.Message($"클라이언트 {IP}가 정상적으로 연결을 종료하였습니다. (Disconnect 패킷 수신함)");
                            }

                            else
                            {
                                Debug.Error($"통신 중 문제가 발생하여 서버에서 연결을 강제로 종료하였습니다!");
                            }

                            Disconnect();
                            break;

                        case PacketType.AESIV:
                        case PacketType.AESKey:
                        case PacketType.RSAPublic:
                            throw new InvalidOperationException("초기 설정에 대한 패킷이 올바르지 않은 시간에 도착하였습니다.");

                        default:
                            throw new InvalidOperationException($"전송받은 패킷의 타입이 올바르지 않습니다. : {packet.Type.ToString()}");
                        }
                    }


                    Thread.Sleep(5);
                }
            }
            catch (Exception e)
            {
                Disconnect();
                Debug.Error($"{IP}와 통신 중 예외 발생, 연결 종료 : {e.GetType().ToString()}, {e.Message}\n\n{e.StackTrace}");
            }
        }