WSAAsyncSelect 그림그리기 클라이언트

2021. 11. 13. 21:48게임서버/namespace univ_dev

320x100

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

이번에는 WSAAsyncSelect를 이용한 공유 그림판 클라이언트를 하나 만들어 봤어요.

 

사실 이벤트 드리븐이란게 진행도를 따로 그리기가 힘들어서 그냥 구조를 말로 설명드릴게요.

우선 내가 만든 소켓이랑 윈도우에 UM_NETWORK라는 메세지와 플래그로 FD_CONNECT, FD_CLOSE, FD_WRITE, FD_READ 정도를 async select하게 만들었습니다.

 

위쪽 소켓 초기화 부분은 비동기 소켓 하나 만들고 그냥 아이피 하나 읽어와서 연결하는 부분이라 따로 적진 않겠습니다.

네 뭐 이정도만 올려도 되겠죠?

우선 링거로 Time wait을 없애버렸고 WSAAsyncSelect로 메시지를 등록했습니다.

 

다음으로 이작업이 끝나게 되면 메시지를 기다렸다가 메시지가 뜨게되면 윈도우 프로시져가 돌게되는데요... 뭐 다른 부분은 크게 중요하지 않으니까 WM_MOUSEMOVE와 UM_NETWORK, WM_LBUTTON(UP,DOWN) 부분을 상세히 보고 다른건 그냥 말로 설명드리겠습니다.

WM_CREATE 에서는 그냥 펜을 하나 만들고 끝입니다.

WM_DESTROY에서는 closesocket이랑 pen을 delete해주고 PostQuitMessage를 호출해줍니다.

네... 뭐 command 같은건 기본 형태 그대로기때문에 그냥 정리하지 않겠습니다.

 

먼저 UM_NETWORK case입니다.

네 먼저 네트워크일 경우에 lParam으로 상위 2바이트는 에러에 대한 정보 하위 2바이트는 메세지중 어떤 이벤트인지 전달을 해줍니다.

에러 체크를 해서 에러가 있다면 WM_DESTROY를 포스트 해놓고 끝내버립니다.

그게 아니라면 이제 아까 등록했던 플래그중 하나가 도착했겠죠

CONNECT와 CLOSE는 따로 하는게 없습니다.

그냥 끝나도 모르는거기때문이죵...

CONNECT의 경우에는 최초 연결이 되었을때 FD_WRITE가 뜨기때문에 사실 CONNECT에서도 할 게 없습니다...

그래서 두칸은 그냥 비워뒀구요.

FD_WRITE의 경우에는 Send가 가능하다는 flag이기 때문에 sendFlag를 true로 만들어두고 WriteEvent를 호출합니다.

FD_READ의 경우에는 뭐... L4버퍼에 읽을게 있는거니까 L4에서 링버퍼로 옮긴다음 로직을 수행하면 될 것 같습니다.

 

넵 그럼 먼저 WriteEvent에 대해서 알아보겠습니다.

 우선 WSAEWOULDBLOCK 상태로 더이상 보낼 수 없는 상황이라면 bSendFlag가 false일겁니다.

내 송신버퍼가 다 찼는데 보낼순 없죠... 그렇기 때문에 그냥 리턴해버립니다.

그후에 무한루프를 돌면서 링버퍼에서 Peek을 수행후 Peek할게 없다면 무한루프를 탈출하고

그걸 보냅니다. 대신... 이경우에는 이번에 send를 하지 않으면 다음번엔 send의 기회가 또 오지 않기떄문에 보낼 수있는 모든걸 다 보낸다는 조건이 걸립니다. 그래서 Peek를 해서 있으면 send하고 send가 되었으면 에러체크를 해서 문제가 없다면 sendRet 즉 send성공한 바이트만큼 링버퍼를 확 땡겨버립니다.

 

다음으로 ReadEvent입니다.

좀 길기떄문에 두조각 내서 올리겠습니다.!

먼저 1000바이트정도 버퍼를 잡고 읽습니다.

그리고 WSAEWOULDBLOCK 체크를 하고 문제가 있다면 그냥 종료를 포스트하고 리턴합니다.

그게 아니라면 이제 링버퍼에 인큐를 하는데요 사실 이구간만 보면 좀 메모리 낭비 연산낭비같아 보일수도있습니다만.

