Multiplexing
select()
#include <sys/select.h>
#include <sys/time.h>
FD_CLR(int fd, fd_set* set);
FD_ISSET(int fd, fd_set* set);
FD_SET(int fd, fd_set* set);
FD_ZERO(fd_set* set);
int select(int maxfd,
fd_set* readset,
fd_set* writeset,
fd_set* excepts,
struct timeval* timeout);
// 성공시 O 이상, 실패시 -1 반환
// maxfd 검사 대상이 되는 파일 디스크립터의 수.
// readset fd_set형 변수에 '수신된 데이터의 존재여부'에 관심 있는 파일 디스크립터 정보를 모두 등록해서 그 변수의 주소 값을 전달한다.
// writeset fd_set형 변수에 '블로킹 없는 데이터 전송의 가능여부'에 관심 있는 따일 디스크립터 정보를 모두 등록해서 그 변수의 주소 값을 전달한다.
// excepts fd_set형 변수에 '예외상황의 발생여부'에 관심이 있는 파일 디스크립터 정보를 모두 등록해서 그 변수의 주소 값을 전달한다.
// timeout select 함수호출 이후에 무한정 블로킹 상태에 빠지지 않도록 타임아웃(time-out)을 설정하기 위한 인자를 전달한다.
// 반환값 오류발생시 -1 반환, 타임 이웃에 의한 반환 시에는 0 반환, 그리고 관심대상으로 등록된 파일 디스크립터에 해당 관심에 관련된 변화가 발생하면 0보다 큰 값이 반환되는데, 이 값은 변화가 발생한 파일 디스크립터의 수를 의미한다.
fd_set
#define __FD_SETSIZE 1024
typedef long int __fd_mask;
#define __NFDBITS (8 * (int) sizeof (__fd_mask))
typedef struct
{
__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS]; // int fds_bits[32];
} fd_set;
// __FD_SETSIZE 'FD 집합의 크기', fd_set 구조체에 포함시킬 소켓의 최대 개수를 변경 가능하다.
// __fd_mask FD를 4바이트 정수로 masking 하기 위해 사용
// __NFDBITS 1024 / 32 = 32
FD_ZERO
FD_ZERO(fd_set * fdset)
인자로 전달된 주소의 fd_set형 변수의 모든 비트를 0으로 초기화한다.
FD_SET
FD_SET(int fd , fd_set *fdset)
매개변수 fd_set으로 전달된 주소의 변수에 매개변수 fd로 전달된파일 디스크립터 정보를등록한다.
FD_CLR
FD_CLR(int fd , fd_set *fdset)
매개변수 fd_set으로 전달된 주소의 변수에서 매개변수로 전달된 파일 디스크립터 정보를 삭제한다 .
FD_ISSET
FD_ISSET(int fd , fd_set *fdset)
매개변수 fd_set으로 전달된 주소의 변수에 매개변수 fd로 전달된 파일 디스크립터 정보가 있으면 양수를 반환한다.
파일 디스크립터의 설정
*fd = File Discripter
타임아웃 설정
struct timeval
{
long tv_sec; // seconds
long tv_usec; // microsecends
}
// 관찰중인 파일 디스크립터에 변화가 생겨야 반환을 한다.
// 때문에 변화가 생기지 않으면 무한정 블로킹 상태에 머물게 된다.
select 함수 호출 이후 결과 확인
select 함수호출이 완료되면, select 함수의 인자로 전달된 fd set형 변수
에는 변화가 생긴다.
1 로 설정된 모든 비트가 다 0으로 변경되지만 변화가 발생한 파일 디스크립터에
해당하는 비트만 그대로 1 로 남아있게 된다 . 때문에 여전히 1 로 남아있는 위치의 파일 디스크립터에서 변화가 발생했다고 판단할 수 있다 .
select 예제
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>
#define BUF_SIZE 30
int main(int argc, char *argv[])
{
fd_set reads, temps;
int result, str_len;
char buf[BUF_SIZE];
struct timeval timeout;
FD_ZERO(&reads); // fd_set형 변수 초기화
FD_SET(0, &reads); // 0 is standard input(console)
// 파일 디스크립터 0의 위치를 1로 설정, 표준 입력 변화 주시
/*
timeout.tv_sec=5;
timeout.tv_usec=5000;
*/
while(1)
{
temps=reads; // 원본 유리를 위한 복사
timeout.tv_sec=5;
timeout.tv_usec=0;
result=select(1, &temps, 0, 0, &timeout); // select 함수 호출
if(result==-1)
{
puts("select() error!");
break;
}
else if(result==0)
{
puts("Time-out!");
}
else
{
if(FD_ISSET(0, &temps)) // select 함수가 0보다 큰 수를 반환시 실행
{
str_len=read(0, buf, BUF_SIZE); // 표준 입출력으로부터 데이터를 읽어 콘솔 출력
buf[str_len]=0;
printf("message from console: %s", buf);
}
}
}
return 0;
}
Multiplexing 기반의 echo server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>
#define BUF_SIZE 100
void error_handling(char *buf);
int main(int argc, char *argv[])
{
int serv_sock, clnt_sock;
struct sockaddr_in serv_adr, clnt_adr;
struct timeval timeout;
fd_set reads, cpy_reads;
socklen_t adr_sz;
int fd_max, str_len, fd_num, i;
char buf[BUF_SIZE];
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");
FD_ZERO(&reads);
FD_SET(serv_sock, &reads); // 관찰 대상에 서버 소켓을 등록, 클라이언트 요청도 수신된 데이터로 해석
fd_max=serv_sock;
while(1)
{
cpy_reads=reads;
timeout.tv_sec=5;
timeout.tv_usec=5000;
if((fd_num=select(fd_max+1, &cpy_reads, 0, 0, &timeout))==-1) // select 실행 1 이상일때 실행
break;
if(fd_num==0)
continue;
for(i=0; i<fd_max+1; i++)
{
if(FD_ISSET(i, &cpy_reads)) // 상태 변화가 있었던(수신된 데이터가 있는 소켓)파일 디스크립터 확인
{
if(i==serv_sock) // connection request! 상태변화 확인
{
adr_sz=sizeof(clnt_adr);
clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz); // 연결 수락
FD_SET(clnt_sock, &reads);
if(fd_max<clnt_sock)
fd_max=clnt_sock;
printf("connected client: %d \n", clnt_sock);
}
else // read message! // 상태변화 소켓이 아닌 경우 == 수신할 데이터가 잇는 경우
{
str_len=read(i, buf, BUF_SIZE);
if(str_len==0) // close request! EOF인 경우 확인
{
FD_CLR(i, &reads); // 소켓 종료, reads의 정보 삭제
close(i);
printf("closed client: %d \n", i);
}
else
{
write(i, buf, str_len); // echo!, 수신 데이터가 문자열인 경우,에코
}
}
}
}
}
close(serv_sock);
return 0;
}
void error_handling(char *buf)
{
fputs(buf, stderr);
fputc('\n', stderr);
exit(1);
}