크래프톤 정글 TIL : 0816

lazyArtisan·2024년 8월 16일
0

정글 TIL

목록 보기
47/147

🎯 To-do List


⌚ Time Tracking

  • 09:30 - 10:10 : 기상, 빨래 걷기, 교육장 이동
  • 10:20 - 11:50 : malloc lab 발표 시간
  • 11:50 - 13:30 : 점심
  • 13:30 - 14:15 : 운영진 티타임
  • 14:15 - 14:30 : 산책
  • 14:35 - 16:00 : 알고리즘 스터디 발표, 방향성 회의
  • 16:00 - 16:45 : 산책
  • 17:00 - 17:40 : CSAPP 11장 읽기
  • 17:40 - 18:05 : 저녁
  • 18:05 - 18:35 : 키워드 공부
  • 18:40 - 21:00 : CSAPP 11장 읽기
  • 21:00 - 21:20 : 라운지 이동 및 세팅
  • 21:20 - 23:00 : CSAPP 11장 읽기
  • 23:00 - 00:50 : 에코 서버 만들기 (CSAPP 11장)

✴️ Today

  1. CSAPP 11장 읽기 ✅ | 다 했다고 보긴 애매한데 정독하는 건 끝이니 체크함
  2. 11장 보며 웹 서버 구현 ✅ | 하루에 못 끝내는 거니까 체크

⏳ Routine

🍪 Plan

  • 소켓이해 ✅ → 에코서버 → tiny 웹서버를 만들고 → 숙제문제 11.6c, 7, 9, 10, 11 중 세문제 이상 풀기 → 프록시 과제 도전
  • 퀴즈 봤던거 정리 (기술 면접 준비 +알파 느낌으로)
  • CS:APP 읽기 | 6장


📚 Journal


운영진 티타임

코테 c++? 파이썬?
파이썬 써도 됨. 편함.
어차피 나중되면 언어는 중요하지 않음.

답지?
인터넷 솔루션 자제하고 최대한 자기 힘으로.
결국 회사에선 자기 힘으로 풀어야 하는 문제들 있음.
동료에게 묻더라도 답 대신 힌트.

건강
회사 가면 크런치같은 것도 어쩔 수 없이 해야되니까 건강 관리 미리미리

산책

운영진 티타임 때 질문 했더니 코치님이 내가 마스크 쓰고 있는 이유에 대해 먼저 물으셨음. 그냥 교육장에 있으면 기침이 나서, 라고 했더니 건강 관리를 잘 하라고 하심. 요즘 수면 패턴도 앞으로 땡기느라 건강이 안 좋아졌는데 일단 몸을 먼저 챙겨야할듯. 집중력도 떨어짐.

오기 전엔 매일 산책을 1시간 동안 해서 잠도 잘 오고 컨디션도 좋았는데 여기서도 이제부터 산책 좀 해야할듯. 4시에 나가보니까 너무 덥고, 해가 아직 떠 있는 6시 쯤에 나가서 돌면 될듯. 대충 저녁 먹고 나가면 될듯? 썬크림도 까먹지 말고 발라야 함.

확실히 산책하고 나니까 컨디션이 달라진다. 개운해짐.



📝 배운 것들


🏷️ C 헤더 파일 참조법

#include할 때 <> 이거랑 "" 이거 차이가 뭐임

#include 문에서 <>""의 차이는 헤더 파일을 찾는 위치에 있습니다.

  1. #include <filename>:
    이 구문은 주로 표준 라이브러리 헤더 파일이나 시스템 헤더 파일을 포함할 때 사용됩니다.
    - 검색 경로: 컴파일러는 표준 라이브러리 디렉토리나 지정된 시스템 경로에서만 해당 파일을 찾습니다.

  2. #include "filename":
    이 구문은 주로 사용자 정의 헤더 파일을 포함할 때 사용됩니다.
    - 검색 경로: 컴파일러는 먼저 현재 소스 파일이 있는 디렉토리에서 해당 파일을 찾습니다. 만약 현재 디렉토리에서 찾지 못하면, 그 다음에 표준 라이브러리 디렉토리나 지정된 시스템 경로를 검색합니다.

🏷️ C : memset 함수

memset(&hints, 0, sizeof(struct addrinfo));

memset 함수는 메모리 블록을 특정한 값으로 설정하는 데 사용됨.
메모리 블록의 시작 주소와 크기, 설정할 값을 넣어줘야 함.

🏷️ C : |= 연산자

|=a |= b 형태로 사용됨.
a = a | b와 같은 의미.

