본문 바로가기
개발 및 운영/프로그래밍

효율적인 그리고 안전한 소켓 통신 서버

by Joseph.Lee 2017. 11. 13.

Thread pool을 이용해서 소켓 서버를 만들 때 recv하는 방법은 대체로 쉽다.

Windows에서는 IOCP로, Linux에서는 epoll을 이용하면 된다.

이런걸 이용한 예제는 참 많지만... 실제 서버에 적용하면 문제가 생길 가능성이 있다.

TCP send의 경우 클라이언트가 데이터를 받는걸 확인해야만 return한다.

(물론 전송 실패 시에는 한참 뒤에 return한다.)

만약 한정된 Thread pool에서 클라이언트에게 데이터를 전송하는데 마침 전송중인 모든 클라이언트의 연결이 강제로 끊긴 다면?

서버는 오랜 시간 Waiting하다가 겨우겨우 다음 데이터를 처리할 것이다.

이런일이 발생할까? 고려해야 할까? 할 수도 있지만 충분히 가능한 시나리오고

이걸 악용한다면 매우 쉽게 DoS공격을 할 수도 있다.


Windows에서는 IOCP을 이용해서 Send도 할 수 있다. (WSASend)

IOCP사용시 Non-blocking으로 바로 return하고 Overlapped되어서 자동으로 커널에서 처리해준다.

참...편하다..^^ 윈도우를 별로 안좋아하지만서도.. 이건 참...^^

Linux에서는 send에 MSG_DONTWAIT옵션이 있다. 이 옵션은 Overlapped처럼 친절하지는 않다..

MSG_DONTWAIT옵션을 추가하면 전송에 즉시 성공하면 성공을 return하고

즉시 전송하지 못하면 (일시 연결끊김 / 송신 버퍼부족) -1을 return하고 errno에는 EAGAIN가 들어간다.

이 옵션을 이용하면 일단 하나의 클라이언트 때문에 Blocking되는건 막을 수 있다. 하지만 남은 데이터 전송을 자동으로 되지 않는다.

데이터 전송 큐와 전송 쓰레드를 따로 만들수도 있지만 쓰레드를 다 만드는건 별로 효율적이지 못할거 같다.

이런걸 구현하는것도 꽤나 귀찮은 일일 것이다.

epoll 이벤트 옵션에는 EPOLLIN뿐만 아니라 EPOLLOUT도 있다.

이름으로 알 수 있드시 Out이 가능하면 즉 소켓이 writeable상태가 되면 발생하는 이벤트이다.

EPOLLOUT는 송신버퍼가 1byte라도 생기면 이벤트가 발생한다면서 좋지 않다는 말도 있긴 하지만 패킷에 데이터가 1byte씩 실려서 전송되는건 아니니 이런 상황은 드물거 같다.

EPOLLOUT는 소켓이 "writeable"이면 발생하는 이벤트인걸 기억히면 주의할 점이 하나 있다.

잘못 사용하면 https://kldp.org/node/97751 이 글과 같이 CPU점유율이 100%가 될 수 있기 때문이다.

소켓이 writeable상태면 항상 이벤트가 발생하기 때문에 당연한 문제이다.

따리서 데이터 송신을 시도하고(send) 실패 시(EAGAIN) epoll_ctl을 통해 EPOLLOUT이벤트를 추가하고 이벤트 발생 시 데이터를 재전송히고 전송이 모두 완료되면 EPOLLOUT이벤트는 해제해야한다.

반응형

댓글