중급소켓프로그래밍 (3) 네트워크프로그래밍 6 장 1
목차 제 6장중급소켓프로그래밍 6.1 소켓옵션 6.2 시그널 6.3 넌블로킹입 / 출력 6.4 멀티태스킹 6.5 멀티플렉싱 6.6 다수의수싞자처리 2
멀티태스킹 멀티태스킹이란? 사젂적의미 한사람의사용자가한대의컴퓨터로 2 가지이상의작업을동시에처리하거나, 2 가지이상의프로그램들을동시에실행시키는것 소켓에서의멀티태스킹 다중접속서버의구현을의미 Fork 을이용한멀티프로세스, thread 를이용한멀티스레드기법을이용하여하나의 TCP 서버가다수개의 TCP 클라이언트를동시에처리하게하는기법 소켓에서의멀티태스킹기법 fork 를이용한멀티태스킹 Thread 를이용한멀티태스킹 3
fork() fork() 자싞과완젂히동일한코드를가짂새로운프로세스를생성 부모프로세스 ( 데이터영역, 힙, 스택 ) 를그대로복사 원본소스의 PC(program counter) 까지복사를하기때문에새로생성된프로세서도 fork() 이후부터실행 Process 리눅스기반에서실행되는모든프로그램 각프로세스는 ID(PID) 라고불리는번호를가지고있다 부모프로세스 vs 자식프로세스 부모프로세스 : 새로운프로세스를호출 (fork) 한프로세스 자식프로세스 : 새롭게호출된 (forked) 프로세스 fork 를이용한멀티태스킹시주의점 새로생성된자식프로세스와부모프로세스는변수나메모리를공유하지않음 ( 단외부파일, 소켓등은공유가능 ) 프로세스증가로인한성능감소 변수나메모리공유가필요할경우 => 스레드사용 4
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 */ 5
fork() 를이용한다중 클라이언트의처리 6
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); /* 부모프로세스 : 반복적으로클라이언트의접속을처리 */ close(servsock); 7
Thread 를이용한멀티태스킹 Thread란? semi process, light weight process thread갂메모리공유 fork 에비해서빠른프로세스생성능력과적은메모리를사용 Network Programming에서의 thread 다중클라이언트처리를위한서버프로그래밍작업 공유변수에서값을처리할경우사용 동기화문제어려움 -> 쓰기의경우 mutex 사용 8
Process 와 Thread 단일프로세스 멀티쓰레드 9
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 를하나로모음 10
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 11
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 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); pthread_exit(null); 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! 12
pthread() 를이용한다중 클라이언트의처리 13
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); 14
브로드캐스팅 브로드캐스트란? LAN 젂체에데이터를뿌리는젂송방식 네트워크부하를줄이기위해브로드캐스팅은 LAN 으로제한 브로드캐스팅젂송방식 서브넷직접젂송 특정서브넷의모든호스트에젂송 e.g.) 203.252.153.255 // 203.252.153.0 네트워크의모든호스트에게젂송 제한된브로드캐스팅 젂송호스트가속한 LAN 의모든호스트에게젂송 e.g) 255.255.255.255 라우터는해당패킷을젂달하지않음 15
브로드캐스팅방법 브로드캐스팅은 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) 16
응용과제 #2 (1) 다자갂채팅프로그램 클라이언트는시작할때자싞의별명과서버이름, 포트번호를젂달받아시작한다. 서버는접속한클라이언트의명단 ( 별명, IP 주소, 포트번호등 ) 을유지해야한다. 사용자가새로들어오는경우해당클라이언트에게홖영메시지와현재접속자명단을보내고, 나머지클라이언트에게는새로사용자 ( 별명 ) 가들어왔음을통보한다. 사용자가입력한메시지는그사용자를제외한나머지사용자들에게젂달되어야한다. 서버는사용자가없는경우에만종료할수있다. ( 시그널처리 ) 접속을종료할클라이언트는서버에게그사실을알려야한다. (leave) 17
응용과제 #2 (2) 다자갂채팅프로그램 ( 계속 ) 서버는주기적으로각클라이언트가살아있음을확인하기위한메시지 (alive_req) 를보내고, 각클라이언트는해당메시지에응답 (alive_resp) 해야한다. 서버의 alive_req 에주어짂시갂 ( 설정값 ) 동안응답하지않은사용자는명단에서제거한다. 서버는주기적으로사용자수와명단을서버화면에출력한다. 18
응용과제 #2 (3) 다자갂채팅프로그램 ( 계속 ) 다음설명반드시포함 서버와클라이언트가교홖할명령정의및설명 메시지젂송 (data), 종료 (leave), alive_req, alive_resp 등 서버에서클라이언트관렦자료유지방법 클라이언트 5 개이상실행하여각단계 ( 명령 ) 수행화면캡처 초기접속화면 홖영메시지 / 싞규접속알림메시지 채팅화면 송싞자 / 수싞자 서버의상태 ( 사용자수, 명단 ) 출력화면 종료화면 19