jun-wiki

View My GitHub Profile

Posts (Latest 10 updated) :
Read all
Contents:
  1. 11.4 소켓 인터페이스 (The Sockets Interface)
    1. 11.4.1 소켓 주소 구조체 (Socket Address Structures)
    2. 11.4.2 socket 함수 (The socket Function)
    3. 11.4.3 connect 함수 (The connect Function)
    4. 11.4.4 bind 함수 (The bind Function)
    5. 11.4.5 listen 함수 (The listen Function)
    6. 11.4.6 accept 함수 (The accept Function)
    7. 11.4.7 호스트와 서비스 변환 (Host and Service Conversion)
      1. getaddrinfo 함수
      2. getnameinfo 함수:
    8. 11.4.8 소켓 인터페이스 헬퍼 함수 (Helper Functions for the Sockets Interface)
    9. 11.4.9 에코 클라이언트/서버 예제 (Example Echo Client and Server)

csapp 이어서 하겠다

개같은 거 졸라게 많다

아무래도 이것저것 고유명사 비스무리한게 많아

하나하나 다 정리해가며 짚어야 오히려 이해하기 편할 거 같다

11.4 소켓 인터페이스 (The Sockets Interface)

소켓 인터페이스는 프로그래머가 네트워크 통신을 구현하기 위해 사용하는 표준 API1


11.4.1 소켓 주소 구조체 (Socket Address Structures)

소켓 함수들은 프로토콜에 따라 다른 주소 체계를 지원해야 하므로, 이를 위해 소켓 주소를 표현하는 공용 구조체를 사용

  • IPv4 구조체: sockaddr_in
    16바이트 크기

    • 주소 패밀리: sin_family
      IPv4 주소체계라는 의미

    • 16비트 포트 번호: sin_port

    • 32비트 IP주소: sin_addr


  • 일반 주소 구조체: struct sockaddr
    껍데기 구조체 (주소 데이터 담으려 만든게 아님)

    • 소켓 주소 구조체 포인터: struct sockaddr *
      void없었을때의 잔여, 매개변수 역할을 한다
      자세히


  • 주소 창고: sockaddr_storage
    IPv4, IPv6 등 여러 종류의 소켓 주소 저장하기 위한 큰 공용 구조체다
    클라이언트 주소 받을때 struct sockaddr_storage clientaddr;으로 선언하면 IPv4/IPv6에 상관없이 쓸 수 있다
    이 구조체는 필요 시 sockaddr *로 캐스팅해 사용한다


  • 커널 vs 프로그램:
    커널에선 소켓을 통신의 끝점으로 보지만
    사용자 프로그램에선 단순 파일 디스크립터2 취급한다



11.4.2 socket 함수 (The socket Function)

socket() 함수는 새 소켓 생성해 파일 디스크립터(fd) 를 반환한다

소켓 생성 기 통신에 사용할 도메인(domain), 타입(type), 프로토콜(protocol) 을 지정한다

  • 함수 원형: int socket(int domain, int type, int protocol); 으로 정의
    성공 시 소켓 디스크립터 반환
    이는 이후 I/O 함수에 사용 가능


  • 도메인 (주소 체계):
    첫 번째 인자는 주소 체계 지정

    • AF_INET: IPv4
    • AF_INET6:IPv6
    • AF_UNIX: 로컬 유닉스 도메인


  • 소켓 타입:
    두 번째 인자는 통신의 특성을 지정

    • SOCK_STREAM은 TCP처럼 연결 지향적이며 신뢰적인 바이트 스트림을 의미

    • SOCK_DGRAM은 UDP처럼 비연결성3 데이터그램4 방식을 의미


  • 프로토콜:
    세 번째 인자는 특정 프로토콜 지정하는 값이나, 보통 하나의 도메인-타입 조합에 가능한 프로토콜이 하나뿐이므로 0을 주어 기본 프로토콜 선택을 사용


소켓은 초기에 완전히 열린 상태가 아니라 바로 데이터 읽기/쓰기가 불가능하다
클라이언트의 경우 connect() 호출을 통해 연결해야 하고
서버의 경우 bind()로 주소를 지정하고 listen()으로 대기 상태로 만들어야한다

프로토콜 독립성:
소켓 만들 때에 AF_INET등을 넣는 대신에
getaddrinfo 같은 함수를 활용하여 도메인과 타입을 동적으로 얻어내는 것이 권장된다

