ํ๊ตญ๊ณตํ๋ํ๊ต ์ ๋ดํ ๊ต์๋ 4-1 ๊ฒ์ ์๋ฒ ํ๋ก๊ทธ๋๋ฐ ์์ ์ค ์ผ๋ถ์ ๋๋ค.
- ๊ฒ์ ํ๋ก๊ทธ๋จ ํน์ง
: ๊ฒ์์ ์งํ ์๋๋ CPU์ ์๋๊ฐ ์๋๋ผ ์ค์ ์๊ฐ.
: REAL TIME โ ๋ด๊ฐ ๋ช ๋ น์ ์ ๋ ฅํ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ์ง ์์!!!! (์ ์ด๋ ๋ ๋๋ง์ ๊ณ์ ๋จ)
: INDEPENDENCE โ ๋ชจ๋ ๊ฐ์ฒด๊ฐ ๋ ๋ฆฝ์ ํ๋ (๊ฒ์ ์งํ ์ ํด์ ธ ์์ง ์์ + ํ๋ ์ด์ด ํ๋ ์ ๊ธฐ๋ค๋ฆผ, non-blocking)
- Non-blocking I/O
: ๊ธฐ์กด Blocking ์์ผ์ I/O ์์ ์ ์ํํ ๋ ํด๋น ์์ ์ด ์๋ฃ๋ ๋๊น์ง ๋๊ธฐ
: Non-blocking โ ์์ ์ํ ์ ์์ ์ด ์๋ฃ๋์ง ์์๋ ์ฆ์ ๋ฐํ
: WSARecv() ํธ์ถ์ ์ฆ์ ์๋ฃํ์ง ๋ชปํ๋ ๊ฒฝ์ฐ
1) ๋์ฐฉํ ๋ฐ์ดํฐ ์์ผ๋ฉด,
2) WSAEWOULDBLOCK ์๋ฌ ๋ด๊ณ ๋ฆฌํด
3) ๊ธฐ๋ค๋ฆฌ์ง ์์
: ๋ง์ฝ n:1 ์จ๋ผ์ธ ๊ฒ์์ ๊ตฌํํ๋ค๋ฉด? โ ์๋ฒ ๋ฌธ์ ๋ฐ์ (๋ง์ ํด๋ผ์ด์ธํธ๋ก ์ธํ ์ฆ์ WSARecv ์คํจ๋ก ์ฑ๋ฅ ์ ํ) : recv ๋ฟ๋ง ์๋๋ผ accept๋ ๊ณ ๋ คํด์ผ ํจ + ๋ฉํฐ์ฐ๋ ๋ ๊ณ ๋ ค ํ์
// server main loop (Non-blocking I/O)
while(true){
// Process Client Packet
ret = recv(client_A, buf);
if (SUCCESS == ret) process_packet(client_A, buf);
ret = recv(client_B, buf);
if (SUCCESS == ret) process_packet(client_B, buf);
//update world
// do npc ai, do heal......
}
>> ์คํจํ RECV == CPU ๋ญ๋น !!!
- ๋คํธ์ํฌ I/O ๋ชจ๋ธ
: ๊ฒ์ ์๋ฒ์์์ ๋ค์ค ์ ์ โ ์ ํด์ง์ง ์์ ์์, ์ ์ฒ ๊ฐ์ ์ ์, ์๋์ ๋ฎ์ ์ ์ ๋น ๋์ญํญ (bandwidth)
: ๋ธ๋ญํน ๋ฐฉ์ง์ ๋น ๊ท์น์ ์ธ ์ ์ถ๋ ฅ์ ํจ์จ์ ๊ด๋ฆฌ, ๋๊ท๋ชจ ๋ค์ค ์ ์ ๊ด๋ฆฌ๋ก ์ธํด ๋คํธ์ํฌ I/O ๋ชจ๋ธ์ด ํ์
1) Non-blocking I/O
2) Socket Thread
3) Select
4) WSAAsyncSelect
5) WSAEventSelect
6) Overlapped I/O(Event)
7) Overlapped I/O(Callback)
8) I/O Completion Port
- Non-blocking I/O
// Server main loop
while (true) {
for (SOCKET s : sockets){
recv(s, ...); // ๋ชจ๋ ํจํท์ ๋ํ ์ฒ๋ฆฌ
if (success)
/*ํจํท ์ฒ๋ฆฌ*/;
}
// ๋ค๋ฅธ ์์
}
: ๋จ์ โ Busy Waiting ) ๋ชจ๋ ์์ผ์ recv๋ฅผ ๋์๊ฐ๋ฉฐ ๋ฐ๋ณต ํธ์ถํด์ผ ํ๋ฉฐ ๋ง์ recv๊ฐ ์คํจํจ
: ์ฆ์ ์์คํ call โ CPU ๋ญ๋น โ ์ฑ๋ฅ์ ํ
- Socket Thread
: Thread๋ฅผ ํตํ ์ฒ๋ฆฌ, 1:1 ๊ฒ์์๋ฒ๊ฐ ์ฌ๋ฌ ๊ฐ ์๋ ๊ฒ๊ณผ ๋น์ท (Blocking : ์ ๋ ์คํจํ์ง ์๋ ๋์ ๋ฉ์ถ๋ค)
while (!shutdown){
new_sock = accept(sock, &addr, &len);
thread t = thread { do_io, new_sock };
}
void do_io(mysock){
while(true){
recv(mysock);
process_packet();}}
: ๋ค์ค ์์ผ ์ฒ๋ฆฌ ok, non-blocking ๋ถํ์ โ ํจ์จ์ API ํธ์ถ
: ๊ณผ๋ํ thread ๊ฐ์๋ก ์ธํ OS Overhead
- context switching : ์ฐ๋ ๋ ๊ฐ ์ ํ์์ OS๋ ํ์ฌ ์คํ ์ค์ธ ์ฐ๋ ๋ ์ํ๋ฅผ ์ ์ฅ & ์๋ก์ด ์ฐ๋ ๋๋ฅผ ๋ก๋ํ๋ ๊ณผ์ ์ํ
โ ์ฐ๋ ๋ ๊ฐ์๊ฐ ๋ง์์ง์๋ก ์ด ์์ ์ด ๋น๋ฒํ๊ฒ ๋ฐ์
- Thread Control Block(TCB) : ์ฌ๊ธฐ์ ์ฐ๋ ๋์ ์ํ, ์ฐ์ ์์, ๋ ์ง์คํฐ ๊ฐ ๋ฑ์ ์ ๋ณด๊ฐ ํฌํจํ๋๋ฐ ์ด๊ฒ ๋ง์ ์๋ก ๊ด๋ฆฌํ๋ ๋ฐ ํ์ํ ์์์ด ๋ง์์ง
- Stack Memory : ๊ฐ ์ฐ๋ ๋๋ ์์ฒด ์คํ์ ๊ฐ์ง, ์ด๋ ์ฐ๋ ๋๊ฐ ํจ์ ํธ์ถ ์ ์ง์ญ ๋ณ์ ๋ฐ ํจ์ ํธ์ถ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ ๋ฐ ์ฌ์ฉ
โ ๊ณผ๋ํ ์ฐ๋ ๋ ๊ฐ์๋ก ์ธํด ๋ฉ๋ชจ๋ฆฌ ์๋น๊ฐ ๋์ด๋๊ณ ์คํ ๊ณต๊ฐ์ ๊ด๋ฆฌํ๋๋ฐ ์ถ๊ฐ์ ์ธ ์ค๋ฒํค๋๊ฐ ๋ฐ์
: Coroutine์ ์ฌ์ฉํ ์ฒ๋ฆฌ โ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ๋ฒ์ thread - IO์ ๋์ผ + ์ค๋ฒํค๋ ์ ๋ถ ํด๊ฒฐ
: C++ 23์์ ์ ๋๋ก ๊ตฌํ ์์ ์ด๋ผ๊ณ ํจ โ ํ์ฌ ์ฐธ๊ณ https://tango1202.github.io/cpp/modern-cpp-coroutine/
#28. [๋ชจ๋ C++] ์ฝ๋ฃจํด(coroutine)(C++20)
(C++20~) ์ฝ๋ฃจํด์ด ์ถ๊ฐ๋์ด ํจ์์ ์ผ์ ์ ์ง ํ ์ฌ๊ฐ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
tango1202.github.io
- Select
: ์ฌ๋ฌ ๊ฐ์ ์์ผ ๋ฐ์ดํฐ ๋์ฐฉ ์ฌ๋ถ๋ฅผ ํ๋์ API๋ก ๊ฒ์ฌ (ํจ์จ์ API ํธ์ถ)
: ๋จ์ โ socket ๊ฐ์์ ํ๊ณ ์กด์ฌ (unix 64, linux 1024), socket ๊ฐ์๊ฐ ๋ง์์ง ์๋ก ์ฑ๋ฅ ์ ํ ์ฆ๊ฐ
// Select
int select( __in int nfds, __inout fd_set* readfds, __inout fd_set* writefds,
__inout fd_set* exceptfds, __in const struct timeval* timeout);
// nfds : ๊ฒ์ฌํ๋ ์์ผ ๊ฐ์ (window์์ ๋ฌด์)
// readfds : ์ฝ๊ธฐ ๊ฐ๋ฅ ๊ฒ์ฌ์ฉ ์์ผ ์งํฉ ํฌ์ธํฐ
// writefds : ์ฐ๊ธฐ ๊ฐ๋ฅ ๊ฒ์ฌ์ฉ ์์ผ ์งํฉ ํฌ์ธํฐ
// exceptfds : ์๋ฌ ๊ฒ์ฌ์ฉ ์์ผ ์งํฉ ํฌ์ธํฐ
// timeout : select๊ฐ ๊ธฐ๋ค๋ฆฌ๋ ์ต๋ ์๊ฐ
// return value : ์ฌ์ฉ ๊ฐ๋ฅํ ์์ผ ๊ฐ์
// ์ค์ ์ฝ๋ฉ
FD_SET(sock1, &rfds);
FD_SET(sock2, &rfds);
select(0, &rfds, NULL, NULL, &time);
if (FD_ISSET(sock1, &rfds)) recv(sock1, buf, len, 0)
if (FD_ISSET(sock2, &rfds)) recv(sock2, buf, len, 0
// server
while (true) {
select(&all_sockets);
for (i : all_sockets)
if (IS_READY(i)) {
recv(socket[i], buf, โฆ);
process_packet(i, buf);
}
๋ค๋ฅธ ์์
;
}
- WSAAsyncSelect
: ์์ผ ์ด๋ฒคํธ๋ฅผ ํน์ ์๋์ฐ์ ๋ฉ์์ง๋ก ๋ฐ์
int WSAAsyncSelect(
__in SOCKET s, // ์์ผ
__in HWND hWnd, // ๋ฉ์์ง๋ฅผ ๋ฐ์ ์๋์ฐ
__in unsigned int wMsg, // ๋ฉ์์ง ๋ฒํธ
__in long lEvent ); // ๋ฐ์ event ์ ํ
: ํด๋ผ์ด์ธํธ์ ๋ง์ด ์ฐ์, ์๋์ฐ ํ์ / ์๋์ฐ ๋ฉ์ธ์ง ํ ์ฌ์ฉ -> ์ฑ๋ฅ ๋๋ฆผ
: ์๋์ผ๋ก non-blocking mode๋ก ์์ผ ์ ํ
- WSAEventSelect
: ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ ์๋์ฐ๊ฐ ํ์ ์์
: WSAEVENT - ์ด์์ฒด์ ๊ฐ ๊ด๋ฆฌํ๋ ํ๋, true๊ฐ ๋ ๋๊น์ง ์คํ์ ๋ฉ์ถ ์ ์์ (CPU ๋ญ๋น ์์ด)
: socket๊ณผ event์ array๋ฅผ ๋ง๋ค์ด์ WSAWaitForMultipleEvents() ๋ฆฌํด๊ฐ์์ socket ์ถ์ถ (์์ผ ์ 64๊ฐ ์ ํ)
int WSAEventSelect(
__in SOCKET s,
__in WSAEVENT hEventObject,
__in long lNetworkEvents );
// API ๋๊ธฐ ์๋ ๊ฒ์ถ
DWORD WSAWaitForMultipleEvents(
DWORD nCount,
const WSAEVENT *lphEvents,
BOOL bWaitAll,
DWORD dwMiliseconds);
int WSAEnumNetworkEvents(SOCKET s,
WSAEVENT hEventObject;,
LPWSANETWORKEVENTS lpNetworkEvents);
lpNetworkEvents->lNetworkEvents;
: ๋์

- Overlapped I/O
= Asynchronous I/O or ๋น๋๊ธฐ I/O
: IOCP๋ ์ด๋ฅผ ์ด์ฉํจ
: ์ฌ์ฉ ๋ฐฉ๋ฒ์ด select style์ I/O ๋ชจ๋ธ๊ณผ ๋ค๋ฅด๋ค
- I/O ์์ฒญ์ ๋จผ์ ํ๊ณ ์ข ๋ฃ๋ฅผ ๋์ค์ ํ์ธ โ ์์ฒญ ํ ์ฆ์ ๋ฆฌํด & Non-blocking๊ณผ ๋ค๋ฅด๊ฒ ๊ฑฐ์ ์คํจ X
- I/O ์์ฒญ ํ ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ๋ค๋ฅธ ์ผ ๊ฐ๋ฅ, ์ฌ๋ฌ ๊ฐ์ I/O ์์ฒญ์ ๋์์ ๊ฐ๋ฅ
// Select์ while ๋ถ๋ถ๊ณผ ๋น๊ตํด๋ณด๋ฉด ์ ๊ฒ
for (sock : all_sockets)
recv(sock, buf[sock]); // ๋จผ์ ๋ค ๋ฐ์
while (true) {
sock = wait_for_completed_recv();
process_pakcet(sock, buf[sock]);
recv(sock, buf[sock], ...);
}

: Select I/O ๋ชจ๋ธ๋ค์ recv/send์ ๊ฐ๋ฅ ์ฌ๋ถ๋ง์ ๊ฒ์ฌ ํ I/O ์งํ ํ๋ค๋ฉด, ์ด๋ I/O ์์ฒญ ํ ์คํจ ์๋ฃ ํต๋ณด
: (non-blocking ์ฐจ์ด์ ) I/O ํธ์ถ๊ณผ ์๋ฃ์ ๋ถ๋ฆฌ (Overlapped IO๋ ์์ฒญ์ด ์ปค๋์ ๋จ์์ ์ํ๋จ. ๋ฒํผ๊ฐ ์ปค๋์ ๋ฑ๋ก๋จ, ์คํจ
์์)
: send์ recv ํธ์ถ ์ ํจํท ์ก์์ ์ ์๋ฃ์ ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ์ข ๋ฃํจ ( ์ก์์ ์ ์์๋ง์ ์ง์ )
: ๋์๋ค๋ฐ์ ์ผ๋ก recv, sdnd ๊ฐ๋ฅํ๋, ํ๋์ socket์ ํ๋์ recv๋ง ๊ฐ๋ฅ
: OS ์ ์ฅ
- ๋๊ธฐ์ : ๋ฐ์ดํฐ ๋์ฐฉ์ ๋ฒํผ์ ์ ์ฅํด ๋๊ณ Recv ์์ฒญ์ด ์ฌ ๋๊น์ง ๊ธฐ๋ค๋ฆผ, ์ค๋ฉด ๋ณต์ฌํด์ ๋ณด๋
- ๋น๋๊ธฐ์ : ๋ฐ์ดํฐ ๋์ฐฉ์ ์ ์ฅ๋ Recv ์์ฒญ์ด ์๋ค๋ฉด, ์์ฒญ๋ ๋ฒํผ์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ์ฆ์ ์๋ฃํจ
SOCKET WSASocket(int af, int type, int protocol,
LPWSAPROTOCOL_INFO lpProtocolInfo, GROUP g, DWORD dwFlags)
// af : address family
// type : socket type
// protocol : ์ฌ์ฉํ ํ๋กํ ์ฝ ์ข
๋ฅ
// lpProtocolInfo : ํ๋กํ ์ฝ ์ ๋ณด (NULL)
// g : ์์ฝ
// dwFlags : WSA_FLAG_OVERLAPPED -> ์ด๋ ๊ฒ ํ๊ณ ์ธ์ค๋ฒ๋ฉ ์จ๋ ์๋์๊ฐ
typedef struct WSAOVERLAPPED {
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
WSAEVENT hEvent;
} WSAOVERLAPPED, FAR *LPWSAOVERLAPPED;
// DWORD type ๋ถ๋ถ์ 0์ผ๋ก ์ด๊ธฐํ ํ ์ฌ์ฉ
// hEvent : I/O๊ฐ ์๋ฃ๋์์์ ์๋ ค์ฃผ๋ Eevent ํธ๋ค
// -> LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine (callback ํจ์, ๋ค์์ ์ค๋ช
)
// Overlapped I/O Event model
// LPWSAOVERLAPPED lpOverlapped ๊ตฌ์กฐ์ฒด์ WSAEVENT hEvent ์ฌ์ฉ
WSAWaitForMultipleEvents() // ์๋ฃ ํ์ธ(recvํ๋ค, ๋ช๋ฐ์ดํธ ์ฝ์๋ค๋ฅผ ์๋ ค์ฃผ์ง ์์)
WSAGetOverlappedResult() // ์ ๋ณด ์ป๊ธฐ(recv์๋ฃ๊ฐ ๋์๋ค๋ฉด, ์ฑ๊ณต/์คํจ, ์ฑ๊ณตํ๋ค๋ฉด ๋ช ๋ฐ์ดํธ ํ๋์ง ์๋ ค์ค)
BOOL WSAGetOverlappedResult(
SOCKET s,
LPWSAOVERLAPPED lpOverlapped,
LPDWORD lpcbTransfer,
BOOL fWait,
LPDWORD lpdwFlags);
}
// s : socket
// lpOverlapped : WSARecv์ ๋ฃ์๋ ๊ตฌ์กฐ์ฒด
// lpbTransfer : ์ ์ก๋ ๋ฐ์ดํฐ ํฌ๊ธฐ
// fWait : ๋๊ธฐ ์ฌ๋ถ
// lpdwFlags : Recv์ lpFlag ๊ฒฐ๊ณผ
// 1. WSACreateEvent()๋ฅผ ์ฌ์ฉํด์ ์ด๋ฒคํธ ์์ฑ
// 2. WSAOVERLAPPED ๊ตฌ์กฐ์ฒด ๋ณ์์ ์ธ, 0์ผ๋ก ์ด๊ธฐํ, hEvent์ 1์ ์ด๋ฒคํธ
// 3. WSASend(), WSARecv() -> 2์ ๊ตฌ์กฐ์ฒด๋ฅผ WSAOVERLAPPED์
// โ ์ค๋ณต ์ฌ์ฉ ๋ถ๊ฐ๋ฅ!! ํธ์ถ ์๋ฃ ํ ์ฌ์ฌ์ฉ
// - lpCompletionROUTINE -> NULL
// 4. WSAWaitForMultipleEvents()ํจ์๋ก ์๋ฃ ์ด๋ฒคํธ ๊ฐ์ง
// 5. WSAGetOverlappedResult()ํจ์๋ก ์ ๋ณด ํ๋
// ๋จ์ : ์์คํ
ํธ์ถ์ด ๋น๋ฒ, ์ต๋ ๋์ 64 ==> callback mmodel
// Overlapped I/O Callback model
// ์ด๋ฒคํธ ์ ํ ๊ฐ์ ์์ผ๋ฉฐ ์ฌ์ฉ ํธ๋ฆฌ
// LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine ํจ์ ์ฌ์ฉ
// Overlapped I/O๊ฐ ๋๋ํ lpCompletionRoutine ํธ์ถ
void CALLBACK CompletionROUTINE(
DWORD dwError,
DWORD cbTransferred,
LPWSAOVERLAPPED lpOverlapped,
DWORD dwFlags);
}
// dwError : ์์
์ ์ฑ๊ณต ์ ๋ฌด
// cbTransferred : ์ ์ก๋ ๋ฐ์ดํธ ์
// lpOverlapped : WSASend, WSARev์์ ์ฌ์ฉํ ๊ตฌ์กฐ์ฒด
// dwflags : WSASend, WSARecv์์ ์ฌ์ฉํ flag
// 1) ๋๊ฐ/์ธ์ Callback์ ํธ์ถํ๋๊ฐ?
// OS์์ I/O ์์
์ด ์๋ฃ๋๋ฉด
// ํด๋น ์์
์ ๋ํ ์๋ฃ๋ฅผ ์๋ฆฌ๊ธฐ ์ํด ์ง์ ๋ ์ฝ๋ฐฑ ํจ์๊ฐ ํธ์ถ
// 2) ๋ฉํฐ์ฐ๋ ๋๋ก ๋์์ Callback์ด ์คํ๋๋๊ฐ?
// No!, ์ฝ๋ฐฑ ํจ์๊ฐ ๋จ์ผ ์ฐ๋ ๋์์ ์์ฐจ์ ์ผ๋ก ์คํ
// 3) ์ด์์ฒด์ ๊ฐ ์คํ์ค์ธ ํ๋ก๊ทธ๋จ์ ๊ฐ์ ๋ก ๋ฉ์ถ๊ณ Callback์ ์คํํ๋๊ฐ?
// No!, I/O ์์
์ด ์๋ฃ๋๋ฉด ํด๋น ์์
์ ๋ํ ์๋ฃ๋ฅผ ์๋ฆฌ๊ธฐ ์ํด ๋ฑ๋ก๋ ์ฝ๋ฐฑ ํจ์๋ฅผ ํธ์ถ
// Callback์ ์ฌ์ฉํ ํ๋ก๊ทธ๋จ์ ๊ตฌ์กฐ
// ํ ์ผ์ด ์์ผ๋ฉด ํ๊ณ , ์์ผ๋ฉด SleepEx๋ฅผ ํธ์ถํ๋ค
- ์ฑํ ์๋ฒ ( Overlapped 1: N Chat Server )
: ํด๋ผ์ด์ธํธ์ ๋ค๋ฅธ ํด๋ผ์ด์ธํธ์ ๋ฉ์์ง๋ ์ ๋ฌํด ์ฃผ์ด์ผ ํจ
: ๋ฉ์์ง์ ํด๋ผ์ด์ธํธ ID ํฌํจํด์ผ ํจ โ Packet ๋จ์๋ก Send/Recv ํ์ (size + id + message)
: ๋์ด์ send์ recv๊ฐ ์ฐจ๋ก๋ก ํธ์ถ๋์ง ์์ (์ ํด์ง ์์ ์์ด ๋์๋ค๋ฐ์ ๋ฐ์)
: Send์ Recv๊ฐ ๋์ด์ OVERLAPPED, WSABUF, BUF๋ฅผ ๊ณต์ ํ ์ ์์
[ ์๋ฒ ]
#include <iostream>
#include <unordered_map>
#include <WS2tcpip.h>
#pragma comment (lib, "WS2_32.LIB")
constexpr short PORT = 4000;
constexpr int BUFSIZE = 256;
bool b_shutdown = false;
class SESSION;
std::unordered_map<LPWSAOVERLAPPED, int> g_session_map;
std::unordered_map<int, SESSION> g_players;
void CALLBACK send_callback(DWORD err, DWORD sent_size, LPWSAOVERLAPPED pover, DWORD recv_flg);
void CALLBACK recv_callback(DWORD err, DWORD recv_size, LPWSAOVERLAPPED pover, DWORD recv_flg);
void print_error(const char* msg, int err_no);
class EXP_OVER {
public:
WSAOVERLAPPED over; // WSAOVERLAPPED ๊ตฌ์กฐ์ฒด
WSABUF wsabuf[1]; // WSABUF ๋ฐฐ์ด
char buf[BUFSIZE]; // ๋ฐ์ดํฐ ๋ฒํผ
EXP_OVER(int s_id, char* msg, int m_size)
{
ZeroMemory(&over, sizeof(over));
wsabuf[0].buf = buf;
wsabuf[0].len = m_size + 2;
buf[0] = m_size + 2;
buf[1] = s_id;
memcpy(buf + 2, msg, m_size);
}
};
class SESSION {
char buf[BUFSIZE]; // ๋ฐ์ดํฐ ๋ฒํผ
WSABUF wsabuf[1]; // WSABUF ๋ฐฐ์ด
SOCKET client_s; // ํด๋ผ์ด์ธํธ ์์ผ
WSAOVERLAPPED wsaover; // WSAOVERLAPPED ๊ตฌ์กฐ์ฒด
public:
SESSION(SOCKET s, int my_id) :client_s(s) {
g_session_map[&wsaover] = my_id;
wsabuf[0].buf = buf;
wsabuf[0].len = BUFSIZE;
}
SESSION() {
std::cout << "ERROR";
exit(-1);
}
~SESSION() { closesocket(client_s); }
// ์์ ํจ์ - ๋น๋๊ธฐ
void do_recv()
{
DWORD recv_flag = 0;
ZeroMemory(&wsaover, sizeof(wsaover));
int res = WSARecv(client_s, wsabuf, 1, nullptr, &recv_flag, &wsaover, recv_callback);
if (res != 0) {
int err_no = WSAGetLastError();
if (WSA_IO_PENDING != err_no)
print_error("WSARecv", WSAGetLastError());
}
}
// ์ก์ ํจ์ - ๋น๋๊ธฐ
void do_send(int s_id, char* msg, int recv_size)
{
auto b = new EXP_OVER(s_id, msg, recv_size);
int res = WSASend(client_s, b->wsabuf, 1, nullptr, 0, &b->over, send_callback);
if (res != 0) {
print_error("WSARecv", WSAGetLastError());
}
}
void print_message(DWORD recv_size)
{
int my_id = g_session_map[&wsaover];
std::cout << "Client[" << my_id << "] Sent : ";
for (DWORD i = 0; i < recv_size; ++i)
std::cout << buf[i];
std::cout << std::endl;
}
void broadcast(int m_size)
{
for (auto& p : g_players)
p.second.do_send(g_session_map[&wsaover], buf, m_size);
}
};
void print_error(const char* msg, int err_no)
{
// ์ถ๋ ฅ ๋ฉ์ธ์ง๋ฅผ ์ ์ฅํ buf
WCHAR* msg_buf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, err_no, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPWSTR>(&msg_buf), 0, NULL);
std::cout << msg;
std::wcout << L" : ์๋ฌ : " << msg_buf;
while (true); // ๋๋ฒ๊น
LocalFree(msg_buf);
}
void CALLBACK send_callback(DWORD err, DWORD sent_size, LPWSAOVERLAPPED pover, DWORD recv_flg)
{
if (err != 0)
print_error("WSASend", WSAGetLastError());;
auto b = reinterpret_cast<EXP_OVER*>(pover); // EXP_OVER ๊ฐ์ฒด๋ก ํ๋ณํ
delete b; // ๋ฉ๋ชจ๋ฆฌ ํด์
}
void CALLBACK recv_callback(DWORD err, DWORD recv_size, LPWSAOVERLAPPED pover, DWORD recv_flg)
{
if (err != 0) {
print_error("WSARecv", WSAGetLastError());
}
int my_id = g_session_map[pover]; // ํ์ฌ ์ธ์
ID ๊ฐ์ ธ์ด
if (recv_size == 0) {
// b_shutdown = true;
// (์ฌ๊ธฐ์๋ ์ฝ๋) ์ ์ ํ๋๊ฐ ๋๊ฐ๋ค๊ณ ์๋ฒ ๋ ๋ฆฌ๋ ์ง
g_players.erase(my_id); // ํด๋น ํด๋ผ์ด์ธํธ ์ ๊ฑฐ
return;
}
g_players[my_id].print_message(recv_size);
g_players[my_id].broadcast(recv_size);
g_players[my_id].do_recv();
}
int main()
{
std::wcout.imbue(std::locale("korean"));
WSADATA WSAData;
WSAStartup(MAKEWORD(2, 0), &WSAData);
SOCKET server_s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED);
SOCKADDR_IN server_a;
server_a.sin_family = AF_INET;
server_a.sin_port = htons(PORT);
server_a.sin_addr.S_un.S_addr = htons(INADDR_ANY);
bind(server_s, reinterpret_cast<sockaddr*>(&server_a), sizeof(server_a));
listen(server_s, SOMAXCONN);
int addr_size = sizeof(server_a);
int id = 0;
while (b_shutdown == false) {
SOCKET client_s = WSAAccept(server_s, reinterpret_cast<sockaddr*>(&server_a), &addr_size, nullptr, 0);
g_players.try_emplace(id, client_s, id); // ์๋ก์ด ํด๋ผ์ด์ธํธ ์ธ์
์์ฑ
g_players[id++].do_recv(); // ์์ ์์
}
g_players.clear(); // ๋ชจ๋ ์ธ์
์ ๊ฑฐ
closesocket(server_s);
WSACleanup();
}
[ ํด๋ผ์ด์ธํธ ]
#include <iostream>
#include <WS2tcpip.h>
#pragma comment (lib, "WS2_32.LIB")
constexpr short PORT = 4000;
constexpr char SERVER_ADDR[] = "127.0.0.1";
constexpr int BUFSIZE = 256;
bool bshutdown = false;
WSABUF wsabuf[1]; // WSABUF ๋ฐฐ์ด
SOCKET server_s; // ์๋ฒ ์์ผ
char buf[BUFSIZE]; // ๋ฒํผ
WSAOVERLAPPED wsaover; // WSAOVERLAPPED๊ตฌ์กฐ์ฒด
void CALLBACK send_callback(DWORD, DWORD, LPWSAOVERLAPPED, DWORD);
void read_n_send()
{
std::cout << "Enter Message : ";
std::cin.getline(buf, BUFSIZE);
wsabuf[0].buf = buf;
wsabuf[0].len = static_cast<int>(strlen(buf) + 1);
if (wsabuf[0].len == 1) {
bshutdown = true;
return;
}
ZeroMemory(&wsaover, sizeof(wsaover));
WSASend(server_s, wsabuf, 1, nullptr, 0, &wsaover, send_callback);
}
void CALLBACK recv_callback(DWORD err, DWORD recv_size, LPWSAOVERLAPPED pwsaover, DWORD sendflag)
{
int p_size = 0; // ํ์ฌ ์ฒ๋ฆฌํ ๋ฐ์ดํฐ ํฌ๊ธฐ
while (recv_size > p_size) { // ๋ฐ์ ๋ฐ์ดํฐ๊ฐ ์ฒ๋ฆฌ๋์ง ์์๋ค๋ฉด ๋ฐ๋ณต
int m_size = buf[0 + p_size]; // ๋ฉ์์ง ํฌ๊ธฐ ์ฝ๊ธฐ
std::cout << "Player [" << static_cast<int>(buf[1 + p_size]) << "] : "; // ํ๋ ์ด์ด ID ์ถ๋ ฅ
for (DWORD i = 0; i < m_size; ++i)
std::cout << buf[i + p_size + 2]; // ๋ฉ์์ง ์ถ๋ ฅ
std::cout << std::endl;
p_size = p_size + m_size; // ์ฒ๋ฆฌํ ๋ฐ์ดํฐ ํฌ๊ธฐ ๊ฐฑ์
}
read_n_send();
}
void CALLBACK send_callback(DWORD err, DWORD sent_size, LPWSAOVERLAPPED pwsaover, DWORD sendflag)
{
wsabuf[0].len = BUFSIZE;
DWORD recv_flag = 0;
ZeroMemory(pwsaover, sizeof(*pwsaover));
WSARecv(server_s, wsabuf, 1, nullptr, &recv_flag, pwsaover, recv_callback);
}
int main()
{
std::wcout.imbue(std::locale("korean"));
WSADATA WSAData;
WSAStartup(MAKEWORD(2, 0), &WSAData);
server_s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED);
SOCKADDR_IN server_a;
server_a.sin_family = AF_INET;
server_a.sin_port = htons(PORT);
inet_pton(AF_INET, SERVER_ADDR, &server_a.sin_addr);
connect(server_s, reinterpret_cast<sockaddr*>(&server_a), sizeof(server_a));
read_n_send(); // ๋ฉ์์ง ์ฝ๊ณ ์ ์ก ์์
while (false == bshutdown) {
SleepEx(0, TRUE); // ์ด๋ฒคํธ๋ฅผ ๊ธฐ๋ค๋ฆผ
}
closesocket(server_s);
WSACleanup();
}