TCP는 byte stream입니다. 그래서 50개의 메시지가 하나에 묶여서 올수도있고 1개의 메시지가 50개에 나눠져서 올수도있습니다. 그래서 recv한것을 Enqueue해서 링버퍼에 메모리가 하나의 패킷이 될수 있을때까지 모아둬야되죠... recv를 하는순간 L4버퍼에서는 사라져버리니까요... 그런 이유로 링버퍼에 모으고있었습니다. 물론 지금의 테스트 경우에는 당연히... 그런 일이 발생활 확률이 0에가깝습니다...

그리고 Enqueue를 하는데... Enqueue가 안되는거면 L4에서 가져온 버퍼가 L7에 가득 찼다는 의미입니다... 사실 저런 케이스는 존재할 수 없습니다. 왜냐하면 메시지 하나가 링버퍼의 크기를 벗어나버리면 처리할 수 없겠지만 우리의 경우에는 18바이트로 고정되어있기 때문입니다. 그러니까... 절대로 실패할 일이 없다는거죠... 만약 누군가 악의적으로 10000바이트 이상을 보낸다고 한다면 음... 링버퍼가 먹통이 될 수도있겠네요...

그래서 그런경우에는 Resize를 해주거나 혹은 그냥 터트려야되는 케이스인데... 저의 경우에는 Resize보다 그냥 터트리를 쪽을 택했습니다.

그리고 나서 그림을 그릴 Device context를 가져옵니다.

그러고는 무한루프를 돌면서 패킷 사이즈만큼 뽑아낼 수 있는지 확인하고 그게 안된다면 그냥 브레이크 걸어서 남은 잔재들은 그냥 보관하고 뽑을 수 있다면 Peek를 합니다. 그리고 PeekRet가 0이라면... 이것도 0일수 없겠지만 혹시 모르기 때문에 한번더 확인했습니다.

그럼 Header의 사이즈 만큼 Peek를 성공했으니 Header의 사이즈만큼 writePointer를 앞으로 땡겨버립니다. 그리고 패킷을 가져와서 패킷을 가지고 디큐를 합니다.

디큐에 성공하지 못한다면 링버퍼의 문제겠죠 제가 잘못만든거... 일단은 당장 발생하지 않았습니다.

그리고 나서 MoveToEX, LineTo를 이용해 그림을 그려줍니다.

이걸 링버퍼에 사이즈가 16보다 작아질때까지 계속 반복한뒤에 DC반환하고 리턴입니다.

ㅋㅋㅋ뭐 생각보다 단순하죠... 여기까지가 UM_NETWORK의 케이스였구요...

 

그다음으로 WM_MOUSEMOVE입니다.  당연히 그림판이라면 마우스 좌표를 따라서 팬이 움직여야될거고 마우스 클릭을 통해서 그림이 그려져야될겁니다.

MOUSEMOVE의 케이스입니다.

일단 마우스 x와 y값을 받아온뒤에

헤더와 패킷을 채워줍니다.

그리고 헤더와 패킷을 sendRingBuffer에 enqueue하고 WriteEvent를 메시지에 의한 호출이 아니라 그냥 명시적으로 호출하고있습니다. 뭐 너무 간단하죠...?

 

요건 마우스가 눌려졌는지 안눌려졌는지에 대한 부분입니다... 뭐 따로 정리 안하겠습니다... 사진이 곧 내용입니다.

 

음... 이렇게해서 WSAAsyncSelect로 만든 심플한 공유그림판 클라이언트를 만들어 봤는데요... 다음번에는 서버를 가지고 와볼까 아니면 다른걸 할까 고민중입니다... 일단은 오늘도 긴글 읽어주셔서 정말 감사드립니다.

그럼 다음에 더 좋은 내용으로 돌아오겠습니다. 안녕히계세요

320x100

'게임서버 > namespace univ_dev' 카테고리의 다른 글

Serializing Buffer  (0) 2021.11.23
WSAAsyncSelect로 게임클라이언트 제작  (0) 2021.11.19
WSAAsyncSelect  (0) 2021.11.13
링 버퍼  (2) 2021.11.12
select를 이용한 별움직이기 서버  (0) 2021.11.08