참고로 | 연산자는 비트 OR 연산자.
비트 OR 연산자는 두 비트가 하나라도 1이면 결과 비트를 1로 만듦. 그렇지 않으면 0.



🌐 Web Server


🔷 Echo 서버 구현

troubleshooting #0 : 참조할 헤더 존재하지 않음

echo client 바로 구현하려고 몇 줄 적었는데
맨 처음 헤더 파일부터 빨간 줄 뜨길래 뭔가 했더니만
책 앞쪽에서 설명했던 함수가 내장 함수가 아니라 만드는 함수였음.
open_clientfd랑 open_listenfd를 csapp.c에 구현해야 함.

📌 Implement : open_clientfd


1. troubleshooting : struct addrinfo 빨간 줄

struct addrinfo에 빨간 줄이 떴었음.
책을 뒤졌지만 해결책을 찾지 못함.

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

코파일럿의 도움을 받아 이걸 추가했지만 고쳐지지 않음.
gpt의 신탁도 마저 받아서 빨간 줄 없앰.

#define _POSIX_C_SOURCE 200112L // POSIX 기능 활성화

오 이거 추가했더니 빨간 줄 사라짐 왜 이러는거임?

#define _POSIX_C_SOURCE 200112L를 추가했을 때 빨간 줄이 사라진 이유는, 이 매크로 정의가 POSIX 표준에 따른 기능들을 활성화하기 때문입니다.

상세 설명

  1. POSIX 표준:

    • POSIX(Portable Operating System Interface)는 유닉스 운영체제의 표준을 정의한 규격입니다. 다양한 운영체제에서 일관된 API를 제공하기 위해 이 표준이 사용됩니다.
  2. _POSIX_C_SOURCE 매크로:

    • 이 매크로는 특정 POSIX 기능을 사용할 수 있도록 컴파일러에게 알려줍니다.
    • 예를 들어, _POSIX_C_SOURCE 200112L로 정의하면 POSIX 2001 표준에 정의된 기능들이 활성화됩니다.
  3. 헤더 파일의 조건부 컴파일:

    • 많은 시스템 헤더 파일(특히 POSIX 표준을 따르는 헤더 파일)은 특정 매크로가 정의되어 있을 때만 그에 해당하는 구조체나 함수들을 노출합니다.
    • netdb.h 헤더 파일도 그 중 하나로, POSIX 표준에 따라 정의된 기능들은 _POSIX_C_SOURCE 매크로가 설정되어 있을 때만 제대로 노출됩니다.
  4. addrinfo 구조체:

    • addrinfo 구조체와 관련된 정의는 POSIX 표준의 일부이며, 이 표준에 따라 컴파일러가 해당 기능들을 인식하려면 _POSIX_C_SOURCE가 정의되어 있어야 합니다.
    • 이 매크로를 정의하지 않으면 컴파일러가 POSIX 관련 정의를 포함하지 않기 때문에, addrinfo 구조체와 같은 일부 POSIX 기능을 인식하지 못하게 됩니다.

결론

#define _POSIX_C_SOURCE 200112L를 추가함으로써 컴파일러가 POSIX 2001 표준에 따른 기능들을 사용할 수 있도록 허용하였고, 이로 인해 addrinfo 구조체가 제대로 인식되어 빨간 줄이 사라졌습니다.

이는 POSIX 표준에 따라 작성된 코드가 호환성 있게 동작하도록 보장하는 데 중요한 역할을 합니다. 컴파일러가 POSIX 기능을 인식하게 하려면 이와 같은 매크로를 정의해야 하는 경우가 있습니다.

어차피 gcc로 컴파일 할 거면 vscode에서 빨간 줄 떠도 무시하면 되는거지?

네, GCC로 컴파일할 때 오류 없이 빌드가 된다면, Visual Studio Code(VSCode)에서 나타나는 빨간 줄은 무시해도 됩니다. 이러한 빨간 줄은 보통 IDE의 코드 분석 도구나 언어 서버가 코드에서 문제가 될 수 있는 부분을 표시하는 것인데, 실제 컴파일러의 동작과는 차이가 있을 수 있습니다.

2. Question : 비트 연산자와 AI_ADDRCONFIG

hints.ai_flags |= AI_ADDRCONFIG; // Recommended for connections

책 코드에 이런 내용이 있었음.
코드도 이해가 안 가고 주석도 이해가 안 갔음.
|=는 위에 적어놓았으니 패스.

플래그?

