네트워크 프로그래밍 - 소켓 인터페이스

Jocy·2022년 5월 19일
0
post-thumbnail

소켓 인터페이스

소켓 인터페이스네트워크 어플리케이션을 만들기 위한
Unix I/O 함수들과 함께 사용되는 함수들의 집합

소켓 인터페이스 기반 네트워크 응용프로그램 개요

소켓

소켓인터넷 주소16비트 정수 포트로 구성된
소켓 주소를 address : port로 나타내고 연결의 종단점이다.
클라이언트의 소켓 주소 내의 포트는 클라이언트가 연결 요청할 때 커널이 자동으로 할당한다

소켓 주소 구조체

리눅스 커널의 관점에서는 소켓은 통신을 위한 끝점
Unix 프로그램 관점에서는 소켓은 해당 식별자를 가지는 열린 파일

인터넷 소켓 주소는 위의 그림(11.13)과 같이 sockaddr_in 타입16바이트 구조체에 저장
각 필드 sin_family(AF_INET), sin_port(16비트 포트번호), sin_addr(32비트 IP주소),
IP주소와 포트번호는 항상 네트워크 바이트(빅 엔디안) 순서로 저장

connect(), bind(), accept()는 프로토콜에 특화된 소켓 주소 구조체를 가리키는 포인터를 필요

소켓 인터페이스를 설계한 사람들이 당면한 문제는 어떻게 이 함수들을 정의해서
어떤 종류의 소켓 주소 구조체라도 받아 들일 수 있도록 하는가 였다.
오늘날 우리는 포괄적인 void * 포인터를 사용한다.

이 문제에 대한 해결책으로 소켓 함수를 원천 sockaddr 구조체로의 포인터를 기대하도록 하고
그 후에 어플리케이션프로토콜에 특화된 구조체로의 모든 포인터
일반적인(Generic) 구조체로 캐스팅하도록 정의 하였다.

typedef struct sockaddr SA; // 일반적인 sockaddr 구조체

sockaddr_in 구조체일반적인 sockaddr 구조체캐스팅할 필요가 있을 때마다 이 타입 사용

소켓 인터페이스에 사용되는 함수

  • 1) socket 함수 : 클라이언트와 서버는 소켓 식별자 생성하기 위해서 사용

  • 2) connect 함수 : 클라이언트가 서버와 연결을 수립하기 위해 사용

  • 3) bind 함수 : sockaddr 구조체 addr에 있는 서버의 소켓 주소소켓 식별자 sockfd와 연결

  • 4) listen 함수 : 기본적으로 커널은 socket함수만든 식별자
    한 연결의 클라이언트 쪽 끝에서 존재하는 능동소켓에 대응
    서버listen 함수호출해서 이 식별자를 클라이언트 대신에 서버가 사용하게 될 것이라고 알려줌
    sockfd를 능동 소켓에서 listenfd 듣기 소켓으로 변환
    backlog인자는 커널이 요청들을 거절하기 전에 큐에 저장해야 하는 연결의 수에 대한 정보 제공

  • 5) accept 함수 : 클라이언트로부터의 연결 요청듣기 식별자(listenfd)에 도달하기를 기다린다.
    addr 내에 클라이언트 소켓 주소를 채우고 Unix I/O 함수들을 사용해서
    클라이언트와 통신하기 위해 사용될 수 있는 연결식별자(connfd) 리턴한다.
    듣기 식별자는 클라이언트의 연결 요청의 끝점, 서버가 살아있는 동안 존재
    연결 식별자는 클라이언트와 서버 사이에 성립된(established) 연결의 끝점
    서버가 연결 요청을 수락할 때마다 생성, 서비스하는 동안에만 존재

호스트와 서비스 변환

리눅스는 getaddrinfogetnameinfo 라고 하는 강력한 함수들을 제공
소켓 인터페이스를 사용하는데 필요한 함수들의 인자나 구조체의 필드에 직접 전달
이진 소켓 주소 구조체들과 호스트이름, 호스트주소, 서비스이름, 포트번호들 사이에 앞뒤로 변환해준다.
특정 IP 프로토콜의 버전에 의존하지 않는 네트워크 프로그램 작성 가능하게 함

📍getaddrinfo 함수

int getaddrinfo(const char *host, // host와 service중에 주소로 변환하고 싶지 않으면 NULL  
				const char *service, // 대신 host와 service중 1개만
                const struct addrinfo *hints, // 선택적으로 사용하는 인자
                struct addrinfo **result); // 연결되면 함수는 0을 리턴, 실패하면 0이 아닌 에러코드를 반환

void freeaddrinfo(struct addrinfo *result); // 연결되면 메모리 누수를 피하기 위해 연결리스트 result를 삭제