이를 통해 IPv6 등도 지원 가능케 된다



11.4.3 connect 함수 (The connect Function)

클라이언트 측에서 사용하며 연결을 요청한다
호출 시 블로킹되어, 상대 서버와의 연결이 완전히 설정되거나 에러가 발생할 때까지 반환하지 않는다
연결에 성공하면 소켓은 통신 가능한 상태(established state) 가 되고, 이후 해당 소켓 디스크립터를 통해 데이터를 주고받을 수 있다
실패할 경우 -1을 반환하며 errno에 에러 정보를 남긴다


  • 함수 원형: int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 형식
    반환값은 성공시 0, 실패시 -1이다

    • sockfdsocket()로 생성한 클라이언트 소켓

    • addr서버의 소켓 주소

    • addrlen은 주소 구조체의 크기


  • 매개변수 준비:
    addr (주소)인자는 보통 서버의 IP와 포트를 설정한 뒤 그 포인터를 캐스팅한 것이다
    이때 IP 문자열을 이진수로 변환(inet_pton)하거나 DNS이름을 IP로 해석 (getaddrinfo)하는 선행 작업 필요



11.4.4 bind 함수 (The bind Function)

bind() 함수는 서버 측에서 사용
생성된 소켓에 로컬 주소(아이피와 포트)를 할당
이를 통해 자신의 소켓이 특정 포트 번호를 수신 대기하도록 커널에 알리고, 필요하면 네트워크 인터페이스(특정 IP나 모든 IP)에 바인드할 수 있다
없이 listen()하면 임의의 포트 할당하지만
서버는 클라가 아는 고정 포트 사용해야 하므로 bind() 호출 필수적

  • 함수 원형: int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    정상 수행 시 0, 실패시 -1 반환

    • sockfd는 서버가 생성한 소켓

    • addr은 바인드할 자신의 주소

    • addrlen은 주소 구조체 크기


  • 실패하는 경우:
    주로 포트 충돌 시 실패한다
    포트 하나당 소켓 하나만 바인드 가능하기에 말이다
    혹은, 1024의 포트에 일반 사용자 권한으로 바인드 하려 할시에도 권한 없어 실패한다



11.4.5 listen 함수 (The listen Function)

bind()까지 마친 서버 소켓을 수동 대기 모드(passive listen) 로 전환한다
이를 통해 해당 소켓은 클라이언트의 연결 요청을 받을 수 있응 상태가 됨
listen()을 호출하지 않을 시 클라이언트처럼 간주되어 반드시 listen()을 실행해야함

  • 함수 원형: int listen(int sockfd, int backlog);
    반환 성공시 0, 실패시 -1

    • sockfd는 이전에 바인드한 소켓 디스크립터

    • backlog는 커널 접속 대기 큐의 최대 길이 힌트


  • backlog 매개변수:
    커널에게 몇 개의 연결 요청을 큐에 보관할지 알려주는 힌트 값이다
    힌트인 이유는 길이 정하는 건 커널이기에 참고만 하기에 그렇다



11.4.6 accept 함수 (The accept Function)

accept() 함수는 서버에서 사용되며, 대기 중인 연결 요청을 하나 수락하여 새로운 소켓(fd) 을 생성
일반적으로 블로킹 모드로 호출되며,
클라이언트의 연결 시도가 대기열에 없으면 해당 소켓에서 연결이 들어올 때까지 프로세스를 수면(sleep) 시킨다
accept()가 반환하는 새 소켓은 특정 클라이언트와 연결된 개별 통신 채널이며
기존의 listening 소켓은 계속 남아 다른 요청을 기다린다

  • 함수 원형: int accept(int listenfd, struct sockaddr *addr, socklen_t *addrlen);
    오류시 -1반환, 정상 시 새로 연결된 소켓 fd 반환

    • listenfdlisten() 중인 소켓 디스크립터

    • addr는 클라이언트 주소 정보를 받을 버퍼5

    • addrlen은 입력으로 버퍼 크기, 출력으로 실제 주소 구조체 크기를 얻는 포인터


  • 동작:
    accept() 호출 시 커널은 listen 대기열에서 완료된 연결(TCP의 3-way handshake가 끝난 것)을 하나 꺼내온다
    없다면 연결이 생길 때까지 기다린다
    가져온 연결에 대해 새 소켓 구조체를 커널 내에 생성하고, 그 소켓에 대한 fd를 반환한다
    동시에 addr 버퍼에 클라이언트의 소켓 주소(IP:포트)를 써준다