- EXP_OVER์์ ์์ฑ์ ๋ถ๋ถ์ m_size + 2 ๊ฐ์ ์ฝ๋๋ ์๋นํ ์ํํ ์ฝ๋.
- ์๋ฒ์์ ํด๋ผ๋ก ๋ณด๋ด๋ ํจํท์ด ๋ญ์ณ์ ์์ ๋์ ์ฒ๋ฆฌ ์๊ณ , ์์์ ์์น์์ ์๋ ค ์ค๊ธฐ๋ ํ๋๋ฐ ์ด๋ฅผ ์ด์ด์ฃผ๋ ์ฝ๋๋ ํ์.
- Avatar(๋) / PC(Playing Character. ๋ค๋ฅธ ์ฌ๋)
- ์๋ฒ์ ๊ฐ์ฒด ๋ณํ ์ํ๋ BroadCast
- ํจํท ์ข ๋ฅ์ ๋ค์ํ โ ํ๋กํ ์ฝ ์ ์ ํ์ (์ข ๋ฅ - Object ์ถ๊ฐ/์ด๋/์ญ์ , Size/Type/Data(id, ์ขํ, ์๋ฐํ์ฌ๋ถ ๋ฑ)
- IOCP ๋ฉํฐ์ฐ๋ ๋
: GetQueuedCompletionStatus๋ฅผ ๋ณ๋์ ์ฐ๋ ๋๋ก (GQCS๋ฃจํ๋ฅผ worker thread)
: Worker Thread ์์ฑ - Core ๊ฐ์ / thread::hardware_concurrency() - ์ฐ๋ ๋ ๊ฐ์
// ๋ชจ๋ ์ ์ญ
unordered_map <int, CLIENT> clients;
SOCKET g_s_socket;
HANDLE g_h_iocp;
: g_h_iocp, g_s_socket๋ ์ด๊ธฐํ๋ ํ ๋ณํ์ง ์๊ธฐ์ No DataRace
: Clients ์ปจํ ์ด๋ ์์ฒด๊ฐ ์์ โ DataRace
โ concurrent_unordered_map <int, atomic_shared_ptr <CLIENT>> clients;
class CLIENT
{
int m_id; // no data race
EXP_OVER m_recv_over; // no data race
unsigned char m_prev_recv; // no data race
SOCKET m_s; // no data race -> data race
bool m_inuse; // data race
char m_name[MAX_NAME]; // data race
short m_x, m_y; // data race
};
// no data race - ์ค์ง ํ ์ฐ๋ ๋์์๋ง ์ ๊ทผํ ๋, ๊ฐ์ด ์ ํด์ง ํ ๋ค๋ฅธ ์๋ฌด๋ ๊ฐ์ ๋ณ๊ฒฝ X
: ์ฌ์ฌ์ฉ ๋ฌธ์
โ clients ์ฌ์ฌ์ฉ์ m_socket์ด DataRace. ๊ฐ์ด validํ์ง๋ฅผ ๋ํ๋ด๋ ๋ณ์๊ฐ ํ์ (m_state)
โ m_state : ST_FREE(๋ฏธ์ฌ์ฉ), ST_ALLOC(Accpet๋จ, ์์ง cs_login ๋ฐ์ง ์์, ์ด ํด๋ผ์ด์ธํธ๋ broadcasting์์ ์ ์ธ), ST_INGAME (๊ฒ์ ์ฐธ์ฌ์ค)
'๐ง Study > Game Server' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[06์ฃผ์ฐจ] ๋ฉํฐ์ฐ๋ ๋ 01 (0) | 2024.04.22 |
---|---|
[05์ฃผ์ฐจ] IOCP (0) | 2024.04.22 |
[02์ฃผ์ฐจ] ํ๊ฒฝ (1) | 2024.04.19 |
[03์ฃผ์ฐจ] ๋คํธ์ํน - 1 (1) | 2024.04.01 |
[01์ฃผ์ฐจA] ๊ฒ์ ์๋ฒ (1) | 2024.03.12 |