중급 소켓프로그래밍

14
데데데 데데데 + 데데 데데 데데데데데

Upload: quxn6

Post on 20-Jun-2015

404 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: 중급 소켓프로그래밍

데이터 송수신 + 중급 소켓 프로그래밍

Page 2: 중급 소켓프로그래밍

정수 인코딩

소켓은 바이트의 순서열을 전송 .

데이터의 크기가 8bit 를 넘을 경우 여러 바이트에 걸쳐 보내는데 이 때 , 보내지는 순서가 리틀 - 엔디안인지 빅 - 엔디안 인지 판단하는 것이 중요함 .

인터넷에서 사용되는 대부분의 프로토콜들은 빅 - 엔디안을 사용 .

이를 네트워크 바이트 순서라 함 .

하드웨어가 사용하는 기법은 리틀 - 엔디안이든 빅 - 엔디안이든 상관없이 네이티브 바이트 순서 혹은 호스트 바이트 순서라고 함

ntohl, htons 같은 함수의 n 또는 h 는 변환시킬 데이터가 호스트냐 네트워크냐를 뜻 하고 to 는 h, n 은 변환될 데이터를 뜻함 , s,l 은 byte 수를 나타냄 (short, long 등 )

Page 3: 중급 소켓프로그래밍

TCP 소켓을 스트림으로 포장하기

소켓을 스트림으로 포장하게 되면 fgets(), fpusts(), fread(), fwrite() 등을 사용 가능

FILE *fopen( int socketdes, const char *mode)

소켓을 스트림으로 포장하고 결과를 반환 (error 시 NULL)

int fclose(FILE *stream)

하부 소켓과 스트림을 닫는다 .

int fflush(FILE *stream)

스트림에 버퍼링 된 데이터를 하부 소켓으로 밀어낸다 .

Page 4: 중급 소켓프로그래밍

구조체 오버레이

구조체로 데이터를 감싸서 구조체만 보낼 수 있다 .

이 경우 구조체의 멤버 변수들을 각각 바이트 순서 변환한 후 구조체만 send 할 수 있다 .

구조체는 정렬 (alignment) 되는데 이때 , 다음 두 가지 조건을 따른다 .

구조체의 주소는 내부의 가장 큰 정수 자료형으로 나눠져야 한다 . ( 가장 큰 자료형이 int

값이라면 4byte 로 나눠져야 함 )

자료형이 2 바이트 이상일경우 그 필드의 크기로 정렬된다 . 즉 int32 의 경우 시작 주소가 4byte 로 나눠져야 한다 . 2byte 자료형이라면 시작주소가 2 로 나눠저야 함

따라서 15 바이트짜리 구조체를 만든다면 컴파일러가 알아서 pad 를 넣어준다 . 이 넣어주는 pad 가 예측하기 어려우므로 구조체를 만들 때 , padding 을 고려하는 것이 좋다 .

Page 5: 중급 소켓프로그래밍

문자열과 텍스트

char 와 wchar_t 를 지원

문자열의 바이트 순서열간 변환을 하기 위해서는 wcstombs() 를 사용한다 . (wide char-

acter string to multibyte string) 반대는 mbstowcs

size_t wcstombs(char * restrict s, const wchar_t * rstrict pwcs, size_t n);

** 여기서 restrict 키워드는 오직 그 포인터만 대상을 가리킬 수 있게 하는 것 .

Page 6: 중급 소켓프로그래밍

프레이밍 framing

TCP 방식으로 보내진 메세지는 경계가 없기 때문에 메세지의 끝을 찾기위해 두 가지 방법을 사용한다 .

구분자 기반 방식 : 메세지의 끝을 특별한 기호로 나타낸다 . ex) HTTP 의 \r\n\r\n

길이 명시 방식 : 크기를 나타내는 길이 필드를 메세지 앞에 삽입한다 .

Page 7: 중급 소켓프로그래밍

시그널

signal 은 어떤 이벤트가 발생했을 때 운영체제가 프로그램에 이를 알리는 기법윈도우에서 메세지 같은것도 시그널의 한 종류인가 ?

