Overlapped IO Introduction

2022. 2. 10. 00:41게임서버/win socket 프로그래밍

320x100

안녕하세요 대학생개발자입니다.

 

마지막 글에서 기본적인 소켓 api를 다뤘었습니다.

그리고 한동안 알고리즘과 스레드, 그리고 select 서버에 대한 얘기를 했었습니다.

아마 제가 만든 AsyncSelect 클라이언트를 보셨는지는 모르겠습니다만,

그 이상의 새로운 모델이 아직 나오지 않았었습니다.

그리고 이제 Overlapped IO모델을 정리하면서 동시에 IOCP서버 개발에 착수할 예정입니다.

물론 처음부터 크고 멋진 서버를 만들겠다는 계획은 없습니다.

그리고 스레드 디자인부분도 아직 완벽하지 않기 때문에 확실하게 모든게 짚어지고 나면 그다음 제작에 진입을 할 예정입니다.

 

우선 서론이 길었습니다.

 

먼저 이전에 다뤘었던 모델들의 장점과 단점에대한 정리를 간단히 해보고 넘어가도록 하겠습니다.

 

우선 select 이전의 비동기 소켓 io(모델이름도 없습니다)은 생각지도 않도록 하겠습니다.

 

우선 select의 장점이라고 하면 당연히... 구현이 너무 쉽습니다.

제가 구현해본 결과로는 크게 문제가 생길 부분이 존재하지가 않습니다.

그리고 문제가 생겨도 크게 시간을 잡아먹는 부분도 없었던 것 같구요.

 

단점이라고하면 한번에 64개의 클라이언트밖에 담지 못한다는것이겠죠.

그리고 이점 때문에 매우 느릴겁니다.

심각하게 느릴겁니다.

한번에 7000명의 클라이언트를 받고난 뒤의 서버 상태는 프레임 방어도 제대로 되지 않는걸 확인 할 수 있었습니다.

물론 그게 큰 문제거리는 아니었습니다만... 째뜬 그정도로 느립니다.

select와 IO가 너무 느리기 때문에 프레임이 푹푹 떨어졌었습니다.

 

 

그리고 그다음으로 했던것이 AsyncSelect인데요.

AsyncSelect는 비동기 IO는 아닙니다.

select에 대한 Async를 커널이 보장해주는 겁니다.

우리는 그냥 WSAAsyncSelect를 호출해둔 상태로 있으면 select를 호출한 것과같이 커널에서 메시지로 알려줄겁니다.

그러면 우리는 이 메시지를 받아서 처리하면 끝이죠.

장점으로는 커널내부에서 이런 과정을 처리해주기 때문에 개발하기 편하고 직관적이라는겁니다.

그리고 select에서 제한되었던 64명이 풀리게됩니다.

 

단점으로는 이 AsyncSelect 소켓모델 자체가 싱글 코어를 베이스로 만들어진것이기 때문에 사실상 서버에서 쓸 수 없습니다.

추가로 AsyncSelect모델은 Winmain에 상당히 종속적이기 때문에 서버에서 사용하기 참 그렇습니다.

 

 

그리고 제가 언급을 한번도 안했었던 EventSelect라는 모델도 있습니다.

EventSelect는 뭐... AsyncSelect와 약간 맥락을 같이하는 경향이 있습니다.

AsyncSelect에서는 무슨 메시지가 도착했는지 메시지를 주는 방식으로 우리에게 통보를 했었습니다만,

EventSelect에서는 무슨 메시지인지도 모르고 그냥 일단 니가 넣은 소켓이 시그널이 되었다.

그 정보만 알려줍니다.

그리고 select모델이기 때문에 64개의 이벤트만 넣을 수 있다는 단점도 존재합니다.

각 소켓당 send recv에 대한 이벤트가 하나씩 나와야 되기 때문에 select보다 2배더 많은 뺑뺑이가 필요해집니다.

당연히 서버에서 쓰기에는 좋지 않습니다.

클라이언트에서는 EventSelect를 통한 개발을 많이합니다.

해봐야 2~3개의 연결밖에 존재하지 않기때문입니다.

 