hints.ai_flags랑 AI_ADDRCONFIG는 플래그임.
플래그는 비트마다 1과 0으로 특정 상태를 저장하는 거임.

A라는 기능에 대한 플래그가 두 번째 비트에 저장돼있으면
0100은 켜진거고, 0000은 꺼진 거임.

hints.ai_flags랑 AI_ADDRCONFIG를 or연산 해서
둘 중 하나라도 선택된 비트가 있으면 1로 만들어버림.

AI_ADDRCONFIG?

시스템에서 IPv4나 IPv6을 쓸 건지 말건지 정의돼있는 상수.
우리가 선언한 hints.ai_flags랑 플래그를 합치면
한쪽이라도 IPv4, IPv6를 사용하겠다고 했으면 사용된다.
원래는 그래야 할테지만,

우리는 hints.ai_flags를 초기화 한 후 AI_NUMERICSERV를 넣었었다.
AI_NUMERICSERV는 AI_ADDRCONFIG와 관련이 없다.
플래그로 삼는 비트가 안 겹침.

그래서 기존에 넣은 값을 무시하지 않으면서
새로운 플래그를 추가하려면 = 대신 |=를 사용해야 함.

그래서 왜 추천?

님 컴이 IPv4 쓰는지 IPv6 쓰는지 몰라서 그러는듯?



📖 CS:APP


11.3.1 IP 주소

struct in_addr {
    uint32_t s_addr; /* 네트워크 바이트 순서로 저장된 주소 (빅 엔디안) */
};

IP는 부호가 없는 32비트 정수다.
네트워크 프로그램은 IP 주소를 IP 주소 구조체에 저장한다.

바이트 순서

인터넷에 연결된 호스트는 서로 다른 바이트 순서(엔디안)를 가질 수 있기 때문에,
TCP/IP는 패킷 헤더에 포함되는 IP 주소 같은 모든 정수형 데이터에 대해
모두 네트워크 바이트 순서(빅 엔디안 바이트 순서)를 정의한다.
리틀 엔디안 호스트에서도 IP 주소는 항상 빅 엔디안으로 저장된다는 뜻.

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong); // 호스트 바이트 순서를 네트워크 바이트 순서로 변환
uint16_t htons(uint16_t hostshort); // 호스트 바이트 순서를 네트워크 바이트 순서로 변환
uint32_t ntohl(uint32_t netlong);   // 네트워크 바이트 순서를 호스트 바이트 순서로 변환
uint16_t ntohs(uint16_t netshort);  // 네트워크 바이트 순서를 호스트 바이트 순서로 변환

↑ Unix에서 네트워크와 호스트 바이트 순서 간의 변환을 위해 제공하는 함수들 예시

도트 10진수 표기법

linux> hostname -i
128.2.210.175

IP 주소는 일반적으로 인간이 이해하기 쉬운 도트 10진수 표기법으로 표현됨.
이 표기법에선 각 바이트가 10진수로 나타나고, 바이트 간에는 점으로 구분됨.
예를 들어, 128.2.194.242는 주소 0x8002c2f2의 도트 10진수 표현임.
리눅스 시스템에서는 hostname 명령어를 사용하여 자신의 호스트에 할당된 도트 10진수 주소를 확인할 수 있음.

IP 주소와 문자열 간의 변환

#include <arpa/inet.h>

int inet_pton(int af, const char *src, void *dst); 
// 도트 10진수 문자열을 네트워크 바이트 순서의 이진 IP 주소로 변환
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); 
// 네트워크 바이트 순서의 이진 IP 주소를 도트 10진수 문자열로 변환

응용 프로그램은 IP 주소와 도트 10진수 문자열 간의 변환을 위해 inet_pton이나 inet_ntop을 사용함.
이 함수들에서 "n"은 네트워크, "p"는 프레젠테이션을 의미함. IPv6 주소(128비트)까지도 처리 가능.

11.3.2 인터넷 도메인 이름

IP 주소같은거 사람들 못 외우니까 도메인 이름을 만들었음. www.naver.com 같은거.

인터넷은 도메인 이름의 집합과 IP 주소의 집합 사이의 매핑을 정의함.
1988 이전까지 이 매핑은 HOST.TXT로 수작업으로 관리했음.
이후 이 매핑은 DNS(Domain Name System)라는 전 세계에 분산된 데이터베이스에서 관리됨.
개념적으로 DNS 데이터베이스는 각 도메인 이름과 IP 주소의 매핑을 정의하는 수백만개의 호스트 항목들로 구성됨.

