일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- window
- channel
- 책
- C
- Python
- http
- windows
- Linux
- json
- range
- Close
- write
- API
- install
- Sync
- c++
- go언어
- 리뷰
- go
- bitcoin
- JavaScript
- 영화
- GO 언어
- File
- Callback
- FOR
- tcp
- Golang
- package
- mutex
- Today
- Total
Code Habit
epoll 본문
epoll은 리눅스에서 고성능 네트워크 프로그래밍을 위해 사용되는 I/O 이벤트 알림 모델이다. 대규모 파일 디스크럽터를 효율적으로 관리하며 이는 서버가 많은 수의 클라이언트와 동시에 통신해야 하는 고성능 네트워크 애플리케이션에 매우 유용하다.
- epoll_create
int epoll_create(int size);
size만큼의 입출력 이벤트를 저장할 공간을 만든다. 그러나 리눅스 2.6.8 부터는 커널이 필요한 데이터의 크기를 동적으로 조정하기 때문에 0보다 큰 값만 입력하면 된다.
- epoll_ctl
int epoll_ctl(int epfd, int op, int fd, struct epoll_events *event);
epoll에 fd를 등록/수정/삭제 하는 함수로 epoll에서 관심있는 fd와 이 fd에서 발생할 event를 등록할 수 있다.
op에 fd의 등록/변경/삭제를 결정하는 옵션들을 지정할 수 있다. ( EPOLL_CTL_ADD / EPOLL_CTL_MOD / EPPOL_CTL_DEL )
- epoll_wait
int epoll_wait(int epfd, struct epoll_evnet *events, int maxevents, int timeout);
관심있는 fd들에게 무슨 일이 있었는지 검사한다. 검출할 사건들의 리스트를 events->events[]로 전달하고 이벤트가 발생하면 발생한 이벤트 수를 리턴받는다. maxevents로 한번에 전달 받을 이벤트의 최대값을 지정할 수 있고 timeout으로 이벤트를 기다리는 시간을 밀리세컨드 단위로 전달할 수 있는데 -1을 전달하면 계속해서 이벤트를 기다리는 blocking 상태고 되고 0을 전달하면 이벤트 유무만 검사하고 즉시 리턴하는 상태가 된다.
- example
#include<cstdio>
#include <iostream>
#include <string.h>
#include <fcntl.h>
using namespace std;
#include <sys/unistd.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#define SERVER_PORT 7001
#define BACKLOG 10
#define MAX_EVENTS 1000;
void setnonblocking(int sock)
{
int flag = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL, flag | O_NONBLOCK);
}
int setup_socket()
{
int sock;
struct sockaddr_in sin;
if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
return -1;
}
memset(&sin, 0, sizeof sin);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(SERVER_PORT);
if (bind(sock, (struct sockaddr *)&sin, sizeof sin) < 0)
{
close(sock);
return -1;
}
if (listen(sock, BACKLOG) < 0)
{
close(sock);
return -1;
}
return sock;
}
int main()
{
int server_fd = 0;
int epoll_fd = 0;
struct epoll_event event;
struct epoll_event events[MAX_EVENTS];
int event_count = 0;
char buffer[1024] = {
0,
};
// setup socket
server_fd = setup_socket();
if (server_fd < 0)
{
return 0;
}
// create epoll fd
epoll_fd = epoll_create(100);
if (epoll_fd < 0)
{
close(server_fd);
return 0;
}
// set event
memset(&event, 0, sizeof event);
event.events = EPOLLIN | EPOLLHUP;
event.data.fd = server_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) < 0)
{
close(server_fd);
close(epoll_fd);
return 0;
}
while (true)
{
event_count = epoll_wait(epoll_fd, epoll_events, MAX_EVENTS, 100);
for (int i = 0; i < event_count; i++)
{
// accept
if (epoll_events[i].data.fd == server_fd)
{
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof client_addr;
int client_fd = accept(server_fd, (struct sockaddr_in *)&client_addr, (socklen_t *)&client_addr_len);
if (client_fd < 0)
{
continue;
}
// set client_fd non-block
setnonblocking(client_fd);
// set event
memset(&event, 0, sizeof event);
event.events = EPOLLIN | EPOLLHUP;
event.data.fd = client_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) < 0)
{
close(client_fd);
continue;
}
}
else
{
int client_fd = events[i].data.fd;
int n = read(client_fd, buffer, buffer_fd);
if (n < 0)
{ // error
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, &event);
close(client_fd);
}
else if (n == 0)
{ // client close
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, &event);
close(client_fd);
}
else
{ // read data
write(client_fd, buffer, n); // write echo message
}
}
}
}
return 0;
}
server socket을 셋팅하고 epoll에 등록하여 client 접속 및 메시지를 처리하는 예제이다. 메시지를 받으면 받은 메시지를 바로 write하여 간단한 echo 역할을 수행한다.
참고 ) https://jacking75.github.io/choiheungbae/문서/epoll을%20사용한%20비동기%20프로그래밍.pdf