feat: 중복 로그인 방지 코드 추가

This commit is contained in:
bumpsoo 2026-02-04 13:58:58 +00:00
parent 1d103d7f16
commit 17013f70f5
6 changed files with 102 additions and 7 deletions

View file

@ -33,13 +33,38 @@ bool BaseClient::ReceiveHeader(PacketHeader &header) {
}
}
void BaseClient::Login(const std::string &nickname) {
bool BaseClient::Login(const std::string &nickname) {
PKT_CS_Login loginPkt;
std::memset(loginPkt.nickname, 0, sizeof(loginPkt.nickname));
std::strncpy(loginPkt.nickname, nickname.c_str(),
sizeof(loginPkt.nickname) - 1);
SendPacket(PacketID::Login, &loginPkt, sizeof(loginPkt));
std::cout << "로그인 요청 보냄: " << nickname << std::endl;
// 로그인 결과 대기
PacketHeader header;
if (!ReceiveHeader(header) ||
header.id != static_cast<uint16_t>(PacketID::SC_LoginResult)) {
std::cerr << "로그인 응답을 받지 못했습니다." << std::endl;
return false;
}
PKT_SC_LoginResult res;
if (!ReceivePayload(res)) {
std::cerr << "로그인 응답 수신 실패" << std::endl;
return false;
}
if (res.result == 1) {
std::cout << "로그인 성공!" << std::endl;
return true;
} else if (res.result == 0) {
std::cerr << "로그인 실패: 이미 접속 중인 유저입니다." << std::endl;
return false;
} else {
std::cerr << "로그인 실패: 알 수 없는 오류" << std::endl;
return false;
}
}
void BaseClient::HandleUpgradeResult() {
@ -68,7 +93,10 @@ void BaseClient::HandleSellResult() {
void InteractiveClient::Run() {
std::cout << "닉네임을 입력하세요: ";
std::cin >> nickname_;
Login(nickname_);
if (!Login(nickname_)) {
std::cerr << "로그인에 실패하여 종료합니다." << std::endl;
return;
}
while (true) {
if (currentLevel_ == 0) {
@ -128,7 +156,10 @@ void ScenarioClient::Run() {
if (!(is >> nickname_))
return;
Login(nickname_);
if (!Login(nickname_)) {
std::cerr << "로그인에 실패하여 종료합니다." << std::endl;
return;
}
std::string line;
while (is >> line) {

View file

@ -27,7 +27,7 @@ public:
}
}
void Login(const std::string &nickname);
bool Login(const std::string &nickname);
virtual void Run() = 0;

View file

@ -22,6 +22,8 @@ enum class PacketID : uint16_t {
Ping = 1,
// 로그인 요청 (PKT_CS_Login)
Login = 10,
// 로그인 결과 응답 (PKT_SC_LoginResult)
SC_LoginResult = 11,
Chat = 20,
// 검 키우기 관련 패킷
@ -52,6 +54,11 @@ struct PKT_CS_Login {
char nickname[32];
};
struct PKT_SC_LoginResult {
// 0: 이미 접속 중, 1: 성공
uint8_t result;
};
struct PKT_SC_UpgradeResult {
// 0: 파괴, 1: 성공, 2: 실패
uint8_t result;

View file

@ -11,6 +11,7 @@ public:
static SessionManager &GetInstance();
void Join(std::shared_ptr<Session> session);
bool TryJoin(std::shared_ptr<Session> session, const std::string &nickname);
void Leave(std::shared_ptr<Session> session);
void Broadcast(PacketHeader header, std::span<const uint8_t> body);

View file

@ -30,14 +30,50 @@ PacketHandler::HandlePacket(std::shared_ptr<Session> session,
auto userData = co_await DatabaseManager::GetInstance().LoadUser(nickname);
session->SetNickname(userData.nickname);
// 골드와 검 레벨 설정 (닉네임은 TryJoin에서 설정)
session->SetGold(userData.gold);
session->SetSwordLevel(userData.swordLevel);
Logger::Log("클라이언트 로그인: ", nickname, " (Gold: ", session->GetGold(),
PKT_SC_LoginResult loginResult;
// 원자적으로 중복 체크 및 세션 추가
if (!SessionManager::GetInstance().TryJoin(session, userData.nickname)) {
// 이미 접속 중인 유저
Logger::Log("중복 로그인 거부: ", nickname);
loginResult.result = 0;
PacketHeader header;
header.id = static_cast<uint16_t>(PacketID::SC_LoginResult);
header.size = sizeof(PKT_SC_LoginResult);
std::vector<uint8_t> buffer(sizeof(PacketHeader) +
sizeof(PKT_SC_LoginResult));
std::memcpy(buffer.data(), &header, sizeof(PacketHeader));
std::memcpy(buffer.data() + sizeof(PacketHeader), &loginResult,
sizeof(PKT_SC_LoginResult));
session->Send(buffer);
co_return;
}
Logger::Log("클라이언트 로그인 성공: ", nickname,
" (Gold: ", session->GetGold(),
", Level: ", session->GetSwordLevel(), ")");
SessionManager::GetInstance().Join(session);
// 로그인 성공 응답
loginResult.result = 1;
PacketHeader header;
header.id = static_cast<uint16_t>(PacketID::SC_LoginResult);
header.size = sizeof(PKT_SC_LoginResult);
std::vector<uint8_t> buffer(sizeof(PacketHeader) +
sizeof(PKT_SC_LoginResult));
std::memcpy(buffer.data(), &header, sizeof(PacketHeader));
std::memcpy(buffer.data() + sizeof(PacketHeader), &loginResult,
sizeof(PKT_SC_LoginResult));
session->Send(buffer);
} break;
case PacketID::CS_UpgradeSword: {

View file

@ -21,6 +21,26 @@ void SessionManager::Leave(std::shared_ptr<Session> session) {
}
}
bool SessionManager::TryJoin(std::shared_ptr<Session> session,
const std::string &nickname) {
std::lock_guard<std::mutex> lock(mutex_);
// 중복 체크
for (const auto &existing : sessions_) {
if (existing->GetNickname() == nickname) {
Logger::Log("이미 접속 중인 유저 로그인 시도: ", nickname);
return false;
}
}
// 중복이 없으면 닉네임 설정 후 추가
session->SetNickname(nickname);
sessions_.insert(session);
Logger::Log("클라이언트[", nickname,
"]가 입장했습니다. 총 세션 수: ", sessions_.size());
return true;
}
void SessionManager::Broadcast(PacketHeader header,
std::span<const uint8_t> body) {
std::lock_guard<std::mutex> lock(mutex_);