각 인터넷 호스트는 항상 루프백 주소 127.0.0.1로 매핑되는 localhost라는 로컬 도메인 이름을 갖고 있음.
localhost는 동일한 기기에서 실행 중인 클라이언트와 서버를 참조하고 디버깅하는 데 유용함.
자신의 로컬 호스트의 실제 도메인 이름은 위에서 언급된 hostname으로 확인할 수 있음.

도메인 이름하고 IP 주소는 1대1로 매핑될 수 있음.
어떨 땐 다수의 도메인 이름이 동일한 IP 주소에 매핑될 수도 있음.
대부분의 경우엔 다수의 도메인 이름들이 다수의 IP 주소에 매핑됨.
이렇게 다대다 매핑하면 트래픽 분산이나 장애 조치하기 쉬움.

11.3.3 인터넷 연결

인터넷 클라이언트와 서버는 연결(connection)을 통해 바이트 스트림을 주고받는 방식으로 통신함.
데이터는 동시에 양방향(full-duplex)으로 전송될 수 있음.
송신 측 프로세스가 보낸 바이트 스트림은 수신 측 프로세스에 동일한 순서로 도착하도록 보장됨.

소켓의 형식

연결의 끝점을 소켓(socket)이라고 함. 각 소켓은 인터넷 주소와 16비트 정수 포트 번호로 구성된 소켓 주소를 가짐. address:port 이런 식으로 표기됨.

클라이언트 소켓 주소에서 포트 번호는 클라이언트가 연결 요청할 때 커널이 자동으로 할당하는 단기(ephemeral) 포트임.

반면에, 서버 소켓 주소의 포트 번호는 서비스와 영구적으로 연결된 well-known port를 사용함.
보통 웹 서버는 포트 80, 이메일 서버는 포트 25를 사용함.

well-known port와 연결된 well-known service name들도 있음.
웹 서비스는 http, 이메일 서비스는 smtp라는 이름을 사용함.

well-known port, well-known service name들의 매핑 정보는 etc/services 파일에 저장돼있음.

연결 식별

(cliaddr:cliport, servaddr:servport)

읽는 법 | ip주소:포트

연결은 두 끝점의 소켓 주소에 의해 고유하게 식별됨.
이 소켓 주소 쌍을 소켓 페어라고 함. 튜플로 저장됨.

128.2.194.242:51213 (클라이언트 소켓 주소) <-> 208.216.181.15:80 (서버 소켓 주소)

소켓이 연결된 예시임.

클라이언트 소켓 주소는 128.2.194.242이고, 포트 51213는 커널에 의해 할당된 일시적인 포트임.
서버 소켓 주소는 208.216.181.15이고, 포트 80은 웹 서비스의 well-known port임.

11.4 소켓 인터페이스

소켓 인터페이스는 네트워크 응용을 만들기 위해서 Unix I/O 함수들과 함께 사용되는 함수들의 집합임.

11.4.1 소켓 주소 구조체

커널 관점에서 소켓은 통신을 위한 끝점
프로그램 관점에서 소켓은 식별자가 있고, 열려있는 파일

인터넷 소켓 주소는 sockaddr_in라는 16바이트 구조체에 저장됨.

인터넷 앱 만들 때의 구조체 초기화 예시

  • sin_family : AF_INET → 프로토콜 패밀리. 항상 AF_INET. IPv4 체계 쓴다는 뜻.
  • sin_port : 포트 번호 → 16비트. 네트워크 바이트 순서(빅 엔디안).
  • sin_addr : IP 주소 → 32비트. 빅 엔디안
  • (+ sin_zero : 구조체의 크기를 맞추기 위해 추가된 8바이트 패딩)

(참고) _in 접미사의 의미 : internet

11.4.2 socket 함수

소켓 함수는 클라이언트와 서버가 통신을 시작할 수 있게 소켓 식별자를 생성해준다.

이 함수는 소켓 식별자를 생성하여 반환하는데,
이 식별자는 네트워크 통신의 끝점을 나타내며,
파일 디스크립터와 유사한 방식으로 처리된다.

#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

domain: 소켓이 사용할 프로토콜 패밀리
type: 소켓의 유형
protocol: 사용하려는 프로토콜을 지정. 보통 0으로 설정하고 기본 프로토콜 사용.

socket이 리턴한 clientfd 식별자는 아직 읽거나 쓸 수 없다.
소켓 디스크립터를 어떻게 완전히 여는지는 클라인지 서버인지에 따라 다르다.

클라이언트가 소켓 식별자 완전히 여는 법