const char *gai_strerror(int errcode); // 에러코드를 반환시 에러 메시지를 문자열로 변환
  • host 인자도메인 이름이거나 숫자 주소
  • service 인자서비스이름(ex: http)이거나 십진수 포트번호
  • host 인자service인자의 이름을 주소로 변환하고 싶지 않을 때 NULL로 설정이 가능
    그러나 둘중에 하나는 명시 되어야 한다.
  • hints 인자를 사용시 보다 상세한 제어를 제공하는 addrinfo 구조체다
    hints 인자가 전달될 때 ai_flag, ai_family, ai_socktype, ai_protocol 4개의 필드만 설정
    다른 필드는 memset을 이용하여 전체 구조체를 0 또는 NULL 설정
  • getaddrinfo는 기본적으로 구조체를 생성할 때 ai_flag제외한 모든 필드를 채워준다
  • 소켓 주소의 2개의 구성요소(host, service)가 주어지면
    그것에 대응이 되는 소켓 주소 구조체를 가르키는
    addrinfo 구조체의 연결 리스트를 가르키는 result출력한다
  • result에 3개의 addrinfo 구조체의 연결리스트를 반환
  • 클라이언트는 getaddrinfo 를 호출하고 반환된 연결리스트 result를 탐색해서
    각 소켓 주소를 socket과 connect로의 호출이 성공하고 연결이 성립(established)할 때 까지 시도합니다.
  • 서버는 socket과 bind 로의 호출이 성공하고 식별자(descriptor)가 유효한 소켓 주소에 연결(bound)될 때까지 시도합니다.

📍getaddrinfo가 사용하는 addrinfo 구조체

struct addrinfo {
	/* hints 인자가 전달될 때 상위 4개의 필드만 설정, 다른 필드는 0 또는 NULL 설정 */
    int ai_flags;             /* Hints argument flags */
	int ai_family;            /* IPv4(AF_INET) 또는 IPv6(AF_INET6) 주소로 제한 */
	int ai_socktype;          /* SOCK_STREAM 설정 시  */
	int ai_protocol;          /* Third arg to socket function  */

	size_t ai_addrlen;        /* 소켓 구조체의 크기를 알려줌 */

	char *ai_canonname;       /* 정식 호스트네임, Canonical hostname */
	struct sockaddr *ai_addr; /* 소켓 주소 구조체를 가르킨다 */
	struct addrinfo *ai_next; /* 다음 addrinfo 구조체를 가리킨다 */
};

addrinfo 구조체 내의 필드 설정

ai_flags 필드

  • AI_ADDRCONFIG : 연결을 사용하려고 하면 추천
    getaddrinfo가 로컬 호스트가 IPv4(IPv6)설정된 경우에만 IPv4(IPv6) 주소를 리턴

  • AI_CANONNAME : 기본적으로 ai_canonname 필드NULL 이다.
    이 플래그가 설정되면 host의 공식이름으로의 리스트에서 첫번째 addrinfo 구조체에서의
    ai_canonname 필드를 가리키도록 지시

  • AI_NUMERICSERV : 해당 플래그 사용시 service인자포트번호여야 한다.

  • AI_PASSIVE : getaddrinfo는 클라이언트가 connect를 호출할 때 활성화된 소켓으로 이용할 수 있는 소켓 주소를 리턴하게 해준다. host 인자NULL 이고 결과로 얻는 것은 소켓 주소 구조체에서 주소 필드는 와일드 카드 주소가 된다.
    커널에게 이 서버는 자신의 호스트들에게 들어오는 모든 IP 주소로의 요청들을 수락할 것이라고 알려준다.

ai_socktype 필드

ai_socktypeSOCK_STREAM 설정 시 리스트가 각 고유의 주소에 대해
자신의 소켓 주소가 연결의 끝점으로 사용될 수 있는 최대 한 개의 addrinfo 구조체로 제한

📍getnameinfo 함수

int getnameinfo(const struct sockaddr *sa, socklen_t salen,
                char *host, size_t hostlen,
                char *service, size_t servlen, int flags);
  • 소켓 주소 구조체를 대응되는 호스트, 서비스이름 스트링으로 변환한다
  • 이 함수는 재진입이 가능, 프로토콜이 독립적이다.
  • sa 인자salen 바이트 길이의 소켓 주소 구조체
    host 인자는 hostlen 바이트 길이의 버퍼
    service 인자는 servlen 바이트 길이의 버퍼를 가르킨다.
  • 이 소켓 주소 구조체 sa를 대응되는 호스트서비스 이름 스트링으로 변환하고
    host와 service 버퍼로 복사
  • 호스트이름을 원치 않는다면 host인자를 NULL로 설정

getnameinfo 함수의 flags 인자

  • NI_NUMERICHOST : host에서 숫자주소 스트링을 리턴
    기본적으로 getnameinfo는 host에서 도메인 이름을 리턴
  • NI_NUMBERICSERV : 이것을 세팅하면 etc/services 경로의 탐색과정을 생략하고 포트번호를 리턴
    기본적으로 getnameinfo는 etc/services를 찾아가서 가능하면 포트번호 대신 서비스 이름을 리턴

참고자료
책 CSAPP
TCP/IP Programming.pdf (SWjungle 내부자료)

profile
Software Engineer

0개의 댓글