Epoll 모델을 구현해 볼 시간이다.
먼저, 필요로 하는 변수들이다.
코드는 중요한 변수만 넣었고, 그림에는 주석과 내가 작성한 그대로 캡춰한 것이니 함께 보면 좋을 것 같다.
1
2
3
|
int epoll_fd; // Epoll Instance
struct epoll_event* epoll_events; // 변화가 일어난 디스크립터들에 대해서 저장할 저장소(only 동적 할당)
struct epoll_event epoll_event; // EPOLL_CTL_ADD 로 epoll_ctl 시 사용됨.
|
cs |
다음으로는 초기화 과정이다
1
2
3
4
5
6
7
8
9
|
// epoll 인스턴스 저장소 생성.
this->epoll_fd = epoll_create(Server::SERVER__CONSTVAR__MAX_USER_COUNT);
// 변화가 발생한 디스크립터들만 저장되는 구조체이며, 반드시 동적할당.
this->epoll_events = (struct epoll_event*)malloc(sizeof(struct epoll_event) * Server::SERVER__CONSTVAR__MAX_USER_COUNT);
// 이벤트 등록을 위한 준비
this->epoll_event.events = EPOLLIN;
this->epoll_event.data.fd = this->serv_sock;
// 이벤트 종류 등록
epoll_ctl(this->epoll_fd, EPOLL_CTL_ADD, this->serv_sock, &this->epoll_event);
|
cs |
그림으로 충분히 설명될꺼라고 생각한다.
이어서, 접속 요청이나 데이터 수신을 위해 epoll_wait 함수를 호출하는 부분이다
1
2
|
int changed_fd_count = 0;
changed_fd_count = epoll_wait(this->epoll_fd, this->epoll_events, Server::SERVER__CONSTVAR__MAX_USER_COUNT, -1);
|
cs |
2번 라인에서 epoll instance(epoll_fd)와 변화된 파일 디스크립터를 저장할 버퍼의 주소 값(epoll_events), 변화될 이벤트를 저장할 갯수, -1 값이 순서대로 들어간다.
epoll_wait의 마지막 인자에 -1 값이 들어간다면 파일 디스크립터의 변화가 생겨서 이벤트가 발생할 때까지 무한 대기에 들어간다.
그리고 가장 중요한 것은 select 모델에서 처럼 매번의 epoll_wait 함수 호출마다 등록된 파일 디스크립터의 배열을 전달하지 않는다.
다음은 epoll_wait 함수 호출로 받아온 리턴 값 별 처리이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
changed_fd_count = epoll_wait(this->epoll_fd, this->epoll_events, Server::SERVER__CONSTVAR__MAX_USER_COUNT, -1); // -1 전달 시 이벤트 발생까지 무한 대기
if (changed_fd_count == -1)
HandleStatus("SERVER", true, "epoll_wait() Error\n");
if (changed_fd_count == 0)
return 0;
for (int i = 0, fd = -1; i < changed_fd_count && (fd = this->epoll_events[i].data.fd); i++)
{
if (fd == this->serv_sock) // 접속 요청
{
AcceptConnectionRequest(this->serv_sock);
this->PrintConsoleNowUser();
}
else // 수신 및 종료
{
r_var = this->ReceiveData(fd);
if (r_var == Server::SERVER__RETURNVAR__DISCONNECT_USER) {
this->Disconnect(fd);
this->PrintConsoleNowUser();
} else {
if (this->HandlePacketMessage(fd) == Server::SERVER__RETURNVAR__TERMINATE_BY_ADMIN)
return Server::SERVER__RETURNVAR__TERMINATE_BY_ADMIN;
}
}
}
|
cs |
중요한 것은 6번 라인에서 돌아가는 반복문이 등록된 모든 파일 디스크립터를 대상으로 하는 것이 아니라, 변화가 일어난 파일 디스크립터의 수 만큼만 반복이 이루어진다는 점이다.
좀 더 세분화 했을 때, 접속 요청에 따른 수락 시 클라이언트 파일 디스크립터의 등록이다.
1
2
3
|
this->epoll_event.events = EPOLLIN;
this->epoll_event.data.fd = this->clnt_sock;
epoll_ctl(this->epoll_fd, EPOLL_CTL_ADD, this->clnt_sock, &this->epoll_event);
|
cs |
접속해 온 클라이언트 소켓 파일 디스크립터에 대해서 수신에 대해서 감시하겠다 라는 의미로 EPOLLIN 이벤트로 등록한다
접속한 클라이언트가 종료할 경우를 확인한다
종료하려는 클라이언트의 파일 디스크립터를 당연히 원본 fd_set의 배열에서 빼줘야한다. 종료하는데 지니고 있어서 뭐 하는가 ㅎㅎ..
대충 큰 처리 방법들은 한 번씩 다 훑어봤다.
'프로그래밍응용 > Socket' 카테고리의 다른 글
C기반 I/O Multiprocessing - 9. 멀티 프로세싱에서 좀비 문제 (0) | 2021.02.01 |
---|---|
C기반 I/O Multiprocessing - 8. 드디어 올리는 멀티 프로세싱 (0) | 2021.02.01 |
C기반 I/O Multiplexing - 6. Select와 Epoll의 차이점과 Epoll 컨셉 (0) | 2021.01.18 |
C기반 I/O Multiplexing - 5. Select 모델의 단점 (0) | 2021.01.18 |
C기반 I/O Multiplexing - 4. Level Trigger와 Edge Trigger (0) | 2021.01.17 |