클라이언트는 connect 함수를 호출해서 서버와의 연결한다.

  • connect 함수는 소켓 주소 addr의 서버와 인터넷 연결을 시도한다.
    연결이 성공할 때까지 블록되어 있거나 에러가 발생한다.
    성공하면 clientfd 식별자를 읽거나 쓸 준비가 된다.

서버가 소켓 식별자 완전히 여는 법

서버는 bind, listen, accept를 호출해서 클라이언트와 연결한다.

  • bind 함수는 커널에게 addr에 있는 서버 소켓 주소를 소켓 식별자 sockfd와 연결하라고 한다.
    bind 함수에 인자 제공할 땐 수동으로 설정하지 말고 getaddrinfo 함수를 사용하는게 좋다.

  • 기본적으로 클라이언트는 연결을 요청하고, 서버는 연결 요청을 기다림. 커널은 socket을 일단 클라이언트의 소켓이라고 생각함. 그래서 listen 함수로 sockfd를 서버가 쓰는 소켓으로 바꿔줘야 함. 그래야 클라이언트로부터의 연결 요청 받을 수 있음.

  • accept 함수 호출하면 클라이언트로부터의 연결 요청을 기다리기 시작함.
    - 연결 요청이 듣기 식별자인 listenfd에 도달하는 걸 기다린다는 뜻.
    - 요청 오면 addr 내의 클라이언트 소켓 주소 채우고 연결 식별자를 반환한다.
    - 연결 식별자와 함께 Unix I/O 함수를 사용해서 클라이언트와 소통할 수 있음.

듣기 식별자와 연결 식별자의 차이

  • 듣기 식별자 : 클라이언트 연결 요청에 대해 끝점으로서의 역할
  • 연결 식별자 : 클라이언트와 서버 사이에 성립된 연결의 끝점.
    ➀ 서버가 연결 요청을 수락할 때마다 생성되며, ➁ 서버가 클라이언트에 서비스하는 동안만 존재함.

서버와 클라이언트가 통신하는 과정

  1. 서버가 accept를 호출해서 클라이언트의 연결 요청을 기다림. 이때 서버는 연결 요청이 듣기 식별자 listenfd에 도달하기를 기다림.

  2. 클라이언트가 서버에 연결 요청을 보냄. 이 연결 요청은 서버의 듣기 식별자 listenfd로 전송됨.

  3. 서버가 accept 함수로 클라이언트의 요청을 수락함. 연결 식별자 connfd를 가진 서버 소켓이 만들어지고, 클라이언트 소켓 clientfd 사이에 연결이 확립됨. 서버가 connfd를 프로그램에 반환하고, 클라이언트는 connect 함수에서 빠져나옴.

  4. 이제 서버와 클라이언트는 각각 connfd와 clientfd를 사용해서 데이터를 주고받을 수 있음. 서버는 connfd로 데이터를 읽고 쓰며, 클라이언트는 clientfd로 데이터를 주고 받는다.


듣기 식별자랑 연결 식별자 구분 하는 이유가 듣기 식별자는 그대로 두고 연결 식별자는 각 클라이언트마다 다른거 줄 수 있으니까임?

맞습니다! 듣기 식별자(listenfd)와 연결 식별자(connfd)를 구분하는 이유는 다음과 같습니다:

  • 듣기 식별자(listenfd)는 서버가 클라이언트의 연결 요청을 수신하기 위해 사용되는 소켓입니다. 서버가 여러 클라이언트의 연결 요청을 동시에 처리할 수 있도록 항상 열려 있고, 새로운 연결 요청을 받아들이는 역할을 합니다.

  • 연결 식별자(connfd)는 서버가 특정 클라이언트와의 개별 연결을 관리하기 위해 생성하는 새로운 소켓입니다. 이 식별자는 각 클라이언트와의 통신을 독립적으로 처리할 수 있도록, 클라이언트마다 별도의 연결 식별자가 할당됩니다. 이를 통해 서버는 여러 클라이언트와 동시에 통신할 수 있습니다.

따라서, 듣기 식별자는 새로운 연결을 대기하는 데 사용되고, 연결 식별자는 개별 클라이언트와의 통신에 사용되므로, 이 둘을 구분하는 것이 중요합니다.


11.4.7 호스트와 서비스 변환

getaddrinfo 함수

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
    
int getaddrinfo(const char *host,     // e.g. "www.example.com" or IP
                const char *service,  // e.g. "http" or port number
                const struct addrinfo *hints,
                struct addrinfo **result);

