저번엔 레디스 커넥션을 구현했다.

  • RedisConnectionPool
  • 회원가입, 로그인 로직 구체적으로 정리 및 구현
  • SQL쿼리 바인더
    현재는 sql인젝션에 취약한 상태임
  • 비밀번호 해싱과 관리
  • 타이머 관리
    타이머를 만들긴 했는데 사용이 불편하고, 적절한 해제방법은 구현하지 않았음
  • 이메일 API 사용
  • 너무 느린 빌드 문제 해결하기
  • Client/Server PacketHandler.h 코드 자동생성 기능 만들기
    게임서버강의에서 배운 Protocol.proto 파일을 파싱해서 패킷핸들러 헤더를 자동으로 만들어주는 프로그램을 만들자.

원래 이번에 회원가입 로직을 제대로 만들어보려 헀는데, 반쯤 만들고 테스트해보던 도중 계속 Session과 EpollEvent의 포인터 문제로 터져서 이부분을 고치고 넘어가야겠다.


문제

더미클라이언트에서 패킷을 보내고, 서버가 response를 보내려 할 때 클라이언트를 종료하니 서버측에서 고정적으로 Segmentation fault 오류가 떴다.

원인

우선 EpollEvent 객체와 Session객체가 있다.

EpollEvent의 존재에는 주목적 2가지가 있다.

  • 주목적 1 : accept를 통해 생성된 shared_ptr<Session> (이하 SessionRef)가 소멸되지 않도록 들고있어서 레퍼런스 카운트를 늘리기 위함.
  • 주목적 2 : epoll_ctl로 등록하는 epoll_event.data.ptr에 SessionRef를 담고싶은데, 셰어드포인터를 일반포인터에 담는게 불가능하기 때문에 이를 전달해줄 wrapper 역할.
  • 보조목적 : epoll_wait에서 발생하는 이벤트는 in/out 뿐이기 때문에 이를 더 상세히 구분해주기 위함.

지금은 accept이후 SessionRef와 EpollEvent 객체를 만든다.

SessionRef에서도 EpollEvent* 를 들고있고, EpollEvent도  SessionRef를 들고있다.

이게 평소에는 문제가 없는데 멀티스레드 환경에서 세션이 Disconnect될 때 문제가 생겼다. Disconnect할 때 EpollEvent가 들고있는 SessionRef 를 nullptr로 바꾸는데(그래야 Session의 레퍼런스 카운트를 0으로 만들어 소멸시키기 때문), 다른 스레드에서 EpollEvent->GetOwner() 로 SessionRef를 가져와서 쓰는 경우가 잦아서, 여기서 문제가 터졌다.

해결

단순하게 EpollEvent가 GetOwner를 쓸때마다 null체크하는것보다는 좀 더 낫게 해결해봤다.

일단 EpollEvent에서 셰어드포인터가 아닌 weak_ptr을 들고있게 만들었다. 그래서 기존엔 레퍼런스 카운트 때문에 nullptr로 만들어줘야 했지만, 이제 이 문제를 신경 쓸 필요가 없다.

 

원래 레퍼런스 카운트 늘리려고 만들었다 했는데, 생각해보니 어디선가 레퍼런스 카운트를 갖고있지 않다면 그때야말로 최적의 소멸 타이밍이 아닌가? 그래서 그냥 weak_ptr로 변경했고, GetOwner에서는 _owner.lock()을 리턴해서 쓰게 하고, 그걸 쓰는쪽에서 null체크를 하도록 변경했다.

 

 

 

'프로젝트 > Project_Island' 카테고리의 다른 글

19. 로그인  (0) 2026.03.11
18. 회원가입  (0) 2026.03.10
16. RedisConnection  (0) 2026.03.08
15. 패킷 자동화 (2)  (0) 2026.03.08
14. 패킷 자동화 (1)  (0) 2026.03.08

+ Recent posts