이때까지 우리가 쭉 겪어왔던 소켓 모델들입니다.

간단하게 정리했으니까 이부분에 대한 궁금증이 더 있으신 분들께서는 제가 예전에 올렸던 글을 참고하시면 좋을 듯 합니다.

 

자 그리고 여기까지는 모두 동기IO를 하는 모델들이었습니다.

 

그럼 우리는 동기와 비동기에대한 궁금증이 생길 것 같습니다.

과연 동기는 뭐고 비동기는 무엇인가, 그리고 논블락, 블락과의 차이는 뭐지??

라고 궁금증을 갖는것 당연합니다.

 

일단 간략하게 설명을 드렸었던 기억이 납니다만

다시한번 정리하자면

동기 비동기는 IO에 대한 처리를 기다릴 것이냐 말것이냐라는 의미입니다.

블락 논블락은 IO가 불가능할 경우 기다릴 것이냐 말것이냐라는 의미입니다.

 

약간 논블락과 블락이 동기 IO의 의미를 살짝 내제하고 있는 것 같기도 합니다.

이부분은 논의가 많이 되어야되는 부분이므로 조금 미뤄놓도록 하겠습니다.

 

모든 IO는 커널을 통해서 합니다.

그러므로 우리가 작업을 요청하면 스레드는 블로킹에 걸려서 기다려야됩니다.

아주 유명한 IO함수죠 printf같은 경우에는 동기함수입니다.

이 함수의 return은 IO의 완료를 의미합니다.

 

사실 여기서 IO 완료라는 의미에 대해서도 좀 고민해 봐야되는 부분이 있는데요...

IO를 완료하다 라는것은 절대로 실제 데이터가 출력이 되었다는걸 완료했다 라는게 아닙니다.

그 디바이스가 가져갈 수 있는 곳으로의 복사 완료라는 의미입니다.

 

당연하겠죠...

우리가 FileIO를 한다면 우리는 직접 디스크에 어디부분에 써라 라고 하지못합니다.

우리는 그냥 버퍼에 복사를 해놓으면 디스크가 그걸 읽어다가 디스크 스스로가 쓰는걸거니까요.

우리의 IO과정은 여기까지입니다.

그 이후의 과정은 OS도 관여할 수 없고 우리도 관여할 수 없습니다.

그저 드라이버와 그 하드웨어의 역할입니다.

 

즉 동기에서 IO완료라는 것은 그 하드웨어가 바로 작업을 가져갈 수 있는 버퍼에 내가 작업을 올려놓는걸 완료했다는게 곧 IO완료입니다.

 

이제 중요한 비동기 함수에 대한 얘기를 할겁니다.

비동기함수는 애초에 우리가 이 IO에 대한 요청 자체를 OS에 맡겨버리는겁니다.

즉 이건 그냥 디스크에다가 직접 요구를 하는 꼴이 되는겁니다.

중간에서 우리가 따로 뭔가를 할 필요가 없기 때문입니다.

그리고 우리는 이 완료된 상황에대한 통지를 OS를 통해서 받을겁니다.

물론 인터럽트 기반이겠죠.

 

즉 동기와 비동기를 나눈다는것을 정리하면 이런겁니다.

IO를 할때 우리의 스레드는 기다려야됩니다.

하지만 이상황에서 우리의 스레드가 기다리지 않는다면?

그게 곧 비동기입니다.

 

그리고 제 글에 몇번 등장했던 Overlapped IO모델 같은 경우는 비동기 IO를 사용하는 모델로써 작동합니다.

모델1, 모델2, 그리고 IOCP와 같은 것들이 있는데요...

이 방법들이 나눠지는 것은 전부다 완료 통지를 받는 방법의 차이 때문에 그런것입니다.

 

Overlapped IO모델1의 경우에는 완료에 대한 통지를 이벤트로 받습니다.

당연히 EventSelect와 같은 단점들이 꽤나 존재합니다.

요즘 서버에서는 거의 사용하지 않습니다.

 

Overlapped IO모델2의 경우에는 완료에 대한 통지를 CALLBACK을 통해서 받습니다.

사실 통지를 받는다기 보다는 스스로 완료를 했고 완료에대한 작업도 해주는 꼴이되는거죠.