▲ getaddrinfo가 반환하는 자료구조


getaddrinfo 함수는
host랑 service가 주어지면,
getaddrinfo는 연결 리스트를 가리키는 포인터를 result에 반환한다.
연결 리스트는 addrinfo 구조체를 담고 있다.
이 구조체는 host와 service에 대응되는 소켓 주소 구조체를 가리키고 있다.

  1. host : 도메인 이름 (www.naver.com) or 숫자 주소 (12.23.xx.xx)
  2. service : 서비스 이름 (http) or 포트 번호
  3. hints : 써도 되고 안 써도 됨. 결과를 세부 조정할 수 있음.

클라이언트가 getaddrinfo를 호출하면 socket과 connect 호출이 성공하고, 연결이 성립될 때까지 이 리스트를 방문하면서 각 소켓 주소를 확인한다. 메모리 누수 피하려면 freeaddrinfo를 호출해서 이 리스트를 반환해야 함.

  • host와 service 둘 중 하나는 NULL이어도 됨. 둘 다면 안됨.
  • 기본적으로 getaddrinfo는 IPv4와 IPv6 소켓 주소를 리턴
  • ai_family를 AF_INET으로 설정하면 리스트가 IPv4로, AF_INET6로 설정하면 리스트가 IPv6으로 제한됨.
  • getaddrinfo는 최대 3개의 addrinfo 구조체를 리턴할 수 있음. 각각은 서로 다른 ai_socktype 필드를 가짐. (하나는 데이터 그램, 하나는 원시 소켓을 위한 필드임) ai_socktype을 SOCK_STREAM으로 설정하면 구조체를 1개로 제한함.

getaddr이 결과 리스트에서 addrinfo 구조체를 생성할 때, ai_flags를 제외한 모든 필드를 채워줌.
ai_addr : 소켓 주소 구조체
ai_addrlen : 소켓 주소 구조체의 크기
ai_next_field : 해당 리스트의 다음 addrinfo 구조체


getnameinfo 함수

getnameinfo 함수는 getaddrinfo의 반대임.
소켓 주소 구조체가 주어지면, 호스트와 서비스 이름으로 바꿈.

  1. sa : salen 바이트의 소켓 주소 구조체
  2. host : hostlen 바이트 버퍼
  3. service : servlen 바이트 버퍼
  4. flags : 비트 마스크. 기본 동작 수정.

11.4.8 소켓 인터페이스를 위한 도움함수들

getaddrinfo 함수와 소켓 인터페이스들은 좀 어려움.
그래서 통신할 때 이 함수들을 감싼 open_clientfd와 open_listenfd를 만들어서 사용하면 편함.

!!! 주의 !!!
이 함수들은 내장 함수가 아니라 이렇게 감싸서 사용하면 된다는 느낌으로 책에서 예제 코드를 준 것임.

open_clientfd

int open_clientfd(char *hostname, char *port)

클라이언트는 open_clientfd를 호출해서 서버와 연결을 설정함.

open_clientfd는 호스트 hostname에서 실행됨.
연결 요청을 포트번호 port에서 받는 서버와 연결함.
Unix I/O 함수를 이용해서 입출력할 수 있는 소켓 식별자를 반환함.

open_listenfd

int open_listenfd(char *port)

서버는 open_listenfd를 호출해서 port에서 연결 요청을 받을 듣기 식별자를 생성함.

open_listenfd는 getaddrinfo를 호출해서 결과 리스트를
socket과 bind로의 호출이 성공할 때까지 탐색함.
setsockopt 함수를 이용해서 서버가 종료되고, 재시작되고, 즉시 연결 요청을 받아들이기 시작함.
(재시작된 서버는 기본적으로 클라이언트로부터의 연결 요청들을 거절할 것이며, 이는 디버깅을 심각하게 어렵게 함)

AI_PASSIVE 플래그와 NULL host 인자로 getaddrinfo를 호출했기 때문에 각 소켓 주소 구조체의 주소 필드는 와일드카드 주소로 설정되며, 커널은 서버가 호스트에 대한 모든 IP 주소에 대해 요청을 받으라고 설정함.

마지막으로 listen 함수를 호출해서 listenfd를 듣기 식별자로 변환하고 호출자에게 반환함.
실패하면 끝내기 전에 식별자를 닫아서 메모리 누수를 회피함.


책 나머지 내용이 전부 예제 코드여서 개념으로만 정리하기 좀 애매할듯.
아예 정글 컴퍼스에 있는 구현으로 넘어가자.

0개의 댓글