반응형

TCP/IP Socket 기반 서버를 제작하는 방법은 크게 3가지가 있다.

 

  • 1. 멀티 프로세싱
  • 2. 멀티 쓰레싱
  • 3. 멀티 플렉싱

1번과 2번은 클라이언트 마다 새로운 프로세스, 스레드를 생성하기 때문에 서버의 부하가 많이 이뤄진다. 그래서 성능을 높이기 위해서 멀티 플레싱방식을 사용한다.

 

 

이때 주로 사용하는 것은 OS별로

 

  • linux: select, epoll, lip service
  • window: iocp

 

이번 포스팅에서는 select과 epoll의 차이점과 간단한 epoll 예제에 대해서 설명하겠다.

 

 

select과 epoll 모두 하나의 구조체에 클라이언트의 파일 디스크립터를 모아놓고 동시에 이들을 관찰할 수 있는 구조이다.

 

select함수는 아주 예전에 개발된 방식으로 몇가지 문제점을 가진다.

 

  • 1. 동시접속자수가 100안팍이다.
  • 2. 모든 파일 디스크립터를 대상으로 반복문을 진행하기에 효율이 떨어진다.
  • 3. select함수 호출시 매번 운영체제에게 관찰대상에 대한 정보를 전달해야 한다.

 

이러한 단점들 때문에 성능적인 문제점이 야기되었고, epoll이 등장했다.

 

 

epoll은 운영체제에게 관찰대상에 대한 정보를 딱 한번만 알려주고 관찰대상에 대한 변경이 있을 때 변경 사항만 알려주는 방식이다.

 

epoll의 장점

 

  • 상태변화의 확인을 위한, 전체 파일 디스크립터를 대상으로 하는 반복문이 필요 없다.
  • select 함수에 대응하는 epoll_wait 함수호출 시, 관찰대상의 정보를 매번 전달할 필요가 없다.

 

epoll의 함수

epoll_create : epoll 파일 디스크립터 저장소 생성

epoll_ctl : 저장소에 파일 디스크립터 등록 및 삭제

epoll_wait : select 함수와 마찬가지로 파일 디스크립터의 변화를 대기한다.

 

 

아래는 epoll 에코서버에 대한 간단한 예시이다.

 

server(linux)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>

#define BUF_SIZE 100
#define EPOLL_SIZE 50
void error_handling(char *buf);

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;

	if(argc!=2) {
		printf("Usage : %s <port>\n", argv[0]);
		exit(1);
	}

	serv_sock=socket(PF_INET, SOCK_STREAM, 0);
	memset(&serv_adr, 0, sizeof(serv_adr));
	serv_adr.sin_family=AF_INET;
	serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
	serv_adr.sin_port=htons(atoi(argv[1]));
	
	if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr))==-1)
		error_handling("bind() error");
	if(listen(serv_sock, 5)==-1)
		error_handling("listen() error");

	epfd=epoll_create(EPOLL_SIZE);
	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);
						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);
	return 0;
}

void error_handling(char *buf)
{
	fputs(buf, stderr);
	fputc('\n', stderr);
	exit(1);
}

 

 

client(linux)

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 1024
void error_handling(char *message);

int main(int argc, char *argv[])
{
	int sock;
	char message[BUF_SIZE];
	int str_len;
	struct sockaddr_in serv_adr;

	if(argc!=3) {
		printf("Usage : %s <IP> <port>\n", argv[0]);
		exit(1);
	}
	
	sock=socket(PF_INET, SOCK_STREAM, 0);   
	if(sock==-1)
		error_handling("socket() error");
	
	memset(&serv_adr, 0, sizeof(serv_adr));
	serv_adr.sin_family=AF_INET;
	serv_adr.sin_addr.s_addr=inet_addr(argv[1]);
	serv_adr.sin_port=htons(atoi(argv[2]));
	
	if(connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)
		error_handling("connect() error!");
	else
		puts("Connected...........");
	
	while(1) 
	{
		fputs("Input message(Q to quit): ", stdout);
		fgets(message, BUF_SIZE, stdin);
		
		if(!strcmp(message,"q\n") || !strcmp(message,"Q\n"))
			break;

		write(sock, message, strlen(message));
		str_len=read(sock, message, BUF_SIZE-1);
		message[str_len]=0;
		printf("Message from server: %s", message);
	}
	
	close(sock);
	return 0;
}

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

 

 

 

반응형

'서버개발' 카테고리의 다른 글

(TCP/IP 소켓프로그래밍)epoll 엣지 트리거과 레벨 트리거  (2) 2021.02.22
TCP프로토콜의 모든것  (2) 2021.02.08
HLS 란?  (0) 2021.02.05

+ Recent posts