우리는 너 완료되면 이작업까지 해줘~ 하고 부탁하는겁니다.

이 모델또한 요새 잘쓰진 않습니다...

왜냐면 이 스레드에서 요청한 작업은 이스레드에서만 처리할 수 있다는 단점이 존재하기 때문입니다.

뭐 아직까지는 구체적으로 모르셔도 좋기때문에 이정도만 적겠습니다.

 

IOCP라는건 상당히 강력합니다..

IOCP는 우리가 이때까지 생각해왔던 스레드 모델들과 상당히 비슷한데요...

여러 스레드가 하나의 JobQueue를 대상으로 작업을 하기때문에 요청을 한 스레드가 아닌 다른스레드에서 작업을 처리할 수 있다는 장점이 있을 뿐더러 소켓 제한도 없습니다.(심지어 JobQueue에대한 동기화 보장이 커널에서 되어집니다)

 

물론 이 Overlapped모델들은 NetworkIO에만 사용되는건 아닙니다.

말그대로 IO라면 다 사용가능합니다.

 

제가 요새 즐겨읽는 Windows via책에서는 File IO를 통해서 이부분을 설명하고 있습니다.

 

Overlapped모델의 특징이라고 하면 OVERLAPPED라는 객체를 각 작업당 하나씩 전달 해줘야된다는 것인데요.

OVERLAPPED객체는 다음과같이 생겼습니다.

typedef struct _OVERLAPPED {
  ULONG_PTR Internal;
  ULONG_PTR InternalHigh;
  union {
    struct {
      DWORD Offset;
      DWORD OffsetHigh;
    } DUMMYSTRUCTNAME;
    PVOID Pointer;
  } DUMMYUNIONNAME;
  HANDLE    hEvent;
} OVERLAPPED, *LPOVERLAPPED;

그리고 우리가 사용하게 될 WSAOVERLAPPED 객체는 이렇게생겼습니다.

typedef struct _WSAOVERLAPPED {
  DWORD    Internal;
  DWORD    InternalHigh;
  DWORD    Offset;
  DWORD    OffsetHigh;
  WSAEVENT hEvent;
} WSAOVERLAPPED, *LPWSAOVERLAPPED;

생긴게 달라보이지만 동일하게 생겼습니다.

즉 NetworkIO를 사용한다고 해서 일반 OVERLAPPED라는 걸 쓰면 안된다는건 아니구요.

어찌되었든 Internal, InternalHigh 파트는 Overlapped io모델에서 사용할 파라미터니까 우리가 따로 손대지 않습니다. 

그냥 0으로 초기화해서 전달만합니다.

 

Offset에 대한값은 FileIO에 대한 경우... 값을 세팅하지 않으면 IO가 틀어질 가능성이 있지만, NetworkIO의 경우에는 OS가 보장하고 있습니다.

근거는 msdn에 적혀있는 글입니다.

provider에 의해 reserve되어있는 구간이기때문에...

아마 알아서 처리해주겠다라는 의미로 해석하면 될 것 같습니다.

왜냐면 FileIO에서는 저부분에 값을 이상하게 적으면 IO가틀어지게 되거든요...

 

 

잠...잠깐 스탑해야될것 같아요

제가 예상했던거 보다 글이 너무 길어지고있기때문에...

여기서 한번 끊어야될것 같은데요.

 

Overlapped IO모델 1까지는 정리를 하고 API에 대한 내용을 정리하려고 했으나...

서론이 너무 길어지는 바람에 다 채우지 못할것 같네요ㅠㅠ...

다음글에서 마져 채우도록하고 일단 이번 글을 여기서 마무리하도록 하겠습니다.

그럼 긴글 읽어주셔서 감사합니다.

다들 안녕히계세요

320x100

'게임서버 > win socket 프로그래밍' 카테고리의 다른 글

Overlapped IO Model2  (0) 2022.02.15
Overlapped IO Model + heap  (0) 2022.02.11
L4와 L7그리고 서버  (0) 2021.11.04
Win socket API2  (0) 2021.11.03
Window Socket API 기본 정리1  (0) 2021.11.01