์๊ฐ
์ด ๊ธฐ์ฌ์์๋ I/O ๋ฆฌ์กํฐ์ ๋ชจ๋ ๋ฉด๊ณผ ์๋ ๋ฐฉ์์ ์ดํด๋ณด๊ณ , 200์ค ๋ฏธ๋ง์ ์ฝ๋๋ก ๊ตฌํ์ ์์ฑํ๊ณ , ๋ถ๋น 40์ฒ๋ง ๊ฐ๊ฐ ๋๋ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๊ฐ๋จํ HTTP ์๋ฒ ํ๋ก์ธ์ค๋ฅผ ๋ง๋ค ๊ฒ์ ๋๋ค.
๋จธ๋ฆฌ๋ง
- ์ด ๊ธฐ์ฌ๋ I/O ๋ฆฌ์กํฐ์ ๊ธฐ๋ฅ์ ์ดํดํ๊ณ ์ด๋ฅผ ์ฌ์ฉํ ๋์ ์ํ์ ์ดํดํ๋ ๋ฐ ๋์์ ์ฃผ๊ธฐ ์ํด ์์ฑ๋์์ต๋๋ค.
- ๊ธฐ์ฌ๋ฅผ ์ดํดํ๋ ค๋ฉด ๊ธฐ๋ณธ ์ง์์ด ํ์ํฉ๋๋ค.
C ์ธ์ด ๊ทธ๋ฆฌ๊ณ ๋คํธ์ํฌ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ ๊ฒฝํ์ด ์์ต๋๋ค. - ๋ชจ๋ ์ฝ๋๋ (์ฃผ์: ๊ธด PDF)
C11 ํ์ค์ Linux์ฉ์ด๋ฉฐ ๋ค์์์ ์ฌ์ฉ ๊ฐ๋ฅGitHub์ .
์ํฉ๋๊น?
์ธํฐ๋ท์ ์ธ๊ธฐ๊ฐ ๋์์ง์ ๋ฐ๋ผ ์น ์๋ฒ๋ ๋ง์ ์์ ์ฐ๊ฒฐ์ ๋์์ ์ฒ๋ฆฌํด์ผ ํ๋ฏ๋ก ๋ ๊ฐ์ง ์ ๊ทผ ๋ฐฉ์์ด ์๋๋์์ต๋๋ค. ์ฆ, ๋ค์์ OS ์ค๋ ๋์์ I/O๋ฅผ ์ฐจ๋จํ๋ ๋ฐฉ๋ฒ๊ณผ "์์คํ
์ ํ๊ธฐ"๋ผ๊ณ ๋ ํ๋ ์ด๋ฒคํธ ์๋ฆผ ์์คํ
(
์ฒซ ๋ฒ์งธ ์ ๊ทผ ๋ฐฉ์์ ๋ค์ด์ค๋ ์ฐ๊ฒฐ๋ง๋ค ์๋ก์ด OS ์ค๋ ๋๋ฅผ ์์ฑํ๋ ๊ฒ์ด์์ต๋๋ค. ๋จ์ ์ ํ์ฅ์ฑ์ด ์ข์ง ์๋ค๋ ๊ฒ์
๋๋ค. ์ด์ ์ฒด์ ๋ ๋ง์ ๊ฒ์ ๊ตฌํํด์ผ ํฉ๋๋ค.
์์ ๋ ๋ฒ์ ์ด ๊ฐ์กฐ ํ์๋ฉ๋๋ค.
๋ ๋ฒ์งธ ์ ๊ทผ ๋ฐฉ์์
์ด๋ฌํ ์ ๊ทผ ๋ฐฉ์์ ์ฐจ์ด์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- I/O ์์
์ฐจ๋จ ์ ์ํ๋ค ์ฌ์ฉ์ ํ๋ฆ ๊ทธ๋๊น์งOS๊ฐ ์ ๋๋ก ์๋ํ ๋๊น์ง
์กฐ๊ฐ ๋ชจ์ ๋ค์ด์ค๋IP ํจํท ๋ฐ์ดํธ ์คํธ๋ฆผ์ผ๋ก(TCP , ๋ฐ์ดํฐ ์์ ) ๋๋ ๋ค์์ ํตํ ํ์ ์ ์ก์ ์ํด ๋ด๋ถ ์ฐ๊ธฐ ๋ฒํผ์ ์ฌ์ฉ ๊ฐ๋ฅํ ๊ณต๊ฐ์ด ์ถฉ๋ถํ์ง ์์ต๋๋ค.NIC (๋ฐ์ดํฐ ์ ์ก). - ์์คํ ์ ํ๊ธฐ ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ OS๊ฐ ํ๋ก๊ทธ๋จ์ ์๋ฆฝ๋๋ค. ์ด๋ฏธ ์กฐ๊ฐ ๋ชจ์๋ IP ํจํท(TCP, ๋ฐ์ดํฐ ์์ ) ๋๋ ๋ด๋ถ ์ฐ๊ธฐ ๋ฒํผ์ ์ถฉ๋ถํ ๊ณต๊ฐ ์ด๋ฏธ ๊ฐ๋ฅํฉ๋๋ค(๋ฐ์ดํฐ ์ ์ก).
์์ฝํ์๋ฉด, ๊ฐ I/O์ ๋ํด OS ์ค๋ ๋๋ฅผ ์์ฝํ๋ ๊ฒ์ ์ปดํจํ
์ฑ๋ฅ์ ๋ญ๋นํ๋ ๊ฒ์
๋๋ค. ์ค์ ๋ก ์ค๋ ๋๋ ์ ์ฉํ ์์
์ ์ํํ์ง ์๊ธฐ ๋๋ฌธ์
๋๋ค.
I/O ๋ฆฌ์กํฐ ๋ชจ๋ธ
I/O ๋ฆฌ์กํฐ๋ ์์คํ ์ ํ๊ธฐ์ ์ฌ์ฉ์ ์ฝ๋ ์ฌ์ด์ ๊ณ์ธต ์ญํ ์ ํฉ๋๋ค. ์๋ ์๋ฆฌ๋ ๋ค์ ๋ธ๋ก ๋ค์ด์ด๊ทธ๋จ์ผ๋ก ์ค๋ช ๋ฉ๋๋ค.
- ์ด๋ฒคํธ๋ ํน์ ์์ผ์ด ๋น์ฐจ๋จ I/O ์์ ์ ์ํํ ์ ์๋ค๋ ์๋ฆผ์ด๋ผ๋ ์ ์ ์๊ธฐ์์ผ ๋๋ฆฌ๊ฒ ์ต๋๋ค.
- ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ์ด๋ฒคํธ๊ฐ ์์ ๋ ๋ I/O ๋ฆฌ์กํฐ์ ์ํด ํธ์ถ๋์ด ๋น์ฐจ๋จ I/O ์์ ์ ์ํํ๋ ํจ์์ ๋๋ค.
I/O ๋ฆฌ์กํฐ๋ ์ ์์ ๋จ์ผ ์ค๋ ๋์ด์ง๋ง ์ด ๊ฐ๋ ์ด 1 ์ค๋ ๋: 1 ๋ฆฌ์กํฐ ๋น์จ๋ก ๋ฉํฐ ์ค๋ ๋ ํ๊ฒฝ์์ ์ฌ์ฉ๋์ด ๋ชจ๋ CPU ์ฝ์ด๋ฅผ ์ฌํ์ฉํ๋ ๊ฒ์ ๋ง์ ๋ฐฉ๋ฒ์ด ์๋ค๋ ์ ์ ์ ์ํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
ะ ะตะฐะปะธะทะฐัะธั
๊ณต๊ฐ ์ธํฐํ์ด์ค๋ฅผ ํ์ผ์ ๋ฐฐ์นํ๊ฒ ์ต๋๋ค. reactor.h
reactor.c
reactor.h
๋ค์ ๊ณต์ง์ฌํญ์ผ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
Reactor.h์ ์ ์ธ ํ์
typedef struct reactor Reactor;
/*
* ะฃะบะฐะทะฐัะตะปั ะฝะฐ ััะฝะบัะธั, ะบะพัะพัะฐั ะฑัะดะตั ะฒัะทัะฒะฐัััั I/O ัะตะฐะบัะพัะพะผ ะฟัะธ ะฟะพัััะฟะปะตะฝะธะธ
* ัะพะฑััะธั ะพั ัะธััะตะผะฝะพะณะพ ัะตะปะตะบัะพัะฐ.
*/
typedef void (*Callback)(void *arg, int fd, uint32_t events);
/*
* ะะพะทะฒัะฐัะฐะตั `NULL` ะฒ ัะปััะฐะต ะพัะธะฑะบะธ, ะฝะต-`NULL` ัะบะฐะทะฐัะตะปั ะฝะฐ `Reactor` ะฒ
* ะฟัะพัะธะฒะฝะพะผ ัะปััะฐะต.
*/
Reactor *reactor_new(void);
/*
* ะัะฒะพะฑะพะถะดะฐะตั ัะธััะตะผะฝัะน ัะตะปะตะบัะพั, ะฒัะต ะทะฐัะตะณะธัััะธัะพะฒะฐะฝะฝัะต ัะพะบะตัั ะฒ ะดะฐะฝะฝัะน ะผะพะผะตะฝั
* ะฒัะตะผะตะฝะธ ะธ ัะฐะผ I/O ัะตะฐะบัะพั.
*
* ะกะปะตะดัััะธะต ััะฝะบัะธะธ ะฒะพะทะฒัะฐัะฐัั -1 ะฒ ัะปััะฐะต ะพัะธะฑะบะธ, 0 ะฒ ัะปััะฐะต ััะฟะตั
ะฐ.
*/
int reactor_destroy(Reactor *reactor);
int reactor_register(const Reactor *reactor, int fd, uint32_t interest,
Callback callback, void *callback_arg);
int reactor_deregister(const Reactor *reactor, int fd);
int reactor_reregister(const Reactor *reactor, int fd, uint32_t interest,
Callback callback, void *callback_arg);
/*
* ะะฐะฟััะบะฐะตั ัะธะบะป ัะพะฑััะธะน ั ัะฐะนะผ-ะฐััะพะผ `timeout`.
*
* ะญัะฐ ััะฝะบัะธั ะฟะตัะตะดะฐัั ัะฟัะฐะฒะปะตะฝะธะต ะฒัะทัะฒะฐััะตะผั ะบะพะดั ะตัะปะธ ะพัะฒะตะดัะฝะฝะพะต ะฒัะตะผั ะฒััะปะพ
* ะธะปะธ/ะธ ะฟัะธ ะพััััััะฒะธะธ ะทะฐัะตะณะธัััะธัะพะฒะฐะฝะฝัั
ัะพะบะตัะพะฒ.
*/
int reactor_run(const Reactor *reactor, time_t timeout);
I/O ๋ฆฌ์กํฐ ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌ์ฑ๋ฉ๋๋ค. GHashTable
CallbackData
(์ด๋ฒคํธ ํธ๋ค๋ฌ์ ๊ตฌ์กฐ ๋ฐ ์ด์ ๋ํ ์ฌ์ฉ์ ์ธ์)
Reactor ๋ฐ CallbackData ํ์
struct reactor {
int epoll_fd;
GHashTable *table; // (int, CallbackData)
};
typedef struct {
Callback callback;
void *arg;
} CallbackData;
์ฒ๋ฆฌํ ์ ์๋ ๊ธฐ๋ฅ์ ํ์ฑํํ์ต๋๋ค. reactor.h
์ฐ๋ฆฌ๋ ๊ตฌ์กฐ๋ฅผ ์ ์ธ reactor
, ๋ฐ c reactor.c
์ด๋ฅผ ์ ์ํ์ฌ ์ฌ์ฉ์๊ฐ ํด๋น ํ๋๋ฅผ ๋ช
์์ ์ผ๋ก ๋ณ๊ฒฝํ๋ ๊ฒ์ ๋ฐฉ์งํฉ๋๋ค. ํจํด์ค ํ๋์์
๊ธฐ๋ฅ reactor_register
, reactor_deregister
ะธ reactor_reregister
์์คํ
์ ํ๊ธฐ์ ํด์ ํ
์ด๋ธ์์ ๊ด์ฌ ์๋ ์์ผ ๋ชฉ๋ก๊ณผ ํด๋น ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์
๋ฐ์ดํธํฉ๋๋ค.
๋ฑ๋ก ๊ธฐ๋ฅ ํ์
#define REACTOR_CTL(reactor, op, fd, interest)
if (epoll_ctl(reactor->epoll_fd, op, fd,
&(struct epoll_event){.events = interest,
.data = {.fd = fd}}) == -1) {
perror("epoll_ctl");
return -1;
}
int reactor_register(const Reactor *reactor, int fd, uint32_t interest,
Callback callback, void *callback_arg) {
REACTOR_CTL(reactor, EPOLL_CTL_ADD, fd, interest)
g_hash_table_insert(reactor->table, int_in_heap(fd),
callback_data_new(callback, callback_arg));
return 0;
}
int reactor_deregister(const Reactor *reactor, int fd) {
REACTOR_CTL(reactor, EPOLL_CTL_DEL, fd, 0)
g_hash_table_remove(reactor->table, &fd);
return 0;
}
int reactor_reregister(const Reactor *reactor, int fd, uint32_t interest,
Callback callback, void *callback_arg) {
REACTOR_CTL(reactor, EPOLL_CTL_MOD, fd, interest)
g_hash_table_insert(reactor->table, int_in_heap(fd),
callback_data_new(callback, callback_arg));
return 0;
}
I/O ๋ฆฌ์กํฐ๊ฐ ์ค๋ช
์๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฒคํธ๋ฅผ ์ฐจ๋จํ ํ fd
, ํด๋น ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ํธ์ถํ๊ณ ์ด๋ฅผ ์ ๋ฌํฉ๋๋ค. fd
, void
.
Reactor_run() ํจ์ ํ์
int reactor_run(const Reactor *reactor, time_t timeout) {
int result;
struct epoll_event *events;
if ((events = calloc(MAX_EVENTS, sizeof(*events))) == NULL)
abort();
time_t start = time(NULL);
while (true) {
time_t passed = time(NULL) - start;
int nfds =
epoll_wait(reactor->epoll_fd, events, MAX_EVENTS, timeout - passed);
switch (nfds) {
// ะัะธะฑะบะฐ
case -1:
perror("epoll_wait");
result = -1;
goto cleanup;
// ะัะตะผั ะฒััะปะพ
case 0:
result = 0;
goto cleanup;
// ะฃัะฟะตัะฝะฐั ะพะฟะตัะฐัะธั
default:
// ะัะทะฒะฐัั ะพะฑัะฐะฑะพััะธะบะพะฒ ัะพะฑััะธะน
for (int i = 0; i < nfds; i++) {
int fd = events[i].data.fd;
CallbackData *callback =
g_hash_table_lookup(reactor->table, &fd);
callback->callback(callback->arg, fd, events[i].events);
}
}
}
cleanup:
free(events);
return result;
}
์์ฝํ์๋ฉด, ์ฌ์ฉ์ ์ฝ๋์ ํจ์ ํธ์ถ ์ฒด์ธ์ ๋ค์๊ณผ ๊ฐ์ ํ์์ ์ทจํฉ๋๋ค.
๋จ์ผ ์ค๋ ๋ ์๋ฒ
๋์ ๋ถํ์์ I/O ๋ฆฌ์กํฐ๋ฅผ ํ ์คํธํ๊ธฐ ์ํด ์ด๋ฏธ์ง๋ก ๋ชจ๋ ์์ฒญ์ โโ์๋ตํ๋ ๊ฐ๋จํ HTTP ์น ์๋ฒ๋ฅผ ์์ฑํ๊ฒ ์ต๋๋ค.
HTTP ํ๋กํ ์ฝ์ ๋ํ ๋น ๋ฅธ ์ฐธ์กฐ
HTTP๋ฅผ ํตํด ์ฝ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์์ฒญ ํ์
<ะะะะะะะ> <URI> <ะะะ ะกะะฏ HTTP>CRLF
<ะะะะะะะะะ 1>CRLF
<ะะะะะะะะะ 2>CRLF
<ะะะะะะะะะ N>CRLF CRLF
<ะะะะะซะ>
CRLF
๋ ๋ฌธ์์ ์ํ์ค์ ๋๋ค.r
ะธn
, ์์ฒญ์ ์ฒซ ๋ฒ์งธ ์ค, ํค๋ ๋ฐ ๋ฐ์ดํฐ๋ฅผ ๊ตฌ๋ถํฉ๋๋ค.<ะะะะะะะ>
- ์ค ํ๋CONNECT
,DELETE
,GET
,HEAD
,OPTIONS
,PATCH
,POST
,PUT
,TRACE
. ๋ธ๋ผ์ฐ์ ๊ฐ ์๋ฒ์ ๋ช ๋ น์ ๋ณด๋ ๋๋ค.GET
, "ํ์ผ ๋ด์ฉ์ ๋ณด๋ด์ฃผ์ธ์."๋ฅผ ์๋ฏธํฉ๋๋ค.<URI>
-๊ท ์ผ ์์ ์๋ณ์ . ์๋ฅผ ๋ค์ด URI =์ธ ๊ฒฝ์ฐ/index.html
, ํด๋ผ์ด์ธํธ๋ ์ฌ์ดํธ์ ๋ฉ์ธ ํ์ด์ง๋ฅผ ์์ฒญํฉ๋๋ค.<ะะะ ะกะะฏ HTTP>
โ ํ์์ HTTP ํ๋กํ ์ฝ ๋ฒ์ HTTP/X.Y
. ์ค๋๋ ๊ฐ์ฅ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๋ ๋ฒ์ ์HTTP/1.1
.<ะะะะะะะะะ N>
ํ์์ ํค-๊ฐ ์์ ๋๋ค.<ะะะฎะง>: <ะะะะงะะะะ>
, ์ถ๊ฐ ๋ถ์์ ์ํด ์๋ฒ๋ก ์ ์ก๋ฉ๋๋ค.<ะะะะะซะ>
โ ์์ ์ ์ํํ๊ธฐ ์ํด ์๋ฒ์ ํ์ํ ๋ฐ์ดํฐ์ ๋๋ค. ๊ฐ๋จํ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋คJSON ๋๋ ๋ค๋ฅธ ํ์.
์๋ต ํ์
<ะะะ ะกะะฏ HTTP> <ะะะ ะกะขะะขะฃะกะ> <ะะะะกะะะะ ะกะขะะขะฃะกะ>CRLF
<ะะะะะะะะะ 1>CRLF
<ะะะะะะะะะ 2>CRLF
<ะะะะะะะะะ N>CRLF CRLF
<ะะะะะซะ>
<ะะะ ะกะขะะขะฃะกะ>
์ฐ์ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ํ๋ด๋ ์ซ์์ ๋๋ค. ์ฐ๋ฆฌ ์๋ฒ๋ ํญ์ ์ํ 200(์ฑ๊ณต์ ์ธ ์์ )์ ๋ฐํํฉ๋๋ค.<ะะะะกะะะะ ะกะขะะขะฃะกะ>
โ ์ํ ์ฝ๋์ ๋ฌธ์์ด ํํ์ ๋๋ค. ์ํ ์ฝ๋ 200์ ๊ฒฝ์ฐ ์ด๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.OK
.<ะะะะะะะะะ N>
โ ์์ฒญ๊ณผ ๋์ผํ ํ์์ ํค๋์ ๋๋ค. ์ฐ๋ฆฌ๋ ์ ๋ชฉ์ ๋ฐํํฉ๋๋คContent-Length
(ํ์ผ ํฌ๊ธฐ) ๋ฐContent-Type: text/html
(๋ฐํ ๋ฐ์ดํฐ ์ ํ).<ะะะะะซะ>
โ ์ฌ์ฉ์๊ฐ ์์ฒญํ ๋ฐ์ดํฐ. ์ฐ๋ฆฌ์ ๊ฒฝ์ฐ ์ด๊ฒ์ ์ด๋ฏธ์ง์ ๊ฒฝ๋ก์ ๋๋ค.HTML .
ํ์ผ http_server.c
common.h
common.h์ ํจ์ ํ๋กํ ํ์ ํ์
/*
* ะะฑัะฐะฑะพััะธะบ ัะพะฑััะธะน, ะบะพัะพััะน ะฒัะทะพะฒะตััั ะฟะพัะปะต ัะพะณะพ, ะบะฐะบ ัะพะบะตั ะฑัะดะตั
* ะณะพัะพะฒ ะฟัะธะฝััั ะฝะพะฒะพะต ัะพะตะดะธะฝะตะฝะธะต.
*/
static void on_accept(void *arg, int fd, uint32_t events);
/*
* ะะฑัะฐะฑะพััะธะบ ัะพะฑััะธะน, ะบะพัะพััะน ะฒัะทะพะฒะตััั ะฟะพัะปะต ัะพะณะพ, ะบะฐะบ ัะพะบะตั ะฑัะดะตั
* ะณะพัะพะฒ ะพัะฟัะฐะฒะธัั HTTP ะพัะฒะตั.
*/
static void on_send(void *arg, int fd, uint32_t events);
/*
* ะะฑัะฐะฑะพััะธะบ ัะพะฑััะธะน, ะบะพัะพััะน ะฒัะทะพะฒะตััั ะฟะพัะปะต ัะพะณะพ, ะบะฐะบ ัะพะบะตั ะฑัะดะตั
* ะณะพัะพะฒ ะฟัะธะฝััั ัะฐััั HTTP ะทะฐะฟัะพัะฐ.
*/
static void on_recv(void *arg, int fd, uint32_t events);
/*
* ะะตัะตะฒะพะดะธั ะฒั
ะพะดััะตะต ัะพะตะดะธะฝะตะฝะธะต ะฒ ะฝะตะฑะปะพะบะธััััะธะน ัะตะถะธะผ.
*/
static void set_nonblocking(int fd);
/*
* ะะตัะฐัะฐะตั ะฟะตัะตะดะฐะฝะฝัะต ะฐัะณัะผะตะฝัั ะฒ stderr ะธ ะฒัั
ะพะดะธั ะธะท ะฟัะพัะตััะฐ ั
* ะบะพะดะพะผ `EXIT_FAILURE`.
*/
static noreturn void fail(const char *format, ...);
/*
* ะะพะทะฒัะฐัะฐะตั ัะฐะนะปะพะฒัะน ะดะตัะบัะธะฟัะพั ัะพะบะตัะฐ, ัะฟะพัะพะฑะฝะพะณะพ ะฟัะธะฝะธะผะฐัั ะฝะพะฒัะต
* TCP ัะพะตะดะธะฝะตะฝะธั.
*/
static int new_server(bool reuse_port);
๊ธฐ๋ฅ ๋งคํฌ๋ก๋ ์ค๋ช
๋์ด ์์ต๋๋ค. SAFE_CALL()
๊ทธ๋ฆฌ๊ณ ํจ์๊ฐ ์ ์๋์์ต๋๋ค fail()
. ๋งคํฌ๋ก๋ ํํ์์ ๊ฐ์ ์ค๋ฅ์ ๋น๊ตํ๊ณ ์กฐ๊ฑด์ด true์ธ ๊ฒฝ์ฐ ํจ์๋ฅผ ํธ์ถํฉ๋๋ค. fail()
:
#define SAFE_CALL(call, error)
do {
if ((call) == error) {
fail("%s", #call);
}
} while (false)
๊ธฐ๋ฅ fail()
์ ๋ฌ๋ ์ธ์๋ฅผ ํฐ๋ฏธ๋์ ์ธ์ํฉ๋๋ค(์: printf()
EXIT_FAILURE
:
static noreturn void fail(const char *format, ...) {
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
fprintf(stderr, ": %sn", strerror(errno));
exit(EXIT_FAILURE);
}
๊ธฐ๋ฅ new_server()
์์คํ
ํธ์ถ์ ์ํด ์์ฑ๋ "์๋ฒ" ์์ผ์ ํ์ผ ์ค๋ช
์๋ฅผ ๋ฐํํฉ๋๋ค. socket()
bind()
listen()
new_server() ํจ์ ํ์
static int new_server(bool reuse_port) {
int fd;
SAFE_CALL((fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)),
-1);
if (reuse_port) {
SAFE_CALL(
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)),
-1);
}
struct sockaddr_in addr = {.sin_family = AF_INET,
.sin_port = htons(SERVER_PORT),
.sin_addr = {.s_addr = inet_addr(SERVER_IPV4)},
.sin_zero = {0}};
SAFE_CALL(bind(fd, (struct sockaddr *)&addr, sizeof(addr)), -1);
SAFE_CALL(listen(fd, SERVER_BACKLOG), -1);
return fd;
}
- ์์ผ์ ์ฒ์์ ํ๋๊ทธ๋ฅผ ์ฌ์ฉํ์ฌ ๋น์ฐจ๋จ ๋ชจ๋๋ก ์์ฑ๋ฉ๋๋ค.
SOCK_NONBLOCK
๊ทธ๋์ ํจ์์์on_accept()
(์์ธํ ๋ณด๊ธฐ) ์์คํ ํธ์ถaccept()
์ค๋ ๋ ์คํ์ ์ค์งํ์ง ์์์ต๋๋ค. - ๋ฉด
reuse_port
๊ฐ์true
, ์ด ํจ์๋ ์ต์ ์ ์ฌ์ฉํ์ฌ ์์ผ์ ๊ตฌ์ฑํฉ๋๋ค. ๋ฅผ ํตํดSO_REUSEPORT
๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์ ๋์ผํ ํฌํธ๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด("๋ฉํฐ์ค๋ ๋ ์๋ฒ" ์น์ ์ฐธ์กฐ)setsockopt()
์ด๋ฒคํธ ํธ๋ค๋ฌ on_accept()
OS๊ฐ ์ด๋ฒคํธ๋ฅผ ์์ฑํ ํ์ ํธ์ถ๋ฉ๋๋ค. EPOLLIN
, ์ด ๊ฒฝ์ฐ ์ ์ฐ๊ฒฐ์ด ํ์ฉ๋ ์ ์์์ ์๋ฏธํฉ๋๋ค. on_accept()
์๋ก์ด ์ฐ๊ฒฐ์ ์๋ฝํ๊ณ ์ด๋ฅผ ๋น์ฐจ๋จ ๋ชจ๋๋ก ์ ํํ ํ ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ๋ฑ๋กํฉ๋๋ค. on_recv()
I/O ๋ฆฌ์กํฐ์์.
on_accept() ํจ์ ํ์
static void on_accept(void *arg, int fd, uint32_t events) {
int incoming_conn;
SAFE_CALL((incoming_conn = accept(fd, NULL, NULL)), -1);
set_nonblocking(incoming_conn);
SAFE_CALL(reactor_register(reactor, incoming_conn, EPOLLIN, on_recv,
request_buffer_new()),
-1);
}
์ด๋ฒคํธ ํธ๋ค๋ฌ on_recv()
OS๊ฐ ์ด๋ฒคํธ๋ฅผ ์์ฑํ ํ์ ํธ์ถ๋ฉ๋๋ค. EPOLLIN
, ์ด ๊ฒฝ์ฐ ์ฐ๊ฒฐ์ด ๋ฑ๋ก๋์์์ ์๋ฏธํฉ๋๋ค. on_accept()
, ๋ฐ์ดํฐ๋ฅผ ์์ ํ ์ค๋น๊ฐ ๋์์ต๋๋ค.
on_recv()
HTTP ์์ฒญ์ด ์์ ํ ์์ ๋ ๋๊น์ง ์ฐ๊ฒฐ์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ๋ค์ ํธ๋ค๋ฌ๋ฅผ ๋ฑ๋กํฉ๋๋ค. on_send()
HTTP ์๋ต์ ๋ณด๋
๋๋ค. ํด๋ผ์ด์ธํธ๊ฐ ์ฐ๊ฒฐ์ ๋์ผ๋ฉด ์์ผ์ ๋ฑ๋ก ์ทจ์๋๊ณ ๋ค์์ ์ฌ์ฉํ์ฌ ๋ซํ๋๋ค. close()
on_recv() ํจ์ ํ์
static void on_recv(void *arg, int fd, uint32_t events) {
RequestBuffer *buffer = arg;
// ะัะธะฝะธะผะฐะตะผ ะฒั
ะพะดะฝัะต ะดะฐะฝะฝัะต ะดะพ ัะตั
ะฟะพั, ััะพ recv ะฒะพะทะฒัะฐัะธั 0 ะธะปะธ ะพัะธะฑะบั
ssize_t nread;
while ((nread = recv(fd, buffer->data + buffer->size,
REQUEST_BUFFER_CAPACITY - buffer->size, 0)) > 0)
buffer->size += nread;
// ะะปะธะตะฝั ะพะฑะพัะฒะฐะป ัะพะตะดะธะฝะตะฝะธะต
if (nread == 0) {
SAFE_CALL(reactor_deregister(reactor, fd), -1);
SAFE_CALL(close(fd), -1);
request_buffer_destroy(buffer);
return;
}
// read ะฒะตัะฝัะป ะพัะธะฑะบั, ะพัะปะธัะฝัั ะพั ะพัะธะฑะบะธ, ะฟัะธ ะบะพัะพัะพะน ะฒัะทะพะฒ ะทะฐะฑะปะพะบะธััะตั
// ะฟะพัะพะบ
if (errno != EAGAIN && errno != EWOULDBLOCK) {
request_buffer_destroy(buffer);
fail("read");
}
// ะะพะปััะตะฝ ะฟะพะปะฝัะน HTTP ะทะฐะฟัะพั ะพั ะบะปะธะตะฝัะฐ. ะขะตะฟะตัั ัะตะณะธัััะธััะตะผ ะพะฑัะฐะฑะพััะธะบะฐ
// ัะพะฑััะธะน ะดะปั ะพัะฟัะฐะฒะบะธ ะดะฐะฝะฝัั
if (request_buffer_is_complete(buffer)) {
request_buffer_clear(buffer);
SAFE_CALL(reactor_reregister(reactor, fd, EPOLLOUT, on_send, buffer),
-1);
}
}
์ด๋ฒคํธ ํธ๋ค๋ฌ on_send()
OS๊ฐ ์ด๋ฒคํธ๋ฅผ ์์ฑํ ํ์ ํธ์ถ๋ฉ๋๋ค. EPOLLOUT
, ์ด๋ ์ฐ๊ฒฐ์ด ๋ฑ๋ก๋์์์ ์๋ฏธํฉ๋๋ค. on_recv()
, ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ผ ์ค๋น๊ฐ ๋์์ต๋๋ค. ์ด ํจ์๋ ์ด๋ฏธ์ง์ ํจ๊ป HTML์ด ํฌํจ๋ HTTP ์๋ต์ ํด๋ผ์ด์ธํธ์ ๋ณด๋ธ ๋ค์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๋ค์ ๋ค์์ผ๋ก ๋ณ๊ฒฝํฉ๋๋ค. on_recv()
.
on_send() ํจ์ ํ์
static void on_send(void *arg, int fd, uint32_t events) {
const char *content = "<img "
"src="https://habrastorage.org/webt/oh/wl/23/"
"ohwl23va3b-dioerobq_mbx4xaw.jpeg">";
char response[1024];
sprintf(response,
"HTTP/1.1 200 OK" CRLF "Content-Length: %zd" CRLF "Content-Type: "
"text/html" DOUBLE_CRLF "%s",
strlen(content), content);
SAFE_CALL(send(fd, response, strlen(response), 0), -1);
SAFE_CALL(reactor_reregister(reactor, fd, EPOLLIN, on_recv, arg), -1);
}
๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง์ผ๋ก ํ์ผ์์ http_server.c
, ๊ธฐ๋ฅ ์ค main()
์ฐ๋ฆฌ๋ ๋ค์์ ์ฌ์ฉํ์ฌ I/O ๋ฆฌ์กํฐ๋ฅผ ๋ง๋ญ๋๋ค. reactor_new()
, ์๋ฒ ์์ผ์ ์์ฑํ๊ณ ๋ฑ๋กํ๊ณ ๋ค์์ ์ฌ์ฉํ์ฌ ๋ฆฌ์กํฐ๋ฅผ ์์ํ์ญ์์ค. reactor_run()
์ ํํ XNUMX๋ถ ๋์ ๋ฆฌ์์ค๋ฅผ ํด์ ํ๊ณ ํ๋ก๊ทธ๋จ์ ์ข
๋ฃํฉ๋๋ค.
http_server.c ํ์
#include "reactor.h"
static Reactor *reactor;
#include "common.h"
int main(void) {
SAFE_CALL((reactor = reactor_new()), NULL);
SAFE_CALL(
reactor_register(reactor, new_server(false), EPOLLIN, on_accept, NULL),
-1);
SAFE_CALL(reactor_run(reactor, SERVER_TIMEOUT_MILLIS), -1);
SAFE_CALL(reactor_destroy(reactor), -1);
}
๋ชจ๋ ๊ฒ์ด ์์๋๋ก ์๋ํ๋์ง ํ์ธํด ๋ณด๊ฒ ์ต๋๋ค. ์ปดํ์ผ(chmod a+x compile.sh && ./compile.sh
ํ๋ก์ ํธ ๋ฃจํธ์์) ์์ฒด ์์ฑ๋ ์๋ฒ๋ฅผ ์์ํ๊ณ
์ฑ๋ฅ ์ธก์
๋ด ์ฐจ ์ฌ์์ ๋ณด์ฌ์ฃผ์ธ์
$ screenfetch
MMMMMMMMMMMMMMMMMMMMMMMMMmds+. OS: Mint 19.1 tessa
MMm----::-://////////////oymNMd+` Kernel: x86_64 Linux 4.15.0-20-generic
MMd /++ -sNMd: Uptime: 2h 34m
MMNso/` dMM `.::-. .-::.` .hMN: Packages: 2217
ddddMMh dMM :hNMNMNhNMNMNh: `NMm Shell: bash 4.4.20
NMm dMM .NMN/-+MMM+-/NMN` dMM Resolution: 1920x1080
NMm dMM -MMm `MMM dMM. dMM DE: Cinnamon 4.0.10
NMm dMM -MMm `MMM dMM. dMM WM: Muffin
NMm dMM .mmd `mmm yMM. dMM WM Theme: Mint-Y-Dark (Mint-Y)
NMm dMM` ..` ... ydm. dMM GTK Theme: Mint-Y [GTK2/3]
hMM- +MMd/-------...-:sdds dMM Icon Theme: Mint-Y
-NMm- :hNMNNNmdddddddddy/` dMM Font: Noto Sans 9
-dMNs-``-::::-------.`` dMM CPU: Intel Core i7-6700 @ 8x 4GHz [52.0ยฐC]
`/dMNmy+/:-------------:/yMMM GPU: NV136
./ydNMMMMMMMMMMMMMMMMMMMMM RAM: 2544MiB / 7926MiB
.MMMMMMMMMMMMMMMMMMM
๋จ์ผ ์ค๋ ๋ ์๋ฒ์ ์ฑ๋ฅ์ ์ธก์ ํด ๋ณด๊ฒ ์ต๋๋ค. ๋ ๊ฐ์ ํฐ๋ฏธ๋์ ์ด์ด ๋ณด๊ฒ ์ต๋๋ค. ํ๋์์๋ ๋ค์์ ์คํํ๊ฒ ์ต๋๋ค. ./http_server
, ๋ค๋ฅธ -
$ wrk -c100 -d1m -t8 http://127.0.0.1:18470 -H "Host: 127.0.0.1:18470" -H "Accept-Language: en-US,en;q=0.5" -H "Connection: keep-alive"
Running 1m test @ http://127.0.0.1:18470
8 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 493.52us 76.70us 17.31ms 89.57%
Req/Sec 24.37k 1.81k 29.34k 68.13%
11657769 requests in 1.00m, 1.60GB read
Requests/sec: 193974.70
Transfer/sec: 27.19MB
์ฐ๋ฆฌ์ ๋จ์ผ ์ค๋ ๋ ์๋ฒ๋ 11๊ฐ์ ์ฐ๊ฒฐ์์ ๋ฐ์ํ๋ ๋ถ๋น 100๋ง ๊ฐ ์ด์์ ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์์์ต๋๋ค. ๋์์ง ์์ ๊ฒฐ๊ณผ์ธ๋ฐ ๊ฐ์ ๋ ์ ์์๊น์?
๋ค์ค ์ค๋ ๋ ์๋ฒ
์์์ ์ธ๊ธํ๋ฏ์ด I/O ๋ฆฌ์กํฐ๋ ๋ณ๋์ ์ค๋ ๋๋ก ์์ฑ๋ ์ ์์ผ๋ฏ๋ก ๋ชจ๋ CPU ์ฝ์ด๋ฅผ ํ์ฉํ ์ ์์ต๋๋ค. ์ด ์ ๊ทผ ๋ฐฉ์์ ์ค์ ๋ก ์ ์ฉํด ๋ณด๊ฒ ์ต๋๋ค.
http_server_multithreaded.c ํ์
#include "reactor.h"
static Reactor *reactor;
#pragma omp threadprivate(reactor)
#include "common.h"
int main(void) {
#pragma omp parallel
{
SAFE_CALL((reactor = reactor_new()), NULL);
SAFE_CALL(reactor_register(reactor, new_server(true), EPOLLIN,
on_accept, NULL),
-1);
SAFE_CALL(reactor_run(reactor, SERVER_TIMEOUT_MILLIS), -1);
SAFE_CALL(reactor_destroy(reactor), -1);
}
}
์ด์ ๋ชจ๋ ์ค๋ ๋
static Reactor *reactor;
#pragma omp threadprivate(reactor)
ํจ์ ์ธ์์ ์ ์ํ์ธ์. new_server()
์นํธ์ true
. ์ด๋ ์๋ฒ ์์ผ์ ์ต์
์ ํ ๋นํ๋ค๋ ์๋ฏธ์
๋๋ค. SO_REUSEPORT
๋ ๋ฒ์งธ ์คํ
์ด์ ๋ค์ค ์ค๋ ๋ ์๋ฒ์ ์ฑ๋ฅ์ ์ธก์ ํด ๋ณด๊ฒ ์ต๋๋ค.
$ wrk -c100 -d1m -t8 http://127.0.0.1:18470 -H "Host: 127.0.0.1:18470" -H "Accept-Language: en-US,en;q=0.5" -H "Connection: keep-alive"
Running 1m test @ http://127.0.0.1:18470
8 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.14ms 2.53ms 40.73ms 89.98%
Req/Sec 79.98k 18.07k 154.64k 78.65%
38208400 requests in 1.00m, 5.23GB read
Requests/sec: 635876.41
Transfer/sec: 89.14MB
1๋ถ ๋์ ์ฒ๋ฆฌ๋๋ ์์ฒญ ์๊ฐ ~3.28๋ฐฐ ์ฆ๊ฐํ์ต๋๋ค! ํ์ง๋ง ์ฐ๋ฆฌ๋ ๋ผ์ด๋ ์๋ณด๋ค XNUMX๋ง ์ ๋ ๋ถ์กฑํ์ผ๋ฏ๋ก ์ด๋ฅผ ๊ณ ์น๋๋ก ๋ ธ๋ ฅํด ๋ณด๊ฒ ์ต๋๋ค.
๋จผ์ ์์ฑ๋ ํต๊ณ๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
$ sudo perf stat -B -e task-clock,context-switches,cpu-migrations,page-faults,cycles,instructions,branches,branch-misses,cache-misses ./http_server_multithreaded
Performance counter stats for './http_server_multithreaded':
242446,314933 task-clock (msec) # 4,000 CPUs utilized
1โฏ813โฏ074 context-switches # 0,007 M/sec
4โฏ689 cpu-migrations # 0,019 K/sec
254 page-faults # 0,001 K/sec
895โฏ324โฏ830โฏ170 cycles # 3,693 GHz
621โฏ378โฏ066โฏ808 instructions # 0,69 insn per cycle
119โฏ926โฏ709โฏ370 branches # 494,653 M/sec
3โฏ227โฏ095โฏ669 branch-misses # 2,69% of all branches
808โฏ664 cache-misses
60,604330670 seconds time elapsed
-march=native
, MAX_EVENTS
์ฌ์ฉํ๋ค EPOLLET
์ฑ๋ฅ์ด ํฌ๊ฒ ํฅ์๋์ง ์์์ต๋๋ค. ๊ทธ๋ฐ๋ฐ ๋์ ์ฐ๊ฒฐ ์๋ฅผ ๋๋ฆฌ๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?
352๊ฐ์ ๋์ ์ฐ๊ฒฐ์ ๋ํ ํต๊ณ:
$ wrk -c352 -d1m -t8 http://127.0.0.1:18470 -H "Host: 127.0.0.1:18470" -H "Accept-Language: en-US,en;q=0.5" -H "Connection: keep-alive"
Running 1m test @ http://127.0.0.1:18470
8 threads and 352 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 2.12ms 3.79ms 68.23ms 87.49%
Req/Sec 83.78k 12.69k 169.81k 83.59%
40006142 requests in 1.00m, 5.48GB read
Requests/sec: 665789.26
Transfer/sec: 93.34MB
์ํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ป์์ผ๋ฉฐ 1๋ถ ๋์ ์ฒ๋ฆฌ๋ ์์ฒญ ์๊ฐ ์ฐ๊ฒฐ ์์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋ ๊ฒ์ ๋ณด์ฌ์ฃผ๋ ํฅ๋ฏธ๋ก์ด ๊ทธ๋ํ๊ฐ ํ์๋ฉ๋๋ค.
์๋ฐฑ ๊ฐ์ ์ฐ๊ฒฐ ํ์ ๋ ์๋ฒ ๋ชจ๋์ ๋ํด ์ฒ๋ฆฌ๋ ์์ฒญ ์๊ฐ ๊ธ๊ฒฉํ ๊ฐ์ํ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค(๋ฉํฐ ์ค๋ ๋ ๋ฒ์ ์์๋ ์ด๊ฒ์ด ๋ ๋์ ๋๋๋ค). ์ด๊ฒ์ด Linux TCP/IP ์คํ ๊ตฌํ๊ณผ ๊ด๋ จ์ด ์์ต๋๊น? ๊ทธ๋ํ์ ์ด๋ฌํ ๋์๊ณผ ๋ค์ค ์ค๋ ๋ ๋ฐ ๋จ์ผ ์ค๋ ๋ ์ต์ ์ ๋ํ ์ต์ ํ์ ๋ํ ๊ฐ์ ์ ๋๊ธ์ ์์ ๋กญ๊ฒ ์ ์ด์ฃผ์ธ์.
๋ฐฉ๋ฒ
I/O ๋ฆฌ์กํฐ์ ๋จ์
I/O ๋ฆฌ์กํฐ์๋ ๋ค์๊ณผ ๊ฐ์ ๋จ์ ์ด ์๋ค๋ ์ ์ ์ดํดํด์ผ ํฉ๋๋ค.
- ๋ฉํฐ ์ค๋ ๋ ํ๊ฒฝ์์ I/O ๋ฆฌ์กํฐ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋ค์ ์ด๋ ต์ต๋๋ค. ํ๋ฆ์ ์๋์ผ๋ก ๊ด๋ฆฌํด์ผ ํฉ๋๋ค.
- ์ค์ต์ ๋ฐ๋ฅด๋ฉด ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ๋ก๋๊ฐ ๊ท ์ผํ์ง ์์ ํ ์ค๋ ๋๊ฐ ์์ ์ ๋ฐ์ ๋์ ๋ค๋ฅธ ์ค๋ ๋๊ฐ ๋ก๊น ๋ ์ ์์ต๋๋ค.
- ํ๋์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ์ค๋ ๋๋ฅผ ์ฐจ๋จํ๋ฉด ์์คํ ์ ํ๊ธฐ ์์ฒด๋ ์ฐจ๋จ๋๋ฏ๋ก ์ฐพ๊ธฐ ์ด๋ ค์ด ๋ฒ๊ทธ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํฉ๋๋ค.
๊ฒฐ๋ก
์ด๋ก ์์ ๋ฐ๋ก ํ๋กํ์ผ๋ฌ ๋ฐฐ๊ธฐ ์ฅ์น๋ก์ ์ฌ์ ์ด ๋๋๋ ๊ณณ์ ๋๋ค.
๋ค์ํ ์์ค์ ํธ๋ฆฌํจ๊ณผ ์๋๋ฅผ ๊ฐ์ถ ๋คํธ์ํฌ ์ํํธ์จ์ด๋ฅผ ์์ฑํ๋ ๋ฐ ์์ด ๋๊ฐ์ด ํฅ๋ฏธ๋ก์ด ์ ๊ทผ ๋ฐฉ์์ด ๋ง์ด ์๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋ํด ๋๋ฌด ๊น์ด ์๊ฐํด์๋ ์ ๋ฉ๋๋ค. ์ ์๊ฐ์๋ ํฅ๋ฏธ๋ก์ด ๋งํฌ๊ฐ ์๋์ ์ ๊ณต๋ฉ๋๋ค.
๋๊น์ง ์ฐ๋ฆฌ๋ ๋ค์ ๋ง๋ !
ํฅ๋ฏธ๋ก์ด ํ๋ก์ ํธ
- ๊ทธ๋ค ์์
๋ น
๋ ๋ฌด์์ ์ฝ์ด์ผ ํ๋์?
https://linux.die.net/man/7/socket https://stackoverflow.com/questions/1050222/what-is-the-difference-between-concurrency-and-parallelism http://www.kegel.com/c10k.html https://kernel.dk/io_uring.pdf https://aturon.github.io/blog/2016/09/07/futures-design/ https://tokio.rs/blog/2019-10-scheduler/ https://www.artima.com/articles/io_design_patterns.html https://habr.com/en/post/183832/
์ถ์ฒ : habr.com