728x90

레벨 트리거와 엣지 트리거의 차이

- 레벨 트리거 : 소켓 버퍼에 데이터가 남아있는 동안에 계속 이벤트 발생

- 엣지 트리거 : 소켓 버퍼에 데이터가 들어오는 순간에만 이벤트를 발생

    -> 새로운 데이터가 들어와야만 이벤트 발생

- 기본적으론 레벨 트리거로 설정이 되어 있음

- 서버쪽에서 컨트롤 요소가 많고 데이터 송수신이 빈번한 경우는 엣지 트리거가 유리하고 단순하고 데이터 송수신 상황이 다양하지 않으면 레벨 트리거 방식이 유리

 

엣지 트리거 기반의 서버 구현

- 넌 블로킹 IO로 소켓을 변경

    -> 엣지 트리거는 데이터 수신 시 한번만 이벤트가 발생되기 때문에 충분한 양의 버퍼를 마련한 다음 데이터를 읽어 들여야 함

    -> 데이터의 양에 따라 블로킹이 발생할 수 있기 때문에 넌 블로킹 IO로 변경

    -> 그리고 errno 변수를 참조해서 EAGAIN 값이면 버퍼가 빈 상태인 것을 확인

	int flag=fcntl(fd, F_GETFL, 0);
	fcntl(fd, F_SETFL, flag|O_NONBLOCK);

- BUF_SIZE를 작게 해서 입력 버퍼에 데이터가 남아 있게 설정

#include BUF_SIZE 4

int main(int argc, char *argv[])
{
	int serv_sock, clnt_sock;
	struct sockaddr_in serv_adr, clnt_adr;
	socklen_t adr_sz;
	int str_len, i;
	char buf[BUF_SIZE];

	struct epoll_event *ep_events;
	struct epoll_event event;
	int epfd, event_cnt;
	...;

	serv_sock=socket(PF_INET, SOCK_STREAM, 0);
	...;
	
	if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr))==-1)
		printf("bind() error");
	if(listen(serv_sock, 5)==-1)
		printf("listen() error");

	epfd=epoll_create(EPOLL_SIZE);		// epoll 저장소 생성
	ep_events=malloc(sizeof(struct epoll_event)*EPOLL_SIZE);	// 이벤트 저장할 공간 생성

	event.events=EPOLLIN;
	event.data.fd=serv_sock;	
	epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event);		// 서버 소켓의 이벤트 등록

	while(1)
	{
		event_cnt=epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);		// 이벤트 발생 대기
		if(event_cnt==-1)
		{
			puts("epoll_wait() error");
			break;
		}

		for(i=0; i<event_cnt; i++)
		{
			if(ep_events[i].data.fd==serv_sock)
			{
				adr_sz=sizeof(clnt_adr);
				clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
				event.events=EPOLLIN;
				event.data.fd=clnt_sock;
				epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);		
                // 새로운 소켓도 등록
				printf("connected client: %d \n", clnt_sock);
			} // 발생한 소켓이 서버 소켓이면
			else
			{
					str_len=read(ep_events[i].data.fd, buf, BUF_SIZE);
					if(str_len==0)    // close request!
					{
						epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);		
                        // 소켓을 epoll 관리 대상에서 삭제
						close(ep_events[i].data.fd);		// 소켓 삭제
						printf("closed client: %d \n", ep_events[i].data.fd);
					}
					else
					{
						write(ep_events[i].data.fd, buf, str_len);    // echo!
					}
	
			}
		}
	}
	close(serv_sock);		// 서버 소켓 소멸
	close(epfd);			// epoll 인스턴스 소멸
	return 0;
}

     -> 레벨 트리거일 경우 이벤트가 계속 발생

     -> 엣지 트리거일 경우 한번만 발생

int main(int argc, char *argv[])
{
	...;
	epfd=epoll_create(EPOLL_SIZE);
	ep_events=malloc(sizeof(struct epoll_event)*EPOLL_SIZE);

	int flag=fcntl(fd, F_GETFL, 0);
	fcntl(fd, F_SETFL, flag|O_NONBLOCK);	// 넌 블로킹 모드
    
	event.events=EPOLLIN;
	event.data.fd=serv_sock;	
	epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event);

	while(1)
	{
		event_cnt=epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);
		if(event_cnt==-1)
		{
			puts("epoll_wait() error");
			break;
		}

		puts("return epoll_wait");
		for(i=0; i<event_cnt; i++)
		{
			if(ep_events[i].data.fd==serv_sock)
			{
				adr_sz=sizeof(clnt_adr);
				clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
				int flag=fcntl(clnt_sock, F_GETFL, 0);
				fcntl(clnt_sock, F_SETFL, flag|O_NONBLOCK);		// 클라이언트 소켓도 넌 블로킹
				event.events=EPOLLIN|EPOLLET;
				event.data.fd=clnt_sock;
				epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);
				printf("connected client: %d \n", clnt_sock);
			}
			else
			{
					while(1)
					{
						str_len=read(ep_events[i].data.fd, buf, BUF_SIZE);
						if(str_len==0)    // close request!
						{
							epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);
							close(ep_events[i].data.fd);
							printf("closed client: %d \n", ep_events[i].data.fd);
							break;
						}
						else if(str_len<0)
						{
							if(errno==EAGAIN)		// 버퍼가 비어있는지 계속 확인
								break;
						}
						else
						{
							write(ep_events[i].data.fd, buf, str_len);    // echo!
						}
				}
			}
		}
	}
	close(serv_sock);
	close(epfd);
	return 0;
}

 

728x90

+ Recent posts