Advanced Socket Programming Department of Computer Engineering Kyung Hee University. Choong Seon Hong 1
다중처리기술 2
다중처리기술 Multitasking Multi-process Multi-thread Multiplexing Polling Selecting Interrupt 3
Multitasking 여러작업을병행하여처리하는기법 Multi-process 또는 Thread 를사용 4
Multi-process 독립적으로처리해야할작업의수만큼프로세스를생성 장점 간편한구현 단점 프로세스증가로인한프로그램의성능저하 프로세스간데이터공유가불편함 프로세스간데이터공유를위해 IPC를사용해야함 IPC를사용할경우프로그램의복잡성증가 5
Multi-thread 프로세스내에서독립적으로실행되는작업단위 프로세스내에서여러스레드를실행시켰을경우외부에서는하나의프로세스처럼취급 스레드는프로세스의이미지를복사하여사용하지않고원래프로세스의이미지를같이사용 생성된스레드용스택영역은별도로배정 데이터공유 스레드간에스택은공유되지않음 프로세스이미지를공유하므로전역변수를같이사용 동기화문제발생 6
다중처리기술의선택 Multi-process 다중처리작업들이독립적으로진행되어야하는경우 Multi-thread 다중처리작업들이밀접하게연관되어데이터공유가많이필요한경우 7
Multiplexing Block 예 ) read() 시스템콜 수신한데이터를읽기위해 read() 를호출한경우수신된데이터가있으면리턴 수신한데이터가없으면데이터가도착할때까지기다림 (block) 다수의입출력처리시 block 될수있는입출력함수사용은지양 한곳에서입출력이 block되면프로그램전체가 block됨 Multi-process, Multi-thread의경우는무관 Multiplexing 하나의 Process (or Thread) 내에서이루어지는다중처리방법 polling selecting interrupt 8
Polling 처리해야할작업들을순차적으로돌아가면서처리하는방법 서버가각클라이언트로부터의데이터수신을순차적으로처리 입출력함수가 block 되지않아야하므로소켓을 non-block 모드로설정 non-block 모드 즉시처리할수있으면결과를리턴하고, 처리할수없는경우라도리턴됨 여러클라이언트들이고르게트래픽을발생시키는경우에적합 9
Selecting Polling과의반대개념으로동작함 데이터가도착하면해당클라이언트와의입출력을처리 유닉스의 select() 함수와함께소켓을비동기 (asynchronous) 모드로변경하여사용 10
Interrupt 프로세스가어떤작업을처리하는중에특정이벤트가발생하면해당이벤트를처리하는방식 유닉스에서프로세스사이에이벤트전달은시그널을사용 11
멀티프로세스형서버프로그램 다중처리의예 ( 채팅프로그램 ) 새로운클라이언트접속시해당클라이언트와의통신을담당하는프로세스를생성 프로그램작성이편리하지만수백명이상의클라이언트에서는프로세스수가많아지는문제가있음 폴링형서버프로그램 소켓을넌블록모드로설정 서버프로그램은접속된클라이언트의입출력상태를확인 셀렉팅형서버프로그램 소켓은비동기모드로설정 비동기형방식이라고도함 인터럽트형서버프로그램 소켓은비동기모드로설정 원하는 I/O 이벤트가발생하였을때서버프로세스에게시그널로알리고, 서버가시그널처리루틴에서통신서비스를수행 12
소켓의동작모드 13
blocking 모드 소켓의기본모드 소켓에대해시스템콜을호출했을경우시스템이동작을완료할때까지프로세스가멈추어있는모드 블록될수있는소켓관련시스템콜 listen(), connect(), accept(), recv(), send(), read(), write(), recvfrom(), sendto(), close() 14
non-blocking 모드 시스템이즉시처리할수있으면결과를리턴하고처리할수없는경우에도바로리턴 다중화를위해서시스템콜의성공여부를확인하기위해폴링을주로사용 15
asynchronous 모드 소켓에서어떤 I/O 변화가발생하면이를응용프로그램에게알려원하는동작을실행시키는모드 소켓을비동기모드로변환해야함 select(), fcntl() 을이용하여 signal-driven I/O 모드로변환 select() 를이용하는방법 I/O 변화가발생할수있는소켓을대상으로 select() 를호출해두면대상소켓에서 I/O 변화가발생하였을때 select() 가리턴되고이때해당소켓에대해원하는작업을수행 signal-driven I/O 방법 특정소켓에서 I/O 변화가발생했을때 SIGIO 시그널을발생시키고이시그널을받은응용프로그램에서필요한작업을수행 16
비동기형채팅프로그램 17
채팅서버프로그램구조 select() 를이용한비동기형채팅서버 비동기형채팅서버 socket() bind() Y : 채팅참가신청 accept() client_s[] 에소켓번호추가 select() 연결용소켓? 모든소켓의 I/O 변화감지 N : 채팅메시지수신 채팅메시지방송 18
채팅서버와클라이언트의연결관계 채팅서버 채팅클라이언트 (tcp_chatcil.c) tcp_chatserv.c select() 채팅참가자 1 채팅참가자 2 소켓 s clisock_list[] 채팅참가자 3 TCP/IP 채팅참가자 4 채팅참가신청중 19
select() int select(int maxfdp1, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *tvptr); Maxfdp1 : I/O 변화를감지할총소켓의개수 + 1 readfds : 읽기 I/O 변화를감지할소켓 writefds : 쓰기 I/O 변화를감지할소켓 exceptfds : 예외상황 I/O 변화를감지할소켓 tvptr : select() 시스템콜이 I/O 변화를기다리는시간 NULL : I/O 변화가감지할때까지대기 0 : I/O 변화를기다리지않고바로리턴 > 0 : 지정된시간만큼기다린후리턴 ( 도중에변화가감지되면바로리턴 ) 20
select() 시스템콜의동작 소켓번호 0 1 2 3 maxfdp1-1 readfds 1 0 0 1 0 writefds exceptfds 0 1 0 1 0 0 0 0 0 0 fd_set 타입구조체에 I/O 변화를감지할소켓 ( 파일 ) 을 1 로세트하며 select() 를호출해두면해당조건이만족되는순간 select() 문이리턴 readfds writefds 0, 3 번이세트되어있으므로키보드 ( 표준입력 0), 소켓번호 3 에서어떤데이터가입력되어프로그램이이를읽을수있는상태가되면 select() 문이리턴 1, 3 번이세트되어있으므로파일기술자 ( 표준출력 1) 나소켓번호 3 번이 write 를할수있는상태로변하면 select() 문이리턴 21
매크로 fd_set 구조체의값을지정하기위한매크로 FD_ZERO(fd_set *fdset); fdset 의모든비트를지움 ( 삭제 ) FD_SET(int fd, fd_set *fdset); fdset 중소켓 fd 에해당하는비트를 1 로지정 FD_CLR(int fd, fd_set *fdset); fdset 중소켓 fd 에해당하는비트를 0 로지정 FD_ISSET(int fd, fd_set *fdset); fd_set 지정 fdset 중소켓 fd 에해당하는비트가세트되어있으면양수값을리턴 fd_set read_fds; FD_ZERO(&read_fds); // 모든비트를지움 FD_SET(s, &read_fds); // 초기소켓 ( 클라이언트참가용 ) 을선택 for(i=0; i<num_chat, i++) // 참가중인모든클라이언트의소켓을선택 { FD_SET(client_s[i], &read_fds); } 22
fd_set 에서의변화확인 select(maxfdp1, &read_fds, NULL, NULL, NULL); if (FD_ISSET(s, &read_fds)) { // 연결용소켓 s 에서입력발생 // 채팅참가신청처리 } // 클라이언트소켓번호 array client[ ] 를차례로검색 for (i=0; i<num_chat, i++) { if (FD_ISSET(clisock_list[i], &read_fds)) { // 통신용소켓 clisock_list[i] 에서채팅메시지수신 // 모든참가자에게채팅메시지방송 } } 23
통신용소켓구분 서버는클라이언트와의통신에사용하기위한다수의통신용소켓을개설 이소켓들은모두같은포트번호를사용 모든통신용소켓은새로운포트번호를배정받는것이아니라같은 4001번포트를사용 한개의포트번호를이용한각클라이언트의구분 서버는각연결을구분하기위해서클라이언트의 IP주소와포트번호를내부적인키로사용 채팅서버 연결용소켓통신용소켓 TCP IP 포트번호 4001 24
채팅클라이언트프로그램 기능 키보드입력메시지를서버로전송 서버가전송한메시지를수신하여화면에출력 select() 사용자의키보드입력처리와수신메시지출력을처리 소켓은읽기에대한 I/O만처리하면되므로 fd_set 구조체 read_fds 만사용 키보드입력을위해파일디스크립터 0( 표준입력 ) 을 read_fds에사용 25
Tcp_chatserv.c, tcp_chatclin.c 채팅서버 / 클라이언트 26
폴링형채팅프로그램 27
fcntl() 소켓을 non-block 모드나비동기모드로변환 소켓을 non-block 모드로설정 소켓을비동기모드로설정 소켓의소유자설정또는현재소유자를얻음 non-block 모드설정 #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, long flag); fd : 소켓디스크립터 cmd F_SETFL : 플래그세트 F_GETF: : 플래그읽기 F_SETOWN : 소켓의소유자설정 F_GETOWN : 소켓의소유자얻기 flag O_NONBLOCK : non-block 모드로설정 O_ASYNC : SIGIO 시그널에의해구동되도록비동기모드로설정 28
fcntl() 소켓을 non-block 모드로설정하는코드 기존의플래그값을유지하기위하여 F_GETFL 명령으로얻은후이를 O_NONBLOCK와 OR 연산 non-block 모드설정 int val; if ((val = fcntl(sock_fd, F_GETFL, 0)) < 0) exit(1); val = O_NONBLOCK; if ((fcntl, F_SETFL, val) < 0) exit(1); 29
select() 함수는블록형함수 비동기모드 인터럽트형다중화에서는 SIGIO 등의시그널이발생할때입출력처리 가가능 이를위해소켓을비동기모드로설정해야함 int flag; if ((flag = fcntl(fd, F_GETFL, 0)) < 0) exit(1); Flag = O_ASINC; If ((fcntl, F_SETFL, flag) < 0) exit(1); 소켓을처음생성하면소유자가없음 시그널수신을위해서는소유자가필요하므로소유자설정이필요함 fcntl(fd, F_SETOWN, getpid()); Fcntl(fd, F_GETOWN, &pid); 30
폴링형채팅서버 Non-block 모드 fcntl() 을이용하여넌블록모드로설정한후무한루프를돌면서입출 력을폴링 non-block 모드의소켓에대한 read(), write() 는바로리턴 원하는작업의실행여부를확인해야함 정상의경우 0, 에러이면 -1 을반환 에러일경우 errno 의값으로확인가능 non-block 모드의소켓으로즉시리턴된것이면 EWOULDBLOCK 의 errno 값가짐 n = recv(s, buf, length, 0); if (n>0) { // 정상적으로읽은데이터처리 } else if (n==-1 && errno == EWOULDBLOCK) // 에러가아니므로다음작업으로진행 else if (n==-1 && errno!= EWOULDBLOCK) // 시스템콜자체에서에러가발생함 // 에러처리 31
폴링형채팅서버 recv() 를호출했을때 EWOULDBLOCK 이외의에러 클라이언트와의연결종료또는클라이언트가리셋을보낸경우이므로 클라이언트를채팅목록에서제거하는등의에러처리를해야함 accept() non-block 모드의소켓에 accept() 를호출한경우 accept() 가리턴한소켓은블록모드이므로필요한경우명시적으로넌블록모드로변환해야함 소켓의모드확인 fcntl() 함수를사용 int is_nonblock(int sockfd) { int val; // 기존의플래그값을얻어온다. val = fcntl(sockfd, F_GETFL, 0); // non-block 모드인지확인 if (val & O_NONBLOCK) return 0; return -1; } 32
Non-blocking 채팅서버 / 클라이언트 TCP_chatserv_nonb.c TCP_chatcli.c 33