2. 정수 인코딩
소켓은 바이트의 순서열을 전송.
데이터의 크기가 8bit를 넘을 경우 여러 바이트에 걸쳐 보내는데 이 때, 보내지는 순서가 리틀
-엔디안인지 빅-엔디안 인지판단하는 것이 중요함.
인터넷에서 사용되는 대부분의 프로토콜들은 빅-엔디안을 사용.
이를 네트워크 바이트 순서라 함.
하드웨어가 사용하는 기법은 리틀-엔디안이든 빅-엔디안이든 상관없이 네이티브 바이트 순
서 혹은 호스트 바이트 순서라고 함
ntohl, htons 같은 함수의 n또는 h는 변환시킬 데이터가 호스트냐 네트워크냐를 뜻 하고 to는
h, n은 변환될 데이터를 뜻함, s,l은 byte 수를 나타냄(short, long 등)
3. TCP 소켓을 스트림으로 포장하기
소켓을 스트림으로 포장하게 되면 fgets(), fpusts(), fread(), fwrite() 등을 사용 가능
FILE *fopen( int socketdes, const char *mode)
소켓을 스트림으로 포장하고 결과를 반환(error시 NULL)
int fclose(FILE *stream)
하부 소켓과 스트림을 닫는다.
int fflush(FILE *stream)
스트림에 버퍼링 된 데이터를 하부 소켓으로 밀어낸다.
4. 구조체 오버레이
구조체로 데이터를 감싸서 구조체만 보낼 수 있다.
이 경우 구조체의 멤버 변수들을 각각 바이트 순서 변환한 후 구조체만 send할 수 있다.
구조체는 정렬(alignment)되는데 이때, 다음 두 가지 조건을 따른다.
구조체의 주소는 내부의 가장 큰 정수 자료형으로 나눠져야 한다. (가장 큰 자료형이 int값이
라면 4byte로 나눠져야 함)
자료형이 2바이트 이상일경우 그 필드의 크기로 정렬된다. 즉 int32의 경우 시작 주소가 4byte
로 나눠져야 한다. 2byte자료형이라면 시작주소가 2로 나눠저야 함
따라서 15바이트짜리 구조체를 만든다면 컴파일러가 알아서 pad를 넣어준다. 이 넣어주는
pad가 예측하기 어려우므로 구조체를 만들 때, padding을 고려하는 것이 좋다.
5. 문자열과 텍스트
char와 wchar_t를 지원
문자열의 바이트 순서열간 변환을 하기 위해서는 wcstombs()를 사용한다. (wide character
string to multibyte string) 반대는 mbstowcs
size_t wcstombs(char * restrict s, const wchar_t * rstrict pwcs, size_t n);
** 여기서 restrict 키워드는 오직 그 포인터만 대상을 가리킬 수 있게 하는 것.
6. 프레이밍 framing
TCP 방식으로 보내진 메세지는 경계가 없기 때문에 메세지의 끝을 찾기위해 두 가지 방법을
사용한다.
구분자 기반 방식 : 메세지의 끝을 특별한 기호로 나타낸다. ex) HTTP의 rnrn
길이 명시 방식 : 크기를 나타내는 길이 필드를 메세지 앞에 삽입한다.
7. 시그널
signal은 어떤 이벤트가 발생했을 때 운영체제가 프로그램에 이를 알리는 기법
윈도우에서 메세지 같은것도 시그널의 한 종류인가?
시그널이 도착하여 블로킹 되지 않은 경우는 이를 처리한다.
시그널이 처리 중인데 다른 시그널이 도착하면 시그널은 중첩됐다고 한다. 나중에 도착한 시
그널은 그 종류에 따라 블록될 수도 있고 아닐 수도 있다.
시그널 처리중에 같은 종류의 또 시그널이 올 경우에는 보류시키고
이후에 또 같은 시그널이 온다면 버린다.
따라서 같은 시그널이 두 개 이상 도착해도 보류된 시그널 한 개만 수행된다.
8. 넌블로킹 입/출력
소켓 호출의 기본동작은 블로킹이다. recv(), send() 등등 (recv()함수는 메세지를 받을때까지
블록 상태, send()는 전송할 데이터가 버퍼에 찰 때까지)
따라서 udp의 경우 데이터가 유실되면 무한히 블럭될 수 있다.
소켓에 사용되는 모든 호출을 넌블로킹으로 바꿀 수 있다.
fctrl이라는 함수는 몇 개 플래그, O_NONBLOCK 등으로 동작 수정이 가능하다.
9. 비동기 입출력
논블로킹 소켓 호출의 문제점은 함수 호출이 성공할 때까지
주기적으로 호출(polling)해야한다는 것이다.
소켓호출의 성공이 예상될 때, 운영체제가 프로그램에 알려주면 좋을 것.
→ 소켓에 어떤 준비가 되었음을 통지하고 프로그램은 다른 일을 하는 기법을 비동기 입/출력
이라함
10. 타임 아웃
타이머를 동작시켜 타임아웃시간이 지나면 응답을 포기하거나 재 전송하는 것.
unsigned int alarm(unsigned int secs)
11. 멀티 태스킹
기존까지는 하나의 서버가 하나의 클라이언트에 대응했는데, 처리하는데 시간이 길어진다면
다른 클라이언트들은 계속 대기해야 하는 사태가 벌어진다.
멀티 태스킹서버는 클라이언트의 연결이 들어오면 그 연결을 처리하는 새로운 프로세스를 생
성한다. fork()하는 서버는 무한 루프를 돈다.
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: %dn", processID);
close(clntSock); // Parent closes child socket descriptor
childProcCount++; // Increment number of child processes
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
}
14. 다수의 수신자 처리
유니캐스트에서는 동일한 데이터를 보낼 때, 수신자의 숫자만큼 여러 번 전송해
야한다.
브로드 캐스트나 멀티 캐스트를 이용하여 다수의 수신자에게 동일한 데이터를 복
제하여 보낼 수 있다.
but UDP소켓만 가능하며, 브로드 캐스트는 LAN같은 지역범위에서만 가능함
전체 인터넷을 경유하는 멀티 캐스트는 불가능함.