PracticalSocketC_2nd_6장 - 5월 19일 수정본

Download Report

Transcript PracticalSocketC_2nd_6장 - 5월 19일 수정본

얇지만 얇지 않은
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
소켓 옵션
시그널
넌블로킹 입/출력
멀티태스킹
멀티플렉싱
다수의 수신자 처리
2
소켓 옵션
• 소켓 옵션(socket options)
– 소켓의 기본 동작을 변경
• 소켓 코드와 프로토콜 구현 코드에 대한 세부적인 제어 가능
• 소켓 옵션 관련 함수
#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);
– s : 소켓번호
– level : 프로토콜 레벨
• SOL_SOCKET: 소켓의 일반적인 옵션 변경
• IPPROTO_IP: IP 프로토콜에 관한 옵션 변경
• IPPROTO_TCP: TCP에 관한 옵션 변경
– opt : 사용하고자 하는 옵션
– optval : 옵션 지정에 필요한 값의 포인터
– optlen : optval의 크기
3
Socket Option Layer
• level
– SOL_SOCKET
• 프로토콜과 무관한 소켓 그 자체
– IPPROTO_TCP
• TCP에 관련된 옵션
– IPPROTO_IP
• IP에 관련된 옵션
4
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: 송신버퍼의 크기 변경
5
TCP Timer
• TCP Retransmission Timer
• TCP Persist Timer
• TCP Keepalive Timer
• TCP Time-Waited Timer
6
소켓 옵션 예제) Socket 내부 buffer 변경
• TCP, UDP는 송신버퍼와 수신버퍼를 가짐
–
–
–
–
–
TCP의 경우 write() 호출 시 데이터를 송신 버퍼로 복사
데이터가 송신버퍼에 모두 복사되면 시스템이 데이터를 전송
전송 데이터는 유지하고 있다가 ACK를 수신 후 삭제
송신버퍼가 가득 차면 write()는 블록됨
송신/수신버퍼의 크기를 사용자가 지정할 수 있음
• SO_SNDBUF
– 송신 버퍼의 크기 확인 및 지정
• SO_RCVBUF
– 수신 버퍼의 크기 확인 및 지정
• 송신/수신 버퍼의 크기 지정 방법
– 연결설정(3-way handshake) 후에는 버퍼 크기 변경이 불가
• 서버의 경우 listen() 호출 이전에 설정
• 클라이언트의 경우 connect() 호출 이전에 설정
7
소켓 옵션 예제) 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()");
8
소켓 옵션 예제) SO_REUSEADDR 옵션
• 용도
– 사용 중인 IP 주소와 포트 번호를 재사용
• 사용중인 IP 주소와 포트 번호로 bind() 함수를 (성공적으로) 호출할 수 있음
• 목적
– 서버 종료 후 재실행시 bind() 함수에서 오류가 발생하는 것을
방지
• fork()의 부모 프로세스 문제등
9
소켓 옵션 예제) 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));
10
Socket Option Layer
• level
– SOL_SOCKET
• 프로토콜과 무관한 소켓 그 자체
– IPPROTO_IP
• IP에 관련된 옵션
– IPPROTO_TCP
• TCP에 관련된 옵션
11
IPPROTO_IP
• IP_TTL
– Time To Live 변경
• IP_MULTICAST_TTL
– 멀티캐스트 데이터그램의 TTL 변경
• IP_ADD_MEMBERSHIP
– 멀티캐스트 그룹에 가입
• IP_DROP_MEMBERSHIP
– 멀티캐스트 그룹에서 탈퇴
• IP_MULTICAST_LOOP
– 멀티캐스트 데이터그램의 loopback 허용 여부
• IP_MULTICAST_IF
– 멀티캐스트 데이터그램 전송용 인터페이스 지정
12
멀티캐스트(Multicast)
1. 전송 방식.
- UDP를 기반으로 하는 전송 방식.
- 멀티캐스트 그룹을 기반으로 멀티캐스트 패킷을 주고 받음.
- 하나의 멀티캐스트 패킷은 라우터를 통해서 다수의 호스트에 전송.
13
멀티캐스트(Multicast)
2. 라우팅(Routing)과 TTL(Time To Live)
- 라우터에 의해서 패킷이 경로를 찾는 과정을 라우팅이라 한다.
- 멀티캐스트 패킷 내에는 TTL 정보가 포함된다. TTL은 거쳐 갈 수 있는
라우터의 수를 의미한다.
14
멀티캐스트(Multicast)
3. 멀티캐스트 Sender와 Receiver.
- Sender : 임의의 멀티캐스트 그룹에 데이터를 전송하는 호스트
- Receiver : 임의의 멀티캐스트 그룹으로부터 데이터를 수신하는 호스트,
4. 멀티캐스트 Sender와 Receiver의 구현
Sender
Receiver
• UDP 소켓 생성.
• UDP 소켓 생성.
• TTL 설정(소켓 옵션 설정).
• 멀티캐스트 그룹 지정(ip_mreq 구조체).
• 멀티캐스트 그룹으로 데이터 전송.
• 멀티캐스트 그룹 가입(소켓 옵션 설정).
15
브로드캐스트(Broadcast)
1. 전송 방식.
- UDP를 기반으로 하는 전송 방식(멀티캐스트와 같다).
- 일반적인 UDP 패킷과의 차이점은 전송 목적지 IP주소 뿐이다.
- 동일 네트워크에 속하는 모든 호스트에 동시 전송(멀티캐스트와의 차이점).
- 인터넷상에서는 지역 네트워크내에서만 브로드캐스트를 허용한다(네트워크의
부하를 고려).
16
브로드캐스트(Broadcast)
2. 주소선택에 따른 브로드캐스트 방식의 구분.
- 지정된 브로드캐스트 : 예 192.12.31.255
192
12
31
호스트 IP
네트워크 IP
192
12
xxx
31
255
브로드캐스트 address
255
브로드캐스트 address
- 지역적 브로드캐스트 : 예 255.255.255.255
255
255
255
17
Socket Option Layer
• level
– SOL_SOCKET
• 프로토콜과 무관한 소켓 그 자체
– IPPROTO_IP
• IP에 관련된 옵션
– IPPROTO_TCP
• TCP에 관련된 옵션
18
IPPROTO_TCP
• TCP_KEEPALIVE
– keep-alive 확인 메시지 전송 시간 지정
• TCP_MAXSEG
– TCP의 MSSS(최대 메시지 크기) 지정
• TCP_NODELAY
– Nagle 알고리즘의 선택
19
Nagle 알고리즘에 대한 이해.
1.
네트워크상의 패킷 수를 줄이기 위해 제안된 알고리즘.
2.
ACK를 수신해야만 다음 전송을 진행하는 알고리즘.
20
Nagle 알고리즘의 장점과 단점.
1.
장점 : 네트워크의 효율성이 높아진다.(적은 패킷의 양)
2.
단점 : 전송 속도가 느리다(ACK 수신 후 패킷 전송).
3.
생각해 볼 문제 : Nagle 알고리즘의 중단이 데이터 전송 속도를 무조건
향상시켜 주는 것은 아니다.
21
TCP_NODELAY
1.
Nagle 알고리즘을 Disable 시키기 위한 옵션의 변경.
2.
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));
22
멀티태스킹
• 멀티태스킹이란?
– 사전적 의미
• 한 사람의 사용자가 한 대의 컴퓨터로 2가지 이상의 작업을 동시에 처리하
거나, 2가지 이상의 프로그램들을 동시에 실행시키는 것
• 소켓에서의 멀티태스킹
– 다중 접속 서버의 구현을 의미
• Fork을 이용한 멀티 프로세스, thread를 이용한 멀티 스레드 기법을 이용하
여 하나의 TCP 서버가 다수개의 TCP 클라이언트를 동시에 처리하게 하는
기법
• 소켓에서의 멀티태스킹 기법
– fork를 이용한 멀티태스킹
– Thread를 이용한 멀티태스킹
23
fork()
• fork()
– 자신과 완전히 동일한 코드를 가진 새로운 프로세스를 생성
• 부모프로세스(데이터영역, 힙, 스택)를 그대로 복사
• 원본 소스의 PC(program counter)까지 복사를 하기 때문에 새로 생성된 프
로세서도 fork() 이후부터 실행
– Process
• 리눅스 기반에서 실행되는 모든 프로그램
• 각 프로세스는 ID(PID)라고 불리는 번호를 가지고 있다
– 부모 프로세스 vs 자식 프로세스
• 부모 프로세스 : 새로운 프로세스를 호출(fork)한 프로세스
• 자식 프로세스 : 새롭게 호출된(forked) 프로세스
• fork를 이용한 멀티태스킹 시 주의점
– 새로 생성된 자식 프로세스와 부모 프로세스는 변수나 메모리를
공유하지 않음(단 외부 파일, 소켓 등은 공유가능)
– 프로세스 증가로 인한 성능 감소
– 변수나 메모리 공유가 필요할 경우 => 스레드 사용
24
fork() example
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void); /* 프로세스를 복사 */
• fork()가 호출되면 동일한 프로세스가 두개로 복사되어 실
행된다
– 부모와 자식 프로세스를 구분하기 위하여 반환값을 검사해야 함
• 부모 프로세스의 fork()는 자식 프로세스의 process id(pid)를 리턴
• 자식 프로세스의 fork()는 숫자 0을 리턴
• 에러 -> -1
#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 */
}
25
fork()를 이용한 다중 클라이언트의 처리
26
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);
}
/* 부모 프로세스 : 반복적으로 클라이언트의 접속을 처리 */
}
27
Thread를 이용한 멀티태스킹
• Thread란?
– semi process, light weight process
– thread간 메모리 공유
• fork에 비해서 빠른 프로세스 생성 능력과 적은 메모리를 사용
• Network Programming에서의 thread
– 다중 클라이언트 처리를 위한 서버프로그래밍 작업
– 공유변수에서 값을 처리할 경우 사용
• 동기화 문제 어려움->쓰기의 경우 mutex사용
28
Process 와 Thread
단일 프로세스
멀티 쓰레드
29
Pthread
• POSIX thread
– POSIX에서 표준으로 제안한 thread 함수 set
– POSIX란?
• portable operating system interface
• 서로 다른 UNIX OS의 공통 API를 정리하여 이식성이 높은 유닉스 응용 프
로그램을 개발하기 위한 목적으로 IEEE가 책정한 애플리케이션 인터페이스
규격
• pthread 실행 순서
– pthread create()
• worker(thread)가 생성
– worker 시작
• 각 worker는 그들의 작업을 실행
– worker 종료
• pthread_join()에 의해서 worker를 하나로 모음
30
Thread 생성
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
31
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);
}
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!
int main(int argc, char *argv[]) {
pthread_t threads[NUM_THREADS];
Hello World! It's me, thread #4!
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);
}
}
pthread_exit(NULL);
}
32
pthread()를 이용한 다중 클라이언트의 처리
33
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);
}
34
시그널(Signal)
• 시그널이란?
– 예상치 않은 이벤트 발생에 따른 일종의 소프트웨어 인터럽트
• Ex) ctrl + c, ctrl + z, 자식 프로세스의 종료
– 외부에서 프로세스에게 전달할 수 있는 유일한 통로
• 인터럽트와의 차이점
– 인터럽트는 H/W에 의해 OS로 전달됨
– 시그널은 OS에 의해 프로세스로 전달됨
• 시그널 값의 확인
– /usr/include/signal.h
– /usr/include/bits/signum.h
35
/usr/include/bits/signal.h
#define
SIGHUP
1
#define
SIGINT
2
#define
SIGQUIT
3
#define
SIGILL
4
#define
SIGTRAP
5
#define
SIGIOT
6
#define
SIGABRT
6
#define
SIGEMT
7
#define
SIGFPE
8
#define
SIGKILL
9
#define
SIGBUS
10
#define
SIGSEGV
11
#define
SIGSYS
12
#define
SIGPIPE
13
#define
SIGALRM
14
#define
SIGTERM
15
#if defined(__rtems__)
#define
SIGURG
16
#define
SIGSTOP
17
#define
SIGTSTP
18
#define
SIGCONT
19
#define
SIGCHLD
20
#define
SIGCLD
20
#define
SIGTTIN
21
#define
SIGTTOU
22
#define
SIGIO
23
#define
SIGPOLL
SIGIO
#define
SIGWINCH 24
#define
SIGUSR1 25
#define
SIGUSR2 26
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
hangup */
interrupt , ctrl + c */
quit */
illegal instruction (not reset when caught) */
trace trap (not reset when caught) */
IOT instruction */
used by abort, replace SIGIOT in the future */
EMT instruction */
floating point exception */
kill (cannot be caught or ignored) */
bus error */
segmentation violation */
bad argument to system call */
write on a pipe with no one to read it */
alarm clock */
software termination signal from kill */
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
urgent condition on IO channel */
sendable stop signal not from tty */
stop signal from tty */
continue a stopped process */
to parent on child stop or exit */
System V name for SIGCHLD */
to readers pgrp upon background tty read */
like TTIN for output if (tp->t_local&LTOSTOP) */
input/output possible signal */
System V name for SIGIO */
window changed */
user defined signal 1 */
user defined signal 2 */
36
커널이 시그널을 처리하는 방법
• 각 시그널은 시그널 처리기(signal handler)를 통해 기본
동작으로 수행
– 가능한 기본 동작
•
•
•
•
Name
SIGINT
SIGILL
SIGKILL
SIGSEGV
SIGALRM
SIGCHLD
SIGTERM
커널이 시그널을 무시
사용자에게 통지하지 않고 프로세스를 종료
프로그램이 인터럽트 되며 시그널 처리 루틴이 실행
시그널이 블로킹됨
Default action
Quit
Dump
Quit
Dump
Quit
Ignore
Quit
Description
Interrupt
Illegal instruction
Kill
Out of range addr
Alarm clock
Child status change
Sw termination sent by kill
37
시그널 처리 과정
• 시그널 처리 과정
1. 시그널이 프로세스로 보내질 때, OS는 해당 프로세스를 중지
2. 시그널 처리기가 실행되고 내부 루틴이 실행됨
3. OS는 중지되었던 해당 프로세스를 재 실행
38
sigaction()을 이용한 시그널 처리
• 시그널과 시그널 핸들러를 연결시켜 주는 함수
• 특정 시그널에 대한 기본 동작을 바꾸어 준다
#include <signal.h>
int sigaction(int signo, const struct sigaction *act,
struct sigaction *oldact );
• int signo: 시그널 번호
• sigaction act: 새로운 동작이 정의된 sigaction
• sigaction old: 이전 동작이 저장된 sigaction
struct sigaction
{
void
(*sa_handler)( int );
sigset_t sa_mask;
int
sa_flags;
}
/* 시그널 핸들러 지정 */
/* 블록될 시그널 마스킹 */
/* 시그널의 설정 변경 */
39
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);
}
40
타임아웃(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");
}
return 0;
sleep(2);
}
41
SIGCHLD 시그널
• SIGCHLD 시그널이란?
– fork()로 인해 복제된 프로세스 중, 자식 프로세스가 종료되면 부
모 프로세스에게 전달되는 시그널
– fork() 이용시 PTP 프로젝트에 유용
42
wait() vs waitpid()
– wait()– 자식프로세스가 반환될 때까지 블럭됨
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int * status)
– waitpid()– WNOHANG옵션을 이용하면 자식 프로세스가 반환될
때까지 블록 되지 않음
• 시그널의 계류(pending) 특성으로 인해 SIGCHLD 시그널은 한번 도착했지
만 현재 종료된 자식 프로세스는 여러 개 일 수 있다. 따라서 넌블럭 waitpid
를 반복 호출하여 남아있는 좀비 프로세스의 제거가 가능하다
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int * status, int options)
43
프로세스 분기와 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();
}
44
넌 블록 I/O 모델
• 다음과 같은 채팅 프로그램의 구현이 가능한지 토의하라
Client
Server
Hi.. 
Hi..
Hi..
Hi.. 
What’s up? 
What’s up?
Not much!!
Not much!! 
Anyway.. 
Anyway..
What are you doing? 
What are you doing?
I’ve just finished network
programming assignments
I’ve just finished network
programming assignments 
45
넌 블록 I/O 모델
• send()를 연이어 두 번 하거나, 클라이언트 혹은 서버 중
어느 한쪽이 먼저 채팅을 시작하게 할 수 있는가?
Server
Client
Hi.. 
send()
recv()
Hi..
Hi..
recv()
send()
Hi.. 
What’s up? 
send()
recv()
What’s up?
Not much!!
recv()
send()
Not much!! 
Anyway.. 
send()
recv()
Anyway..
What are you doing? 
send()
recv()
What are you doing?
I’ve just finished network
programming assignments
recv()
send()
I’ve just finished network
programming assignments 
46
앞 예제의 문제점
• 사용자의 입출력 패턴과 recv(), send()의 동기화가 필요
함
– send()의 경우, 사용자 입력이 있을 때까지 기다림
– recv()와 같은 함수의 경우, 수신할 데이터가 있을 때까지 기다림
• 블록 함수
– 동기화 되지 않을 경우, block되어 진행 불가
• 사용자의 입력패턴을 정확히 예상하고 send(), recv()를
코딩
– 현실적으로 불가능
– 한턴씩 진행되는 간단한 경우에 사용가능
47
해결 방법
• 문제점
– 블록 함수의 사용으로 인한 고착 상태
• 해결 방안
– Non Block 함수의 사용
• 블록 되지 않고 바로 리턴
• 필요에 따라 폴링(polling) 루틴 작성 필요
– 비동기(Asynchronous I/O)사용
• 소켓(파일)에서 어떤 I/O 변화가 발생하면 그 사실을 응용 프로그램이 알 수
있도록 하여 그 때 원하는 동작을 할 수 있게 하는 모드
• I/O가 발생시 전달되는 SIGIO처리를 통해 폴링이 아닌 인터럽트 방식으로
처리하는 방식
48
블록 모드 vs 넌블럭 모드
• blocking 모드
– 어떤 시스템 콜을 호출하였을 때 네트워크 시스템이 동작을 완료
할 때까지 그 시스템 콜에서 프로세스가 멈춤
• 소켓 생성시 디폴트 blocking 모드
– block 될 수 있는 소켓 시스템 콜
• listen(),connect(), accept(), recv(), send(), read(), write(), recvfrom(),
sendto(), close()
– I/O시 처리가 될 때까지 기다려야 함. 비 동기적인 작업 수행 불
가능
– 일 대 일 통신을 하거나 프로그램이 한가지 작업만 하면 되는 경
우는 blocking 모드로 프로그램을 작성할 수 가능
49
블럭 vs 넌블럭
• Non-blocking 모드
– 소켓 관련 시스템 콜에 대하여 네트워크 시스템이 즉시 처리할
수 없는 경우라도 시스템 콜이 바로 리턴 되어 응용 프로그램이
block되지 않게 하는 소켓 모드
– 통신 상대가 여럿이거나 여러 가지 작업을 병행하려면
nonblocking 또는 비동기 모드를 사용하여야 한다.
• non-blocking 모드를 사용 시 동작 방식
– 넌블럭 모드를 사용하는 경우에는 일반적으로 어떤 시스템 콜이
성공적으로 실행될 때까지 계속 루프를 돌면서 주기적으로 확인
하는 방법(폴링)을 사용한다.
50
Blocking I/O Model
Application
recvfrom
kernel
system call
process
blocks
no datagram
ready
datagram ready
copy datagram
process
datagram
return OK
copy complete
51
Nonblocking I/O Model
Application
kernel
system call
recvfrom
EWOULDBLOCK
no datagram
ready
system call
recvfrom
recvfrom
EWOULDBLOC
K
system call
no datagram
ready
datagram ready
copy datagram
return OK
process
datagram
copy complete
52
넌블럭 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) {}
53
비동기 I/O(시그널 인터럽트 방식)
• SIGIO를 이용한 인터럽트 방식
– Polling방식과 대비됨
• 소켓에서 I/O 변화가 발생하면 커널이 이를 응용프로그램
에게 알려 원하는 동작을 실행
• 동작 과정
1. sigaction()를 이용 인터럽트 시그널의 종류를 시스템에 알림
2. fcntl()을 이용하여 자신을 소켓의 소유자로 지정
3. fcntl()을 통해 소켓에 FASYNC 플래그를 설정하여 소켓이 비동
기 I/O를 처리하도록 수정
54
select()를 이용한 멀티 플렉싱(Multiplexing)
• 멀티플렉싱이란?
– 다수의 송수신자가 하나의 전송 채널을 공유하는 방식
• 소켓 프로그래밍에서의 멀티플렉싱
– 하나의 프로세스를 이용 다수의 송수신 채널을 관리하는 방식
– 주로 select()를 이용하여 처리
• 멀티프로세싱 vs 멀티플렉싱
– 다수의 채널을 관리하기 위해 멀티프로세싱이 다수의 프로세스
를 사용하는데 비해 멀티플렉싱은 하나의 프로세스내부에서 다
수의 채널을 관리
• 용도
– 멀티프로세싱: 동시에 여러 채널의 I/O가 일어날 경우, 즉 하나
의 프로세스의 종료 기간이 일정시간 지속될 경우
– 멀티플렉싱: 프로세스의 I/O처리 시간이 짧아 바로 다음 프로세
스의 처리가 가능한 경우
55
I/O Multiplexing Model(Select)
Application
select
kernel
system call
no datagram
ready
process
blocks
return readable
recvfrom
system call
datagram ready
copy datagram
process
blocks
process
datagram
return OK
copy complete
56
select()의 활용
• select()를 활용
– 단일 프로세스에서 여러 fd를 모니터링하는 방법을 제공
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
Read, write
검사할 최대
fd+1
read(recv)
를 감지할
fds
write(send)
를 감지할 fds
int select(int numfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
0 : timeout
Select가 return될 시간
-1 : error
•0=> 즉시리턴
struct timeval {1 int
int tv_usec;};
이상tv_sec;
: IO가 일어난
FD의 수
•Null => blocking
57
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에서 빼준다
시간 내에 IO의
변화가 있을 경우,
값을 변경
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);
}
58
Select()를 이용한 키보드 입력과 데이터 수신의 비동기 처리
Start loop
select
키가
눌렸는가?
N
Y
키보드를
읽어 처리
0
flag
1
Socket
readable?
N
Y
소켓을
읽어 처리
0
Loop end
flag
1
59
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;
}
Loop안에 있음을 확인!
if(FD_ISSET(sock, &readOK) {
recv(sock, buf, sizeof(buf), 0);
…
if(game_end) break;
}
60
브로드 캐스팅
• 브로드캐스트란?
– LAN전체에 데이터를 뿌리는 전송 방식
• 네트워크 부하를 줄이기 위해 브로드캐스팅은 LAN으로 제한된다.
• 브로드캐스팅 전송 방식
– 서브넷 직접 전송
• 특정 서브넷의 모든 호스트에 전송
• e.g.) 203.252.153.255 // 203.252.153.0 네트워크의 모든 호스트에게 전
송
– 제한된 브로드캐스팅
• 전송 호스트가 속한 LAN의 모든 호스트에게 전송
• e.g) 255.255.255.255
• 라우터는 해당 패킷을 전달하지 않음
61
브로드캐스팅 방법
• 브로드캐스팅은 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)
62