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

Junyeong Fred Kim·2021년 12월 17일
1

4. 소켓 인터페이스

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

통신을 위한 끝점(End point of a connection). 각 소켓은 인터넷 주소와 16비트 정수 포트로 이루어진 소켓 주소를 가지며, address: port로 나타낸다. 소켓은 모든 운영체제에서 지원한다.

(데이터를 주고 받기 위해서 소켓 디스크립터(socket descriptor)라는 파일 시스템을 이용)

  • 클라이언트의 소켓 주소 내의 포트는 클라이언트가 연결 요청을 할 때 커널이 자동으로 할당한다.
  • 서버의 소켓 주소에 있는 포트는 대개 영구적으로 이 서비스에 연결되는 Well-known port이다.

예로 웹 서버는 대개 80을 사용하고 이메일 서버 포트는 25를 사용한다.

인터넷 연결의 구조



소켓 주소 구조체

리눅스 커널의 관점에서 보면 소켓은 통신을 위한 끝점이며, Unix 프로그램의 관점에서 보면 소켓은 해당 식별자를 가지는 열린 파일이다.
connect, bind, accept 함수는 프로토콜에 특화된 소켓 주소 구조체를 가리키는 포인터를 필요로 한다.

  • 소켓 주소 구조체는 다음과 같다.
  /* IP socket address structure */
  struct sockaddr_in  {
    uint16_t        sin_family;  /* Protocol family (always AF_INET) 2*/
    uint16_t        sin_port;    /* Port number in network byte order 2*/
    struct in_addr  sin_addr;    /* IP address in network byte order 4*/ 
    unsigned char   sin_zero[8]; /* Pad to sizeof(struct sockaddr) */
};
/* Generic socket address structure (for connect, bind, and accept) */
struct sockaddr {
	uint16_t  sa_family;    /* Protocol family */
	char      sa_data[14];  /* Address data  */
};

sockaddr_in 구조체를 sockaddr 구조체로 캐스팅할 필요가 있을 때는 아래와 같이 사용하면 된다.

// 소켓의 주소를 담는 기본 구조체 틀의 역할(socket.h에 정의되어 있음)
typedef struct sockaddr SA;

소켓 인터페이스 기반 네트워크 응용 프로그램의 개요는 아래의 그림과 같다.

socket(): 소켓(file descriptor) 생성하는 함수
bind(): 소켓과 서버의 정보 연결하는 함수(외부 호스트에서 서버 연결 요청 시, 소켓 디스크립터 번호 알아야 함)
listen(): 어떤 컴퓨터로부터 요청이 와도 수락할 수 있게 대기상태에 들어가도록 하는 함수
accept(): 서버 소켓과 클라이언트를 연결하는 함수
connect(): 서버에 연결 요청하는 함수. 서버가 바쁘면 대기열에 넣었다가 때가 되면 accept


호스트와 서비스 변환

  • getaddrinfo 함수

getaddrinfo함수는 도메인 주소값을 IP주소값으로 변환할 때 사용하는 함수다. host, service, hint값을 입력하여 result로부터 정보를 추출하는 구조이다.

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
    
int getaddrinfo(const char *host,               // (ex) "www.example.com" or IP
                const char *service,            // (ex) "http" or port number
                const struct addrinfo *hints,   
                struct addrinfo **result);      // DNS서버로부터 받은 네트워크 주소 정보(IP)를 돌려주는 output 매개변수
            //Returns: 0 if OK, nonzero error code on error

// 사용 후 메모리 해제(메모리 누수 방지)
void freeaddrinfo(struct addrinfo *result);

const char *gai_strerror(int errcode);
  • host에는 도메인 이름이나 정수형인 IP주소를 넣을 수 있다. 호스트 이름을 주소로 변환하고 싶지 않은 경우 이 자리에 NULL을 넣으면 된다.

  • service에는 서비스 이름이나 포트 번호를 넣으면 된다. 호스트와 마찬가지로 NULL값으로 설정할 수 있지만, host, service 둘 중 한 개는 명시되어야 한다.

  • addrinfo 구조체

struct addrinfo {
    int		       ai_flags;       // 기본 동작을 더 수정하는 비트마스크
    int		       ai_family;      // AF_INET -> IPv4, AF_INET6 -> IPv6
    int		       ai_socktype;    // TCP 경우 SOCK_STREAM
    int		       ai_protocol;    
    char  	       *ai_canonname;   
    size_t             ai_addrlen;     // 소켓 주소 구조체의 크기
    struct sockaddr    *ai_addr;        // 소켓 주소 구조체 가리킴
    struct addrinfo    *ai_next;        // 다음 addrinfo구조체 가리킴
};
  • hints 인자는 getaddrinfo가 반환하는 addrinfo 구조체들에 대한 요구사항이 있을 때 사용한다. hints인자 또한 addrinfo 구조체로 ai_family, ai_socktype, ai_protocol, ai_flags 필드만이 설정될 수 있으며 나머지 인자는 0 또는 NULL로 설정되어야 한다.
    예를 들어 hints.ai_family = AF_INET;, hint.ai_socktype = SOCK_STREAM;이 설정되었다면 getaddrinfo의 결과로 반환된 연결리스트의 addrinfo 구조체들은 모두 AF_INET, SOCK_STREAM의 조건에 해당하는 ipv4의 TCP 소켓이다.
  • result 인자는 조건에 맞는 addrinfo 구조체들의 연결리스트가 들어갈 주소값이다.

getaddrinfo의 가장 큰 장점 중 하나는 addrinfo의 필드들이 추가적인 조작 없이 소켓 인터페이스에서 함수들로 직접 전달될 수 있다는 것이다.
이전 포스트에서 설명했던 connet와 bind 함수에 addrinfo의 ai_family, ai_socktype, ai_protocol 인자는 직접 전달될 수 있다.


  • getnameinfo 함수

getnameinfo 함수는 getaddrinfo 함수의 역이다. 이 함수는 소켓 주소 구조체를 대응되는 호스트와 서비스 이름 스트링으로 변환한다.


int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *service, size_t servlen, int flags);
	
    성공시 0 반환, 실패시 0이 아닌 에러코드 반환

sa인자는 salen 바이트 크기의 소켓 주소 구조체가 들어간다. host는 hostlen 크기의 버퍼, service는 servlen 크기의 버퍼가 들어간다.
함수의 실행이 성공하면 소켓 주소 구조체 sa를 대응되는 호스트와 서비스 이름 스트링으로 변환하고, 이들을 host와 service 버퍼로 복사한다.
호스트 이름을 원하지 않거나 서비스 이름을 원치 않는다면 둘 중 하나는 NULL로 설정이 가능하다.
flags 인자는 비트 마스크로 기본 동작을 수정한다.
flags = NI_NUMERICHOST를 설정한다면 기본값으로 host를 도메인 이름으로 반환하는 것 대신 ip주소 스트링을 대신해서 반환한다.
NI_NUMERICSERV를 설정한다면 서비스 이름 대신 포트 번호를 반환한다.

profile
기억보다 기록

0개의 댓글