소켓 인터페이스
는네트워크
어플리케이션을 만들기 위한
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) 연결의 끝점
서버가 연결 요청을 수락할 때마다 생성, 서비스하는 동안에만 존재
리눅스는
getaddrinfo
와getnameinfo
라고 하는 강력한 함수들을 제공
소켓 인터페이스를 사용하는데 필요한 함수들의 인자나 구조체의 필드에 직접 전달
이진 소켓 주소 구조체들과 호스트이름, 호스트주소, 서비스이름, 포트번호들 사이에 앞뒤로 변환해준다.
특정 IP 프로토콜의 버전에 의존하지 않는
네트워크 프로그램 작성 가능하게 함
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 구조체다ai_flag
, ai_family
, ai_socktype
, ai_protocol
4개의 필드만 설정
memset
을 이용하여 전체 구조체를 0 또는 NULL
설정getaddrinfo
는 기본적으로 구조체를 생성할 때 ai_flag
를 제외
한 모든 필드를 채워준다소켓 주소 구조체
를 가르키는addrinfo 구조체의 연결 리스트
를 가르키는 result
를 출력
한다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 구조체를 가리킨다 */
};
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
를 SOCK_STREAM 설정
시 리스트가 각 고유의 주소에 대해
자신의 소켓 주소가 연결의 끝점으로 사용될
수 있는 최대 한 개의 addrinfo 구조체로 제한
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
바이트 길이의 버퍼
를 가르킨다.호스트
와 서비스 이름
스트링으로 변환
하고host와 service 버퍼로 복사
NI_NUMERICHOST
: host에서 숫자주소 스트링
을 리턴도메인 이름
을 리턴NI_NUMBERICSERV
: 이것을 세팅하면 etc/services 경로의 탐색과정을 생략하고 포트번호를 리턴
기본적으로 getnameinfo는
etc/services를 찾아가서 가능하면 포트번호 대신 서비스 이름을 리턴