시그널이 도착하여 블로킹 되지 않은 경우는 이를 처리한다 .

시그널이 처리 중인데 다른 시그널이 도착하면 시그널은 중첩됐다고 한다 . 나중에 도착한 시그널은 그 종류에 따라 블록될 수도 있고 아닐 수도 있다 .

시그널 처리중에 같은 종류의 또 시그널이 올 경우에는 보류시키고 이후에 또 같은 시그널이 온다면 버린다 .

따라서 같은 시그널이 두 개 이상 도착해도 보류된 시그널 한 개만 수행된다 .

Page 8: 중급 소켓프로그래밍

넌블로킹 입 / 출력

소켓 호출의 기본동작은 블로킹이다 . recv(), send() 등등 (recv() 함수는 메세지를 받을 때까지 블록 상태 , send() 는 전송할 데이터가 버퍼에 찰 때까지 )

따라서 UDP 의 경우 데이터가 유실되면 무한히 블럭되는 문제가 생길 수 있다 .

→ 소켓에 사용되는 모든 호출을 넌블로킹으로 바꿀 수 있다 .

fctrl 이라는 함수는 몇 개 플래그 , O_NONBLOCK 등으로 동작 수정이 가능하다 .

Page 9: 중급 소켓프로그래밍

비동기 입출력

논블로킹 소켓 호출의 문제점은 함수 호출이 성공할 때까지 주기적으로 호출 (polling) 해야한다는 것이다 .

소켓호출의 성공이 예상될 때 , 운영체제가 프로그램에 알려주면 좋을 것 .

→ 소켓에 어떤 준비가 되었음을 통지하고 프로그램은 다른 일을 하는 기법을 비동기 입 /

출력 이라함

Page 10: 중급 소켓프로그래밍

타임 아웃

타이머를 동작시켜 타임아웃시간이 지나면 응답을 포기하거나 재 전송하는 것 .

unsigned int alarm(unsigned int secs)

Page 11: 중급 소켓프로그래밍

멀티 태스킹

기존까지는 하나의 서버가 하나의 클라이언트에 대응했는데 , 처리하는데 시간이 길어진다면 다른 클라이언트들은 계속 대기해야 하는 사태가 벌어진다 .

멀티 태스킹서버는 클라이언트의 연결이 들어오면 그 연결을 처리하는 새로운 프로세스를 생성한다 . fork() 하는 서버의 메인 프로세스는 무한 루프를 돈다 .

Page 12: 중급 소켓프로그래밍

멀티 태스킹

for (;;) { // Run forever // New connection creates a client socket int clntSock = AcceptTCPConnection(servSock); // Fork child process and report any errors pid_t processID = fork(); if (processID < 0) DieWithSystemMessage("fork() failed"); else if (processID == 0) { // If this is the child process close(servSock); // Child closes parent socket HandleTCPClient(clntSock); exit(0); // Child process terminates }

printf("with child process: %d\n", processID); close(clntSock); // Parent closes child socket de-scriptor childProcCount++; // Increment number of child pro-cesses

Page 13: 중급 소켓프로그래밍

멀티 태스킹

while (childProcCount) { // Clean up all zombies processID = waitpid((pid_t) - 1, NULL, WNOHANG); // Non-blocking wait if (processID < 0) // waitpid() error? DieWithSystemMessage("waitpid() failed"); else if (processID == 0) // No zombie to wait on break; else childProcCount--; // Cleaned up after a child }

Page 14: 중급 소켓프로그래밍

다수의 수신자 처리

유니캐스트에서는 동일한 데이터를 보낼 때 , 수신자의 숫자만큼 여러 번 전송해야한다 .

브로드 캐스트나 멀티 캐스트를 이용하여 다수의 수신자에게 동일한 데이터를 복제하여 보낼 수 있다 .

but UDP 소켓만 가능하며 , 브로드 캐스트는 LAN 같은 지역범위에서만 가능함전체 인터넷을 경유하는 멀티 캐스트는 불가능함 .