반응형
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 |