얇지만 얇지 않은 TCP/IP 소켓 프로그래밍 C 2판 (TCP/IP Sockets in C 2/e, Morgan Kaufmann) 마이클 도나후(Michael J. Donahoo) 케네스 칼버트(Kenneth L. Calvert)
Chapter 06 Beyond Basic Socket Programming 제 6장 중급 소켓 프로그래밍 6.1 소켓 옵션 6.2 시그널 6.3 넌블로킹 입/출력 6.4 멀티태스킹 6.5 멀티플렉싱 6.6 다수의 수신자 처리
소켓 옵션 소켓 옵션(socket options) 소켓 옵션 관련 함수 소켓의 기본 동작을 변경 s : 소켓번호 소켓 코드와 프로토콜 구현 코드에 대한 세부적인 제어 가능 소켓 옵션 관련 함수 s : 소켓번호 level : 프로토콜 레벨 SOL_SOCKET: 소켓의 일반적인 옵션 변경 IPPROTO_IP: IP 프로토콜에 관한 옵션 변경 IPPROTO_TCP: TCP에 관한 옵션 변경 opt : 사용하고자 하는 옵션 optval : 옵션 지정에 필요한 값의 포인터 optlen : optval의 크기 #include <sys/types.h> #include <sys/socket.h> int setsockopt(int s, int level, int opt, const char *optval, int optlen); int getsockopt(int s, int level, int opt, const char *optval, int *len);
Socket Option Layer level SOL_SOCKET IPPROTO_TCP IPPROTO_IP 프로토콜과 무관한 소켓 그 자체 IPPROTO_TCP TCP에 관련된 옵션 IPPROTO_IP IP에 관련된 옵션
SOL_SOCKET Option name SO_BROADCAST: 방송형 메시지 전송 허용 SO_DEBUG: DEBUG 모드를 선택 SO_REUSEADDR: 주소 재사용 선택 SO_LINGER 소켓을 닫을 때 미전송된 데이터가 있어도 지정된 시간만큼 기다렸다가 소켓을 닫음 SO_KEEPALIVE: TCP의 keep-alive 동작 선택 SO_OOBINLINE: OOB 데이터를 일반 데이터처럼 읽음 SO_RCVBUF: 수신버퍼의 크기 변경 SO_SNDBUF: 송신버퍼의 크기 변경
TCP Timer TCP Retransmission Timer TCP Persist Timer TCP Keepalive Timer TCP Time-Waited Timer
소켓 옵션 예제) Socket 내부 buffer 변경 TCP, UDP는 송신버퍼와 수신버퍼를 가짐 TCP의 경우 write() 호출 시 데이터를 송신 버퍼로 복사 데이터가 송신버퍼에 모두 복사되면 시스템이 데이터를 전송 전송 데이터는 유지하고 있다가 ACK를 수신 후 삭제 송신버퍼가 가득 차면 write()는 블록됨 송신/수신버퍼의 크기를 사용자가 지정할 수 있음 SO_SNDBUF 송신 버퍼의 크기 확인 및 지정 SO_RCVBUF 수신 버퍼의 크기 확인 및 지정 송신/수신 버퍼의 크기 지정 방법 연결설정(3-way handshake) 후에는 버퍼 크기 변경이 불가 서버의 경우 listen() 호출 이전에 설정 클라이언트의 경우 connect() 호출 이전에 설정
소켓 옵션 예제) Socket 내부 buffer 변경 int optval; int optlen = sizeof(optval); if(getsockopt(listen_sock, SOL_SOCKET, SO_RCVBUF, (char *)&optval, &optlen) == SOCKET_ERROR) err_quit("getsockopt()"); printf("수신 버퍼 크기 = %d 바이트\n", optval); optval = 2; if(setsockopt(listen_sock, SOL_SOCKET, SO_RCVBUF, (char *)&optval, sizeof(optval)) == SOCKET_ERROR) err_quit("setsockopt()");
소켓 옵션 예제) SO_REUSEADDR 옵션 용도 사용 중인 IP 주소와 포트 번호를 재사용 사용중인 IP 주소와 포트 번호로 bind() 함수를 (성공적으로) 호출할 수 있음 목적 서버 종료 후 재실행시 bind() 함수에서 오류가 발생하는 것을 방지 fork()의 부모 프로세스 문제등
소켓 옵션 예제) SO_REUSEADDR serv_sock=socket(PF_INET, SOCK_STREAM, 0); optlen = sizeof(option); option = TRUE; // #define TRUE 1 setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
Socket Option Layer level SOL_SOCKET IPPROTO_IP IPPROTO_TCP 프로토콜과 무관한 소켓 그 자체 IPPROTO_IP IP에 관련된 옵션 IPPROTO_TCP TCP에 관련된 옵션
IPPROTO_IP IP_TTL IP_MULTICAST_TTL IP_ADD_MEMBERSHIP Time To Live 변경 IP_MULTICAST_TTL 멀티캐스트 데이터그램의 TTL 변경 IP_ADD_MEMBERSHIP 멀티캐스트 그룹에 가입 IP_DROP_MEMBERSHIP 멀티캐스트 그룹에서 탈퇴 IP_MULTICAST_LOOP 멀티캐스트 데이터그램의 loopback 허용 여부 IP_MULTICAST_IF 멀티캐스트 데이터그램 전송용 인터페이스 지정
멀티캐스트(Multicast) 전송 방식. - UDP를 기반으로 하는 전송 방식. - 멀티캐스트 그룹을 기반으로 멀티캐스트 패킷을 주고 받음. - 하나의 멀티캐스트 패킷은 라우터를 통해서 다수의 호스트에 전송.
멀티캐스트(Multicast) 2. 라우팅(Routing)과 TTL(Time To Live) - 라우터에 의해서 패킷이 경로를 찾는 과정을 라우팅이라 한다. - 멀티캐스트 패킷 내에는 TTL 정보가 포함된다. TTL은 거쳐 갈 수 있는 라우터의 수를 의미한다.
멀티캐스트(Multicast) Sender Receiver 3. 멀티캐스트 Sender와 Receiver. • UDP 소켓 생성. • TTL 설정(소켓 옵션 설정). • 멀티캐스트 그룹으로 데이터 전송. • 멀티캐스트 그룹 지정(ip_mreq 구조체). • 멀티캐스트 그룹 가입(소켓 옵션 설정).
브로드캐스트(Broadcast) 전송 방식. - UDP를 기반으로 하는 전송 방식(멀티캐스트와 같다). - 일반적인 UDP 패킷과의 차이점은 전송 목적지 IP주소 뿐이다. - 동일 네트워크에 속하는 모든 호스트에 동시 전송(멀티캐스트와의 차이점). - 인터넷상에서는 지역 네트워크내에서만 브로드캐스트를 허용한다(네트워크의 부하를 고려).
브로드캐스트(Broadcast) 2. 주소선택에 따른 브로드캐스트 방식의 구분. 2. 주소선택에 따른 브로드캐스트 방식의 구분. - 지정된 브로드캐스트 : 예 192.12.31.255 - 지역적 브로드캐스트 : 예 255.255.255.255 192 12 31 xxx 네트워크 IP 호스트 IP 192 12 31 255 브로드캐스트 address 255 255 255 255 브로드캐스트 address
Socket Option Layer level SOL_SOCKET IPPROTO_IP IPPROTO_TCP 프로토콜과 무관한 소켓 그 자체 IPPROTO_IP IP에 관련된 옵션 IPPROTO_TCP TCP에 관련된 옵션
IPPROTO_TCP TCP_KEEPALIVE TCP_MAXSEG TCP_NODELAY TCP의 MSSS(최대 메시지 크기) 지정 TCP_NODELAY Nagle 알고리즘의 선택
Nagle 알고리즘에 대한 이해. 네트워크상의 패킷 수를 줄이기 위해 제안된 알고리즘. ACK를 수신해야만 다음 전송을 진행하는 알고리즘.
Nagle 알고리즘의 장점과 단점. 장점 : 네트워크의 효율성이 높아진다.(적은 패킷의 양) 단점 : 전송 속도가 느리다(ACK 수신 후 패킷 전송). 생각해 볼 문제 : Nagle 알고리즘의 중단이 데이터 전송 속도를 무조건 향상시켜 주는 것은 아니다.
TCP_NODELAY Nagle 알고리즘을 Disable 시키기 위한 옵션의 변경. TCP 소켓은 생성시 기본적으로 Nagle 알고리즘 적용. serv_sock=socket(PF_INET, SOCK_STREAM, 0); opt_val = TRUE; // #define TRUE 1 setsockopt(serv_sock, IPPROTO_TCP, TCP_NODELAY, &opt_val, sizeof(opt_val));
멀티태스킹 멀티태스킹이란? 소켓에서의 멀티태스킹 소켓에서의 멀티태스킹 기법 사전적 의미 다중 접속 서버의 구현을 의미 한 사람의 사용자가 한 대의 컴퓨터로 2가지 이상의 작업을 동시에 처리하거나, 2가지 이상의 프로그램들을 동시에 실행시키는 것 소켓에서의 멀티태스킹 다중 접속 서버의 구현을 의미 Fork을 이용한 멀티 프로세스, thread를 이용한 멀티 스레드 기법을 이용하여 하나의 TCP 서버가 다수개의 TCP 클라이언트를 동시에 처리하게 하는 기법 소켓에서의 멀티태스킹 기법 fork를 이용한 멀티태스킹 Thread를 이용한 멀티태스킹
fork() fork() fork를 이용한 멀티태스킹 시 주의점 자신과 완전히 동일한 코드를 가진 새로운 프로세스를 생성 부모프로세스(데이터영역, 힙, 스택)를 그대로 복사 원본 소스의 PC(program counter)까지 복사를 하기 때문에 새로 생성된 프로세서도 fork() 이후부터 실행 Process 리눅스 기반에서 실행되는 모든 프로그램 각 프로세스는 ID(PID)라고 불리는 번호를 가지고 있다 부모 프로세스 vs 자식 프로세스 부모 프로세스 : 새로운 프로세스를 호출(fork)한 프로세스 자식 프로세스 : 새롭게 호출된(forked) 프로세스 fork를 이용한 멀티태스킹 시 주의점 새로 생성된 자식 프로세스와 부모 프로세스는 변수나 메모리를 공유하지 않음(단 외부 파일, 소켓 등은 공유가능) 프로세스 증가로 인한 성능 감소 변수나 메모리 공유가 필요할 경우 => 스레드 사용
fork() example fork()가 호출되면 동일한 프로세스가 두개로 복사되어 실행된다 부모와 자식 프로세스를 구분하기 위하여 반환값을 검사해야 함 부모 프로세스의 fork()는 자식 프로세스의 process id(pid)를 리턴 자식 프로세스의 fork()는 숫자 0을 리턴 에러 -> -1 #include <sys/types.h> #include <unistd.h> pid_t fork(void); /* 프로세스를 복사 */ #include <unistd.h> #include <sys/types.h> pid_t pid; pid=fork(); /* copy new process */ if(pid==0){ /* new process code here */ } else{ /* parent code here */ }
fork()를 이용한 다중 클라이언트의 처리
fork()를 이용한 다중 클라이언트의 처리 pid_t processID; for (;;) { if(clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr, &clntLen)) < 0) DieWithError("accept() failed"); if ((processID = fork())<0) DieWithError("fork() failed"); else if (processID == 0) { /* 자식프로세스: 클라이언트 처리 */ close(servSock); HandleTCPClient(clntSock); exit(0); } /* 부모 프로세스 : 반복적으로 클라이언트의 접속을 처리 */
Thread를 이용한 멀티태스킹 Thread란? Network Programming에서의 thread semi process, light weight process thread간 메모리 공유 fork에 비해서 빠른 프로세스 생성 능력과 적은 메모리를 사용 Network Programming에서의 thread 다중 클라이언트 처리를 위한 서버프로그래밍 작업 공유변수에서 값을 처리할 경우 사용 동기화 문제 어려움->쓰기의 경우 mutex사용
Process 와 Thread 단일 프로세스 멀티 쓰레드
Pthread POSIX thread pthread 실행 순서 POSIX에서 표준으로 제안한 thread 함수 set portable operating system interface 서로 다른 UNIX OS의 공통 API를 정리하여 이식성이 높은 유닉스 응용 프로그램을 개발하기 위한 목적으로 IEEE가 책정한 애플리케이션 인터페이스 규격 pthread 실행 순서 pthread create() worker(thread)가 생성 worker 시작 각 worker는 그들의 작업을 실행 worker 종료 pthread_join()에 의해서 worker를 하나로 모음
Thread 생성 pthread_t thread pthread_attr_t attr void * (*start_routine) int pthread_create(pthread_t * thread, const pthread_attr_t * attr, void * (*start_routine)(void *), void *arg); pthread_t thread 생성된 스레드 ID를 저장할 변수 pthread_attr_t attr Set to NULL if default thread attributes are used. void * (*start_routine) pointer to the function to be threaded. Function has a single argument: pointer to void. void *arg pointer to argument of function
pthread example() #include <pthread.h> #include <stdio.h> #include <stdlib.h> #define NUM_THREADS 5 void *PrintHello(void *threadid) { int tid; tid = (int)threadid; printf("Hello World! It's me, thread #%d!\n", tid); pthread_exit(NULL); } int main(int argc, char *argv[]) { pthread_t threads[NUM_THREADS]; int rc, t; for(t=0;t<NUM_THREADS;t++){ printf("In main: creating thread %d\n", t); rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t); if (rc){ printf("ERROR; return code from pthread_create() is %d\n", rc); exit(-1); Output In main: creating thread 0 In main: creating thread 1 Hello World! It's me, thread #0! In main: creating thread 2 Hello World! It's me, thread #1! Hello World! It's me, thread #2! In main: creating thread 3 In main: creating thread 4 Hello World! It's me, thread #3! Hello World! It's me, thread #4!
pthread()를 이용한 다중 클라이언트의 처리
pthread()를 이용한 다중 클라이언트의 처리 #include <pthread.h> pthread_t tid; void *do_thread(void *arg); for (;;) { if(clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr, &clntLen)) < 0) DieWithError("accept() failed"); if(pthread_create(&tid, NULL, do_thread, (void *)clntSock) < 0 ) DieWithError(“thread create() failed”); } void *do_thread(void *arg) { int csock; csock=(int)arg; HandleTcpClient(csock); pthread_exit(NULL);
시그널(Signal) 시그널이란? 인터럽트와의 차이점 시그널 값의 확인 예상치 않은 이벤트 발생에 따른 일종의 소프트웨어 인터럽트 Ex) ctrl + c, ctrl + z, 자식 프로세스의 종료 외부에서 프로세스에게 전달할 수 있는 유일한 통로 인터럽트와의 차이점 인터럽트는 H/W에 의해 OS로 전달됨 시그널은 OS에 의해 프로세스로 전달됨 시그널 값의 확인 /usr/include/signal.h /usr/include/bits/signum.h
/usr/include/bits/signal.h #define SIGHUP 1 /* hangup */ #define SIGINT 2 /* interrupt , ctrl + c */ #define SIGQUIT 3 /* quit */ #define SIGILL 4 /* illegal instruction (not reset when caught) */ #define SIGTRAP 5 /* trace trap (not reset when caught) */ #define SIGIOT 6 /* IOT instruction */ #define SIGABRT 6 /* used by abort, replace SIGIOT in the future */ #define SIGEMT 7 /* EMT instruction */ #define SIGFPE 8 /* floating point exception */ #define SIGKILL 9 /* kill (cannot be caught or ignored) */ #define SIGBUS 10 /* bus error */ #define SIGSEGV 11 /* segmentation violation */ #define SIGSYS 12 /* bad argument to system call */ #define SIGPIPE 13 /* write on a pipe with no one to read it */ #define SIGALRM 14 /* alarm clock */ #define SIGTERM 15 /* software termination signal from kill */ #if defined(__rtems__) #define SIGURG 16 /* urgent condition on IO channel */ #define SIGSTOP 17 /* sendable stop signal not from tty */ #define SIGTSTP 18 /* stop signal from tty */ #define SIGCONT 19 /* continue a stopped process */ #define SIGCHLD 20 /* to parent on child stop or exit */ #define SIGCLD 20 /* System V name for SIGCHLD */ #define SIGTTIN 21 /* to readers pgrp upon background tty read */ #define SIGTTOU 22 /* like TTIN for output if (tp->t_local<OSTOP) */ #define SIGIO 23 /* input/output possible signal */ #define SIGPOLL SIGIO /* System V name for SIGIO */ #define SIGWINCH 24 /* window changed */ #define SIGUSR1 25 /* user defined signal 1 */ #define SIGUSR2 26 /* user defined signal 2 */
커널이 시그널을 처리하는 방법 각 시그널은 시그널 처리기(signal handler)를 통해 기본 동작으로 수행 가능한 기본 동작 커널이 시그널을 무시 사용자에게 통지하지 않고 프로세스를 종료 프로그램이 인터럽트 되며 시그널 처리 루틴이 실행 시그널이 블로킹됨 Name Default action Description SIGINT Quit Interrupt SIGILL Dump Illegal instruction SIGKILL Quit Kill SIGSEGV Dump Out of range addr SIGALRM Quit Alarm clock SIGCHLD Ignore Child status change SIGTERM Quit Sw termination sent by kill
시그널 처리 과정 시그널 처리 과정 시그널이 프로세스로 보내질 때, OS는 해당 프로세스를 중지 시그널 처리기가 실행되고 내부 루틴이 실행됨 OS는 중지되었던 해당 프로세스를 재 실행
sigaction()을 이용한 시그널 처리 시그널과 시그널 핸들러를 연결시켜 주는 함수 특정 시그널에 대한 기본 동작을 바꾸어 준다 int signo: 시그널 번호 sigaction act: 새로운 동작이 정의된 sigaction sigaction old: 이전 동작이 저장된 sigaction #include <signal.h> int sigaction(int signo, const struct sigaction *act, struct sigaction *oldact ); struct sigaction { void (*sa_handler)( int ); /* 시그널 핸들러 지정 */ sigset_t sa_mask; /* 블록될 시그널 마스킹 */ int sa_flags; /* 시그널의 설정 변경 */ }
sigaction()을 이용한 시그널 처리 /* SIGINT의 기본동작은 프로그램의 종료이다. 기본 핸들러를 바꾸어서 종료되지 않고 화면에 문자열을 출력되도록 한다. */ void handler(int sig); int main(){ struct sigaction act; act.sa_handler = handler; /* 시그널 핸들러 연결 */ sigemptyset( &act.sa_mask ); /* 블록할 시그널 없음 */ act.sa_flags = 0; /* 기본 동작으로 설정 */ sigaction( SIGINT, &act, 0 ); /* SIGINT과 handler 연결*/ while(1) { printf("Hello World!\n"); sleep(1); } void handler(int sig) { printf(“type of signal is %d \n”, sig);
타임아웃(SIGALM 시그널) 예제 SIGALM 시그널이란? alarm(int) 함수에 의해 발생하며 int 초 이후 발생 처리기의 기본동작은 프로세스 종료 이를 수정하여 화면에 문자열 출력 void timer(int sig) { puts(“alarm!! \n"); exit(0); } int main(int argc, char **argv) { struct sigaction act; act.sa_handler=timer; sigemptyset(&act.sa_mask); act.sa_flags=0; state=sigaction(SIGALRM, &act, 0); alarm(5); while(1){ puts(“wait"); sleep(2); } return 0; }
SIGCHLD 시그널 SIGCHLD 시그널이란? fork()로 인해 복제된 프로세스 중, 자식 프로세스가 종료되면 부모 프로세스에게 전달되는 시그널 fork() 이용시 PTP 프로젝트에 유용
wait() vs waitpid() wait()– 자식프로세스가 반환될 때까지 블럭됨 waitpid()– WNOHANG옵션을 이용하면 자식 프로세스가 반환될 때까지 블록 되지 않음 시그널의 계류(pending) 특성으로 인해 SIGCHLD 시그널은 한번 도착했지만 현재 종료된 자식 프로세스는 여러 개 일 수 있다. 따라서 넌블럭 waitpid를 반복 호출하여 남아있는 좀비 프로세스의 제거가 가능하다 #include <sys/types.h> #include <sys/wait.h> pid_t wait(int * status) #include <sys/types.h> #include <sys/wait.h> pid_t waitpid(pid_t pid, int * status, int options)
프로세스 분기와 sigaction을 이용한 자식 프로세스의 자원수거 void handler(int sig){ int pid; int status; while(1) { pid = waitpid( WAIT_ANY, &status, WNOHANG ); if ( pid < 0 ) { perror("waitpid"); break; } if ( pid == 0 ) break; } } int main(int argc, char *argv[]){ struct sigaction act; act.sa_handler = handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGCHLD, &act, 0); pid = fork();
넌 블록 I/O 모델 다음과 같은 채팅 프로그램의 구현이 가능한지 토의하라 Client Server Hi.. Hi.. What’s up? Not much!! Anyway.. What are you doing? I’ve just finished network programming assignments Hi.. Hi.. What’s up? Not much!! Anyway.. What are you doing? I’ve just finished network programming assignments
넌 블록 I/O 모델 send()를 연이어 두 번 하거나, 클라이언트 혹은 서버 중 어느 한쪽이 먼저 채팅을 시작하게 할 수 있는가? Client Server Hi.. Hi.. What’s up? Not much!! Anyway.. What are you doing? I’ve just finished network programming assignments Hi.. Hi.. What’s up? Not much!! Anyway.. What are you doing? I’ve just finished network programming assignments recv() send()
앞 예제의 문제점 사용자의 입출력 패턴과 recv(), send()의 동기화가 필요함 블록 함수 동기화 되지 않을 경우, block되어 진행 불가 사용자의 입력패턴을 정확히 예상하고 send(), recv()를 코딩 현실적으로 불가능 한턴씩 진행되는 간단한 경우에 사용가능
해결 방법 문제점 해결 방안 블록 함수의 사용으로 인한 고착 상태 Non Block 함수의 사용 블록 되지 않고 바로 리턴 필요에 따라 폴링(polling) 루틴 작성 필요 비동기(Asynchronous I/O)사용 소켓(파일)에서 어떤 I/O 변화가 발생하면 그 사실을 응용 프로그램이 알 수 있도록 하여 그 때 원하는 동작을 할 수 있게 하는 모드 I/O가 발생시 전달되는 SIGIO처리를 통해 폴링이 아닌 인터럽트 방식으로 처리하는 방식
블록 모드 vs 넌블럭 모드 blocking 모드 어떤 시스템 콜을 호출하였을 때 네트워크 시스템이 동작을 완료할 때까지 그 시스템 콜에서 프로세스가 멈춤 소켓 생성시 디폴트 blocking 모드 block 될 수 있는 소켓 시스템 콜 listen(),connect(), accept(), recv(), send(), read(), write(), recvfrom(), sendto(), close() I/O시 처리가 될 때까지 기다려야 함. 비 동기적인 작업 수행 불가능 일 대 일 통신을 하거나 프로그램이 한가지 작업만 하면 되는 경우는 blocking 모드로 프로그램을 작성할 수 가능
블럭 vs 넌블럭 Non-blocking 모드 non-blocking 모드를 사용 시 동작 방식 넌블럭 모드를 사용하는 경우에는 일반적으로 어떤 시스템 콜이 성공적으로 실행될 때까지 계속 루프를 돌면서 주기적으로 확인하는 방법(폴링)을 사용한다.
Blocking I/O Model Application kernel system call no datagram ready recvfrom process blocks datagram ready copy datagram return OK process datagram copy complete
Nonblocking I/O Model Application kernel system call recvfrom EWOULDBLOCK no datagram ready system call recvfrom no datagram ready EWOULDBLOCK system call recvfrom datagram ready copy datagram return OK process datagram copy complete
넌블럭 I/O 작업이 완료되지 않으면 EWOULDBLOCK(WSAEWOULDBLOC)를 리턴 작업이 진행중이라는 뜻으로 에러가 아님 non-blocking I/O 사용법 Linux fcntl(sock_fd, F_SETFL, O_NONBLOCK); // linux Windows unsigned long nb_flag = 1; ioctlsocket(sock, FIONBIO, &nb_flag); // nb_flag = 0이면 off 모두 polling을 하여 결과를 확인해야함 while (read(..) == EWOULDBLOCK) {}
비동기 I/O(시그널 인터럽트 방식) SIGIO를 이용한 인터럽트 방식 Polling방식과 대비됨 소켓에서 I/O 변화가 발생하면 커널이 이를 응용프로그램에게 알려 원하는 동작을 실행 동작 과정 sigaction()를 이용 인터럽트 시그널의 종류를 시스템에 알림 fcntl()을 이용하여 자신을 소켓의 소유자로 지정 fcntl()을 통해 소켓에 FASYNC 플래그를 설정하여 소켓이 비동기 I/O를 처리하도록 수정
select()를 이용한 멀티 플렉싱(Multiplexing) 멀티플렉싱이란? 다수의 송수신자가 하나의 전송 채널을 공유하는 방식 소켓 프로그래밍에서의 멀티플렉싱 하나의 프로세스를 이용 다수의 송수신 채널을 관리하는 방식 주로 select()를 이용하여 처리 멀티프로세싱 vs 멀티플렉싱 다수의 채널을 관리하기 위해 멀티프로세싱이 다수의 프로세스를 사용하는데 비해 멀티플렉싱은 하나의 프로세스내부에서 다수의 채널을 관리 용도 멀티프로세싱: 동시에 여러 채널의 I/O가 일어날 경우, 즉 하나의 프로세스의 종료 기간이 일정시간 지속될 경우 멀티플렉싱: 프로세스의 I/O처리 시간이 짧아 바로 다음 프로세스의 처리가 가능한 경우
I/O Multiplexing Model(Select) Application kernel system call no datagram ready select process blocks return readable datagram ready system call recvfrom copy datagram process blocks return OK process datagram copy complete
select()의 활용 select()를 활용 단일 프로세스에서 여러 fd를 모니터링하는 방법을 제공 #include <sys/time.h> #include <sys/types.h> #include <unistd.h> int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); struct timeval { int tv_sec; int tv_usec;}; Read, write 검사할 최대 fd+1 read(recv)를 감지할 fds write(send)를 감지할 fds 0 : timeout -1 : error 1 이상 : IO가 일어난 FD의 수 Select가 return될 시간 0=> 즉시리턴 Null => blocking
select() 관련 매크로 // macro FD_ZERO(fd_set *set) - 파일기술자 집합을 소거한다 FD_SET(int fd, fd_set *set) - fd 를 set에 더해준다 FD_CLR(int fd, fd_set *set) - fd 를 set에서 빼준다 main(void) { fd_set rfds; struct timeval tv; it retval; FD_ZERO(&rfds); FD_SET(0, &rfds); tv.tv_sec=5; tv.tv_usec=0; retval = select(1, &rfds, NULL, NULL, &tv); if(retval) printf(“Data is available now.\n”); else printf(“No data within 5 seconed.\n”); exit(0); } 시간 내에 IO의 변화가 있을 경우, 값을 변경
Select()를 이용한 키보드 입력과 데이터 수신의 비동기 처리 Start loop select 키가 눌렸는가? N Y 키보드를 읽어 처리 flag 1 Socket readable? N Y 소켓을 읽어 처리 flag Loop end 1
Select()를 이용한 키보드 입력과 데이터 수신의 비동기 처리 FD_ISSET(int fd, fd_set *set) - fd가 set안에서 active한지 확인 int game_end; fd_set readOK; flag=1; while(1) { FD_ZERO(&readOK); FD_SET(0,&readOK); /* 표준 입력과 소켓의 디스크립터를 세팅 */ select(maxfd+1, (fd_set*)&readOK, NULL, NULL, NULL); if(FD_ISSET(0,&readOK)) { send(); … if(game_end) break; } if(FD_ISSET(sock, &readOK) { recv(sock, buf, sizeof(buf), 0); Loop안에 있음을 확인!
브로드 캐스팅 브로드캐스트란? 브로드캐스팅 전송 방식 LAN전체에 데이터를 뿌리는 전송 방식 서브넷 직접 전송 특정 서브넷의 모든 호스트에 전송 e.g.) 203.252.153.255 // 203.252.153.0 네트워크의 모든 호스트에게 전송 제한된 브로드캐스팅 전송 호스트가 속한 LAN의 모든 호스트에게 전송 e.g) 255.255.255.255 라우터는 해당 패킷을 전달하지 않음
브로드캐스팅 방법 브로드캐스팅은 UDP만 지원 수신자는 수정사항 없으며 송신자는 아래와 같은 약간의 수정내용 필요 /* 서버 주소 구조체 초기화 시 주소를 브로드캐스팅 주소로 설정 */ SOCKADDR_IN serv; memset(&remoteaddr, 0, sizeof(remoteaddr)); serv.sin_family = AF_INET; serv.sin_port = htons(9000); serv.sin_addr.s_addr = htonl(INADDR_BROADCAST); … /* 소켓을 브로드캐스팅이 가능토록 설정 */ int broadcastPerm = 1; if (setsockopt(sock,SOL_SOCKET, SO_BROADCAST, $broadcastPerm, sizeof(broadcastPerm)) < 0)