๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

[04์ฃผ์ฐจ] I/O ๋ชจ๋ธ

@GAMEMING2024. 4. 22. 13:24

 

 

ํ•œ๊ตญ๊ณตํ•™๋Œ€ํ•™๊ต ์ •๋‚ดํ›ˆ ๊ต์ˆ˜๋‹˜ 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 (๊ฒŒ์ž„ ์ฐธ์—ฌ์ค‘)

 

 

728x90
GAMEMING
@GAMEMING :: GAMEMING

< School > ํ•œ๊ตญ๊ณตํ•™๋Œ€ํ•™๊ต ๊ฒŒ์ž„๊ณตํ•™๊ณผ ๋ฒค์ฒ˜์ฐฝ์—… ๋ถ€์ „๊ณต < Instagram > ์ž‘์—… @game.plan.diary ๋ณธ๊ณ„ @mi.___.02

์†Œํ†ต ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค ๐Ÿ‘€