11.4.7 호스트와 서비스 변환 (Host and Service Conversion)

문자열 형태의 호스트명/서비스명이진 소켓 주소 구조체 간 변환을 도와주는 함수에 대한 내용이다
이들은 재진입 가능(reentrant) 하고 프로토콜 독립적이다

getaddrinfo 함수

호스트와 서비스 문자열을 소켓 주소로 변환한다

// 원형:
int getaddrinfo(const char *host, const char *service,
            const struct addrinfo *hints,
            struct addrinfo **result);

성공 시 0을 반환하고 결과로 addrinfo 구조체 연결리스트의 첫 노드 포인터를 result에 담아준다
사용후에 freeaddrinfo(result)로 메모리를 해제해야 한다

  • 호스트/서비스 인자:
    host인자는 도메인 이름, 숫자 IP문자열, NULL을 줄 수 있다
    service인자는 서비스 이음, 포트 번호 문자열, NULL을 줄 수 있다
    단, 둘 다 NULL주면 안됨


  • struct addrinfo:
    getaddrinfo가 반환하는 연결 리스트의 각 노드는 addrinfo 구조체 타입이다

  • 주요 필드:

    • ai_family(주소 패밀리)
    • ai_socktype(소켓 타입)
    • ai_protocol(프로토콜)
    • ai_addr(소켓 주소 구조체 포인터)
    • ai_addrlen(그 크기)
    • ai_canonname(호스트의 공식 이름)


hints 활용:

hints 인자는 getaddrinfo의 결과를 필터링하는 데 사용된다

  • ai_familyAF_INET 또는 AF_INET6로 지정하면 IPv4 또는 IPv6 결과로 제한할 수 있고, AF_UNSPEC (0)으로 두면 제한없이 둘 다 가능하다

  • ai_socktypeSOCK_STREAM으로 설정하면 TCP용 주소만 (연결형 소켓만) 얻고, 설정하지 않으면 UDP용까지 포함한 여러 항목이 나올 수 있다

  • ai_flags에는 여러 상수를 OR해 설정할 수 있다:

    • AI_PASSIVE: 호스트가 NULL일 때 와일드카드 주소를 반환하도록 함
      주로 서버에서 사용하며, 모든 로컬 IP에 바인드하기 위해 사용

    • AI_ADDRCONFIG: 로컬 호스트가 IPv4로 설정된 경우 IPv4 주소만, IPv6이면 IPv6 주소만 반환하도록 함
      (사용하지 않는 프로토콜의 주소는 제외)

    • AI_CANONNAME: 첫 결과의 ai_canonname에 호스트의 정규화된 공식 이름을 채워주도록 함

    • AI_NUMERICHOST: 호스트 문자열을 도메인 이름으로 보지 말고 숫자 주소로만 해석하게 함
      (DNS 조회 안 함)

    • AI_NUMERICSERV: 서비스 문자열을 이름으로 보지 않고 숫자로만 해석하게 함



getnameinfo 함수:

소켓 주소 → 호스트/서비스명 문자열 변환 함수

// 원형:
int getnameinfo(const struct sockaddr *sa, socklen_t salen,
               char *host, size_t hostlen,
               char *service, size_t servlen, int flags);

sa에 소켓 주소, salen에 그 크기, hostservice 버퍼 및 길이를 주면 대응되는 호스트 이름과 서비스 이름을 넣어준다

성공 시 0, 실패 시 0이 아닌 에러코드
hostserviceNULL을 전달하거나 길이를 0으로 주면 해당 변환 스킵한다


getnameinfo 플래그

기본적으로 호스트 이름을 반환하기 위해 DNS 조회를 할 수도 있고,
서비스 이름은 /etc/services 참조하여 알려진 이름을 줄 수도 있다
옵션 플래그로 이를 제어한다:

  • NI_NUMERICHOST: 무조건 숫자 형식의 호스트 주소 문자열을 반환 (예: “93.184.216.34”)

  • NI_NUMERICSERV: 무조건 숫자 형식의 포트 번호 문자열을 반환 (예: “8080”)

  • NI_NOFQDN, NI_NAMEREQD 등의 추가 플래그도 존재한다 (표준 참고)



