3์ฅ ์์ผ ํ๋ก๊ทธ๋๋ฐ
6. ๋ ผ๋ธ๋ก ์์ผ
์ผ๋์ผ ๋คํธ์ํน ํ๋ก๊ทธ๋จ์ ๊ฐ๋ฐํ ๋๋ ๋ธ๋กํน ์์ผ์ ์ฌ์ฉํด๋ ๋ฌด๋ฐฉํ๋ค.
ํ์ง๋ง ๋คํธ์ํน์ ํด์ผ ํ๋ ๋์์ด ์ฌ๋ฟ์ด๋ผ๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น?
๋คํํ ๋๋ถ๋ถ ์ด์์ฒด์ ์์๋ ์์ผ ํจ์๊ฐ ๋ธ๋กํน๋์ง ์๊ฒ ํ๋ API๋ฅผ ์ถ๊ฐ๋ก ์ ๊ณตํ๋ค. ์ด๋ฅผ ๋ ผ๋ธ๋ก ์์ผ์ด๋ผ ํ๋ค.
๋ ผ๋ธ๋ก ์์ผ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์๋์ ๊ฐ๋ค.
void NonBlockSocketOperation()
{
s = socket(TCP);
...;
s.connect(...);
// ๋
ผ๋ธ๋ก ์์ผ์ผ๋ก ๋ณ๊ฒฝ
s.SetNonBlocking(true);
while (true)
{
r = s.send(dest, data);
if (r == EWOULDBLOCK)
{
// ๋ธ๋กํน ๊ฑธ๋ฆด ์ํฉ์ด์๋ค. ์ก์ ์ ์ ํ๋ค.
continue;
}
if (r == OK)
{
// ๋ณด๋ด๊ธฐ ์ฑ๊ณต์ ๋ํ ์ฒ๋ฆฌ
}
else
{
// ๋ณด๋ด๊ธฐ ์คํจ์ ๋ํ ์ฒ๋ฆฌ
}
}
}
- ์์ผ์ ๋ ผ๋ธ๋ก ์์ผ์ผ๋ก ๋ชจ๋ ์ ํํ๋ค.
- ๋ ผ๋ธ๋ก ์์ผ์ ๋ํด ํ์์ฒ๋ผ ์ก์ , ์์ , ์ฐ๊ฒฐ๊ณผ ๊ด๋ จ๋ ํจ์๋ฅผ ํธ์ถํ๋ค.
- ์ด ํจ์ ํธ์ถ์ ๋ํด ์ฑ๊ณต ํน์ would block ์ค๋ฅ ๋ ์ค ํ๋๋ฅผ ๋ฌด์กฐ๊ฑด ์ฆ์ ๋ฆฌํดํ๋ค.
์๋๋ฐฉ์๊ฒ TCP ์ ์ ์ญํ ์ ํ๋ connect() ํจ์๋ฅผ ๋ ผ๋ธ๋กํน์ผ๋ก ์ธ ๋๋ ์กฐ๊ธ ๋ค๋ฅด๋ค. ์๋ํ๋ฉด would block์ด ์ก์์ ํจ์์์ ๋ฆฌํด๋๋ค๋ฉด ์๋ฌด ์ผ๋ ์ผ์ด๋์ง ์์ง๋ง, ์ฐ๊ฒฐ ํจ์์์ ๋ฆฌํด๋๋ค๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋์ ์ ์ฐ๊ฒฐ๋ ๊ฒ์ธ์ง ํ์ธ์ด ํ์ํ๋ค. ๋ค์ connect()๋ฅผ ๋ค์ ์๋ํ๋ ๊ฒ๋ณด๋จ 0๋ฐ์ดํธ ์ก์ ๋ฐฉ์์ ํตํด ํ์ธํ๋ ๊ฒ์ด ์ข๋ค.
void NonBlockSocketOperation()
{
result = s.connect();
if (result = = EWOULDBLOCK)
{
while (true)
{
byte emptyData[0]; // ๊ธธ์ด 0์ธ ๋ฐฐ์ด
result = s.send(emptyData);
if (result == OK)
{
// ์ฐ๊ฒฐ ์ฑ๊ณต ์ฒ๋ฆฌ
}
else if (result == ENOTCONN)
{
// ์ฐ๊ฒฐ์ด ์์ง ์งํ ์ค์ด๋ค.
}
else
{
// ์ฐ๊ฒฐ ์คํจ ์ฒ๋ฆฌ
}
}
}
}
- TCP๋ ์คํธ๋ฆผ ๊ธฐ๋ฐ ํ๋กํ ์ฝ์ด๊ธฐ ๋๋ฌธ์ 0๋ฐ์ดํธ๋ฅผ ๋ณด๋ด๋ ๊ฒ์ ์ฌ์ค์ ์๋ฌด๊ฒ๋ ํ์ง ์๋ ์ ์ด๋ค.
- ์ฑ๊ณต์ ๋ฆฌํดํ๋ฉด TCP ์ฐ๊ฒฐ์ด ๋์ด ์๋ค๋ ์๋ฏธ์ด๋ค.
- ENOTCONN์ ๋ฆฌํดํ๋ฉด TCP ์ฐ๊ฒฐ์ด ์งํ ์ค์ด๋ค.
- ๊ธฐํ ์ค๋ฅ ์ฝ๋๊ฐ ๋์ค๋ฉด TCP ์ฐ๊ฒฐ ์๋๊ฐ ์คํจํ ๊ฒ์ด๋ค.
๋ ผ๋ธ๋ก ์์ผ์ ์ฌ์ฉํ๋ฉด ํ ์ค๋ ๋์์ ์ฌ๋ฌ ์์ผ์ ํ๊บผ๋ฒ์ ๋ค๋ฃฐ ์ ์๋ค.
List<Socket> sockets;
void NonBlockSocketOperation()
{
foreach(s in sockets) // ๊ฐ ์์ผ์ ๋ํด
{
// ๋
ผ๋ธ๋ก ์์ . ์ค๋ฅ ์ฝ๋์ ์์ ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋๋ค.
(result, data) = s.receive();
if (data.length > 0) // ์ ์์ ํ์ผ๋ฉด
{
print(data); // ์ถ๋ ฅ
}
else if (result != EWOULDBLOCK)
{
// would block์ด ์๋๋ฉด ์ค๋ฅ๊ฐ ๋ ๊ฒ์ด๋ฏ๋ก
// ํ์ํ ์ฒ๋ฆฌ๋ฅผ ํ๋ค.
...;
}
}
}
- ์์ ํ ๋ฐ์ดํฐ๊ฐ ์์ผ๋ฉด would block ์ค๋ฅ๋ฅผ ๋ฆฌํดํ๋ค.
์์ ์ฝ๋๋ค์ ์ค์ ๋ก ๊ตฌ๋ํ๋ฉด ํด๋น ์ค๋ ๋๋ ์์ผ์ด would block์ธ ์ํ์์๋ ๊ณ์ํด์ ๋ฃจํ๋ฅผ ๋๊ณ CPU๋ฅผ ์ฌ์ง ๋ชปํ๋ ๋ฐ์ ์ํ๋ก ๋ง๋ ๋ค. ์ฆ, CPU ์ฝ์ด ํ๋๊ฐ 100% ์ฌ์ฉ๋์ ์ฐจ์งํ๋ค. select()๋ poll() ํจ์๋ฅผ ์ฌ์ฉํ๋ค๋ฉด CPU ํญ์ฃผ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
- ์ฌ๋ฌ ์์ผ ์ค ํ๋๋ผ๋ would block์ด์๋ ์ํ์ ๋ณํ๊ฐ ์ผ์ด๋๋ฉด, ์ฆ ์ก์ ๋ฒํผ์ ๋น ๊ณต๊ฐ์ด ์๊ธฐ๊ฑฐ๋ ์์ ๋ฒํผ์ ๋ญ๊ฐ๊ฐ ๋ค์ด์จ๋ค๋ฉด ๊ทธ ์ํฉ์ ์๋ ค ์ค๋ค.
- ํน์ ๊ทธ๊ฒ์ ์๋ ค ์ฃผ๊ธฐ ์ ๊น์ง๋ ๋ธ๋กํน ์ค์ด์ด์ CPU ์ฌ์ฉ๋ ํญ์ฃผ๋ฅผ ํด๊ฒฐํ๋ค.
List<Socket> sockets;
void NonBlockSocketOperation()
{
while (true)
{
// 100๋ฐ๋ฆฌ์ด๊น์ง ๋๊ธฐ
// 1๊ฐ๋ผ๋ I/O ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๋ ์ํ๊ฐ ๋๋ฉด
// ๊ทธ ์ ์๋ผ๋ ๋ฆฌํด
select(sockets, 100ms);
foreach(s in sockets)
{
// ๋
ผ๋ธ๋ก ์์
(result, data) = s.receive();
if (data.length > 0)
{
print(data);
}
else if (result != EWOULDBLOCK)
{
// ์์ผ ์ค๋ฅ ์ฒ๋ฆฌ๋ฅผ ํ๋ค.
}
}
}
}
7. Overlapped I/O ํน์ ๋น๋๊ธฐ I/O
๋ ผ๋ธ๋ก ์์ผ์ ๋จ์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ์์ผ I/O ํจ์๊ฐ ๋ฆฌํดํ ์ฝ๋๊ฐ would block์ธ ๊ฒฝ์ฐ ์ฌ์๋ ํธ์ถ ๋ญ๋น๊ฐ ๋ฐ์ํ๋ค.
- ์์ผ I/O ํจ์๋ฅผ ํธ์ถํ ๋ ์ ๋ ฅํ๋ ๋ฐ์ดํฐ ๋ธ๋ก์ ๋ํ ๋ณต์ฌ ์ฐ์ฐ์ด ๋ฐ์ํ๋ค.
์ด๋ฌํ ๋จ์ ์ ํด๊ฒฐํด์ฃผ๋ ๊ฒ์ด Overlapped ๋๋ Asynchronous(๋น๋๊ธฐ) I/O์ด๋ค.
- ์์ผ์ ๋ํด Overlapped ์ก์ธ์ค๋ฅผ ๊ฑธ๊ณ ์ฑ๊ณตํ๋์ง ํ์ธ ํ ์ฑ๊ณตํ์ผ๋ฉด ๊ฒฐ๊ด๊ฐ์ ์ป์ด ์์ ๋๋จธ์ง๋ฅผ ์ฒ๋ฆฌํ๋ค.
- ์๋ฃ๋๊ธฐ ์ ๊น์ง Overlapped status ๊ฐ์ฒด๊ฐ ๋ฐ์ดํฐ ๋ธ๋ก์ ์ค๊ฐ์ ํผ์ํ์ง ๋ง์์ผ ํ๋ค.
void OverlappedSocketOperation()
{
var overlappedSendStatus;
(result, length) = s.OverlappedSend( // ๋ธ๋กํน ์์ผ ๊ทธ๋๋ก ์ฌ์ฉ
data,
overlappedSendStatus);
// ์ฆ์ ๋ฆฌํด
if (length > 0)
{
// ๋ณด๋ด๊ธฐ ์ฑ๊ณต
}
else if (result == WSA_IO_PENDING)
{
// Overlapped I/O๊ฐ ์งํ ์ค
while (true)
{
(result, length) =
GetOverlappedResult(s,
overlappedSendStatus);
if (length > 0)
{
// ์ ๋ณด๋๋ค.
}
else
{
// ์์ง I/O pending์ด๋ค.
}
}
}
}
๋ ผ๋ธ๋ก ์์ผ๊ณผ Overlapped I/O์ ์์ผ ๊ฐ์๊ฐ ๋ง์ ๋ ๋ฃจํ๋ฅผ ๋๋ฉฐ ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
์ด๋ฌํ ๋ฃจํ ์์ด ํ ๋ฒ์ ๋๋ด๋ ๋ฐฉ๋ฒ์ ์์๊น?
๊ทธ๋์ ๋ฑ์ฅํ ๊ฒ์ด IOCP์ epoll์ด๋ค.
8. epoll
epoll์ ํด๋น ์์ผ์ด I/O ๊ฐ๋ฅ ์ํ๊ฐ ๋๋ฉด ์ด๋ฅผ ๊ฐ์งํด์ ์ฌ์ฉ์์๊ฒ ์๋ฆผ์ ํด์ฃผ๋ ์ญํ ์ ํ๋ค.
- ์์ผ 2๊ฐ I/O ๊ฐ๋ฅ์ด ๋๋ ์๊ฐ epoll์ ์ด ์ํฉ์ ๋ด์ฅ ํ์ push ํ๋ค.
- ์ฌ์ฉ์๋ epoll์์ ์ด๋ฌํ ์ด๋ฒคํธ ์ ๋ณด๋ฅผ pop ํ ์ ์๋ค.
๋ฐ๋ผ์ ์์ผ์ด 1๋ง ๊ฐ๋ผ๊ณ ํด๋ ์ด ์ค์์ I/O ๊ฐ๋ฅ์ด ๋ ๊ฒ๋ค๋ง ์ป์ ์ ์๋ค.
์์ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
epoll = new epoll();
foreach(s in sockets)
{
epoll.add(s, GetUserPtr(s));
}
events = epoll.wait(100ms);
foreach(event in events)
{
s = event.socket;
// ์ epoll.add์ ๋ค์ด๊ฐ๋ ๊ฐ์ ์ป๋๋ค.
userPtr = event.userPtr;
// ์์ ? ์ก์ ?
type = event.type;
if (type = = ReceiveEvent)
{
(result, data) = s.recv();
if (data.length > 0)
{
// ์์ ๋ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ค.
Process(userPtr, s, data);
}
}
}
- ๋ชจ๋ ์์ผ์ ๋ํ select() ํจ์ ๋์ epoll์์ ์ด๋ฒคํธ๋ฅผ ๊บผ๋ด ์ค๋ wait() ํจ์๋ฅผ ํธ์ถํ๋ค. ์ด ํจ์๋ ์ฌ์ฉ์๊ฐ ์ํ๋ ์๊ฐ๊น์ง๋ง ๋ธ๋กํน๋๋ฉฐ ๊ทธ์ ์ ์ด๋ฒคํธ๊ฐ ์๊ธฐ๋ ์ฆ์ ๋ฆฌํดํ๋ค. ์ถ๋ ฅ ๊ฐ์๋ ๊บผ๋ด์จ ์ด๋ฒคํธ๋ค์ด ์๋ค.
- ๊ฐ ์ด๋ฒคํธ์ ๋ํด ๋ฃจํ๋ฅผ ๋๋ฉฐ ์ด๋ฒคํธ๊ฐ ๊ฐ๋ฆฌํค๋ ์์ผ ๊ฐ์ฒด์ ์ฌ์ฉ์ ์ ์ ๊ฐ์ ๊บผ๋ด์ ์ฒ๋ฆฌํ๋ค.
๊ทธ๋ฌ๋ ์ ์ฝ๋๊ฐ ์ด์์ ์ผ๋ก ๋ณด์ด์ง๋ง ํ์ค์ ๋ค๋ฅด๋ค. ์์ผ์ ์ก์ ๋ฒํผ๊ฐ ๋น ๊ณต๊ฐ์ด ์๋ ์๊ฐ์ ์ ์งํ๋ ์๊ฐ์ด ์๋์ ์ผ๋ก ์งง์์ ๊ฑฐ์ ๋๋ถ๋ถ์ ์ก์ ๊ฐ๋ฅ ์ํ์ด๋ค. ์ด๋ฌํ ํน์ง ๋๋ฌธ์ ํ์ ์ด์์ ๋ฃจํ๋ฅผ ๋ ๋๊ฒ ๋๋ค. ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์๋ ๋ ๋ฒจ(level) ํธ๋ฆฌ๊ฑฐ ๋์ ์์ง(edge) ํธ๋ฆฌ๊ฑฐ๋ฅผ ์จ์ผ ํ๋ค.
- ๋ ๋ฒจ ํธ๋ฆฌ๊ฑฐ๋ '์์ผ์ด I/O ๊ฐ๋ฅํ๋ค'๋ฅผ ์๋ฏธํ๊ณ ์์ง ํธ๋ฆฌ๊ฑฐ๋ ์์ผ์ด I/O ๊ฐ๋ฅ์ด ์๋์๋๋ฐ, ์ด์ ๊ฐ๋ฅ์ด ๋์๋ค'๋ฅผ ์๋ฏธํ๋ค. ๋ฐ๋ผ์ ๊ฐ๋ฅ์ผ๋ก ๋ณํ๋ ์๊ฐ์๋ง ๊บผ๋ด์ง๋ค.
- ์์ง ํธ๋ฆฌ๊ฑฐ๋ฅผ ์ธ ๋๋ ์์ผ์ ๋ ผ๋ธ๋ก์ผ๋ก ๋ฏธ๋ฆฌ ์ค์ ๋์ด ์์ด์ผ ํ๋ฉฐ I/O ํธ์ถ์ ํ ๋ฒ๋ง ํ์ง ๋ง๊ณ would block์ด ๋ฐ์ํ ๋๊น์ง ๋ฐ๋ณตํด์ผ ํ๋ค.
foreach(event in events)
{
s = event.socket;
// ์ epoll.add์ ๋ค์ด๊ฐ๋ ๊ฐ์ ์ป๋๋ค.
userPtr = event.userPtr;
// ์์ ? ์ก์ ?
type = event.type;
if (type = = ReceiveEvent)
{
while (true)
{
(result, data) = s.recv();
if (data.length > 0)
{
// ์์ ๋ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ค.
Process(userPtr, s, data);
}
if (result == EWOULDBLOCK)
break;
}
}
}
9. IOCP
IOCP๋ ์์ผ์ Overlapped I/O๊ฐ ์๋ฃ๋๋ฉด ์ด๋ฅผ ๊ฐ์งํด์ ์ฌ์ฉ์์๊ฒ ์๋ ค์ฃผ๋ ์ญํ ์ ํ๋ค.
- ์์ผ 2๊ฐ ์๋ฃ๋๋ ์๊ฐ IOCP๋ ์ด ์ํฉ์ ๋ด์ฅ ํ์ push ํ๋ค.
- ์ฌ์ฉ์๋ IOCP์์ I/O๊ฐ ์๋ฃ๋์์์ ์๋ ค์ฃผ๋ ์๋ฃ ์ ํธ๋ฅผ pop ํ ์ ์๋ค.
๋ฐ๋ผ์ ์์ผ์ด 1๋ง ๊ฐ๋ผ๊ณ ํด๋ ์ด ์ค์์ I/O ๊ฐ๋ฅ์ด ๋ ๊ฒ๋ค๋ง ์ป์ ์ ์๋ค.
์์ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
epoll๊ณผ ๊ฑฐ์ ์ ์งํ์ง๋ง ์ฐจ์ด์ ์ด ์๋ค๋ฉด, epoll์ I/O ๊ฐ๋ฅ์ธ ๊ฒ์ ์๋ ค ์ฃผ์ง๋ง IOCP๋ I/O ์๋ฃ์ธ ๊ฒ์ ์๋ ค์ค๋ค.
iocp = new iocp();
foreach(s in sockets)
{
iocp.add(s, GetUserPtr(s));
s.OverlappedReceive(data[s],
receiveOverlapped[s]);
}
events = iocp.wait(100ms);
foreach(event in events)
{
// ์ iocp.add์ ๋ค์ด๊ฐ๋ ๊ฐ์ ์ป๋๋ค.
userPtr = event.userPtr;
ov = event.overlappedPtr;
s = GetSocketFromUserPtr(userPtr);
if (ov = = receiveOverlapped[s])
{
// overlapped receive๊ฐ ์ฑ๊ณตํ์ผ๋,
// ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ค.
Process(s, userPtr, data[s]);
s.OverlappedReceive(data[s],
receiveOverlapped[s]);
}
}
IOCP๋ ์ค๋์ ์ ๋์จ API์ด๊ธฐ ๋๋ฌธ์ epoll๋ณด๋ค ๊ตฌํ์ด ๋ณต์กํ ๊ธฐ๋ฅ์ด ์์ง๋ง ๋ฐ๋๋ก ์ฑ๋ฅ์ ์ ๋ฆฌํ ๊ธฐ๋ฅ๋ ์๋ค.
๋ฐ๋ก ์ค๋ ๋ ํ๋ง์ ์ฝ๊ฒ ๊ตฌํํ ์ ์๋ค๋ ์ ์ด๋ค.
์ด๋ค ์์ผ์ ๋ํด Overlapped I/O๋ฅผ ํ์ง ์๋ ์ด์ ๊ทธ ์์ผ์ ๋ํ ์๋ฃ ์ ํธ๋ ์ ํ ๋ฐ์ํ์ง ์๋๋ค. ์ฆ, ์์ผ ํ๋์ ๋ํ ์๋ฃ ์ ํธ๋ฅผ ์ค๋ ๋ ํ๋๋ง ์ฒ๋ฆฌํ ์ ์๊ฒ ๋ณด์ฅ๋๋ค. ์ด๋ฌํ ํน์ง ๋๋ถ์ IOCP ํ๋๋ฅผ ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๊ธฐ๋ค๋ฆฌ๋๋ก ๊ตฌํํ๊ธฐ๊ฐ ์ฝ๊ณ ๋ง์ ์์ผ์ ๋ํ I/O ์ฒ๋ฆฌ๋ฅผ ๋์๋ค๋ฐ์ ์ผ๋ก ์ํํ ๋ ์ฌ๋ฌ ์ค๋ ๋๊ฐ ์๋ฃ ์ ํธ ์ฒ๋ฆฌ๋ฅผ ๊ณจ๊ณ ๋ฃจ ๋๋์ด์ ์ฒ๋ฆฌํ ์ ์๋ค. ์ด๋ ๋๊ท๋ชจ ๋์์ ์์ ๊ฒ์ ์๋ฒ๋ฅผ ๋ง๋ค๊ณ ์ ๋ฉํฐ์ฝ์ด๋ฅผ ๋ชจ๋ ํ์ฉํด์ผ ํ ๋ ์ ๋ฆฌํ๋ค.