public void RequestLogin(string id, string pw) { // 이미 로그인한 상태에서 다시 로그인을 요청해도 예외처리하지 않음. // 신분을 재확인할때 이 기능을 그대로 다시 사용하기 위함임. if (DoesSendPing) { throw new InvalidOperationException("서버는 로그인을 요청할 수 없습니다."); } TcpLoginInfo loginInfo = new TcpLoginInfo(id, pw); byte[] buffer = loginInfo.Serialize(); byte[] encrypted = mAES.Encrypt(buffer); Packet packet = new Packet(PacketType.RequestLogin, encrypted.Length); mNetStream.Write(packet.ToBytes(), 0, Packet.SIZE); mNetStream.Write(encrypted, 0, encrypted.Length); }
public static TcpLoginResult MatchLoginInfo(TcpLoginInfo info) { DataSet data = DBSelect("*", "[교사 목록]", $"[교사 ID]={info.ID.ToSQLString()}", null); DataTable mainTable = data.Tables[0]; int rowsCount = mainTable.Rows.Count; if (rowsCount == 0) { return(new TcpLoginResult("", "", "", false, false, false)); } else if (rowsCount > 1) { throw new InvalidOperationException("데이터베이스에 같은 교사 ID가 여러개 있습니다."); } string id = info.ID; string name = mainTable.Rows[0]["성명"].ToString(); string type = mainTable.Rows[0]["계정 구분"].ToString(); string correctPWHash = mainTable.Rows[0]["PW 해시"].ToString(); bool pwCorrect = PasswordHashManager.ValidatePassword(info.Password, correctPWHash); bool accepted = (bool)mainTable.Rows[0]["가입 허가 여부"]; return(new TcpLoginResult(id, name, type, true, pwCorrect, accepted)); }
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}"); } }