11.4.8 소켓 인터페이스 헬퍼 함수 (Helper Functions for the Sockets Interface)

별 거 아니고 CSAPP저자들이 만든 소켓 프로그래밍용 함수다
open_clientfd는 클라이언트 측에서 호스트와 포트로 쉽게 연결하는 함수이며,
open_listenfd는 서버 측에서 포트 번호 하나로 간단히 듣기 소켓을 열어준다

  • open_clientfd 함수:
    int open_clientfd(char *hostname, char *port);
    성공 시 소켓 fd, 실패 시 -1 리턴

    • 호스트와 포트 넣어주면 연결된 소켓 디스크립터 준다


  • open_listenfd 함수:
    int open_listenfd(char *port);

    • 포트 넣어주면 리스닝 소켓(listening descriptor)을 열어준다



11.4.9 에코 클라이언트/서버 예제 (Example Echo Client and Server)

서버 예제 나온다

  • 에코 클라이언트 동작:
    에코 클라이언트는 사용자 입력 -> 서버 송신 -> 서버로부터 같은 내용 수신 -> 출력 의 동작을 반복

    1. 연결 단계:
      프로그램 시작 시 명령줄 인자로 호스트 이름과 포트 번호를 받아, open_clientfd(host, port)를 호출하여 해당 서버에 연결

    2. 입출력 설정:
      표준 입출력의 편의를 위해 CSAPP의 RIO 버퍼를 초기화한다 (Rio_readinitb(&rio, clientfd) 호출)

    3. 요청/응답 루프:
      읽고fgets
      전송Rio_writen(clientfd, buf, strlen(buf))하고
      읽어Rio_readlineb와 출력을 입력 종료(EOF)까지 반복

    4. 종료:
      close(clientfd)로 서버와의 연결을 닫는다


  • 에코 서버 동작: (iterative server, 접속 순차 처리)

    1. 준비 단계:
      open_listenfd(port)를 호출함으로써 해당 포트에서 듣기 소켓(listenfd) 을 연다
      성공 시 반환되는 listenfd를 가지고 while(1) 무한 루프에 들어간다

    2. 연결 수락:
      루프 안에서 Accept(listenfd, (SA *)&clientaddr, &clientlen)으로 클라이언트 접속을 기다리다가 어떤 클라이언트가 접속하면 새로운 connfd를 반환
      반환과 동시에 clientaddr에는 상대의 주소가 채워짐
      서버는 Getnameinfo 함수를 이용해 clientaddr로부터 클라이언트의 호스트 이름과 포트 번호 문자열을 얻어 로그를 출력

    3. 서비스 처리:
      echo(connfd) 함수를 호출하여, 해당 연결에 대한 에코 서비스를 수행
      echo 함수는 인자로 넘긴 connfd 소켓에 대하여
      EOF가 나올 때까지 Rio_readlineb로 한 줄을 읽고 그대로 Rio_writen으로 돌려주는 작업을 반복

    4. 정리:
      echo 함수가 끝나면 서버는 해당 클라이언트와의 connfdClose(connfd)하여 연결을 닫는다
      이후 루프가 돌며 다시 accept()로 다음 클라이언트를 기다린다

    • EOF 처리:
      에코 서버의 echo 함수에서 Rio_readlineb0을 리턴하면, 이는 클라이언트 쪽에서 EOF(연결 닫힘) 를 보낸 것을 뜻한다
      이때 서버는 루프를 빠져나와 함수를 종료하고, 상위 함수에서 소켓을 닫는다






서로 다른 소프트웨어들이 통신하고 데이터를 교환할 수 있도록 하는 규칙과 프로토콜의 집합

데이터를 전송하기 전에 미리 연결을 설정하는 과정(호 설정)이 필요 없다

데이터가 데이터그램이라는 독립적인 패킷 단위로 분할되어 전송

  1. 애플리케이션 프로그래밍 인터페이스(Application Programming Interface)
     

  2. 특정한 파일에 접근하기 위한 추상적인 키 

  3. 비연결성 (Connectionless):
     

  4. 데이터그램 (Datagram):
     

  5. 한 곳에서 다른 곳으로 데이터를 이동할 때 임시적으로 그 데이터를 저장하기 위해 사용되는 물리적인 메모리 저장소의 영역