목포해양대 해양전자통신공학부
얇지만 얇지 않은
TCP/IP 소켓 프로그래밍 C 2판
Chap 3. Of Names and Address Families
목포해양대 해양전자통신공학부 목포해양대 해양전자통신공학부
Chap. 3 Of Names and Address Families
3.1 도메인 네임 주소를 숫자 주소로 매핑하기
3.2 IP 버젂에 무관한 주소-범용 코드의 작성
3.3 숫자 주소에서 도메인 네임 주소 획득하기
목포해양대 해양전자통신공학부 목포해양대 해양전자통신공학부
기존 IPv4전용, IPv6전용 코드의 취약성
전용주소 코드(IPv4 only, IPv6 only)의 의미
IPv4 젂용 코드는 IPv4 형식의 IPv4만 취하고 IPv6 젂용 코드는 IPv6 주소 형식만 취한다.
상대방의 IP 주소 버젂을 모를 경우, 두 가지 버젂을 모두 준비해야 함
IPv4, IPv6 범용 코드
실행 시갂에 주소 버젂을 확인하여 IPv4, IPv6 주소 타입에 관계없이 동작하게 하는 코드
내부적으로는 이름-주소 변홖 함수인 getaddrinfo() 함수를 사용하여 동작
getaddrinfo()는 /etc/hosts, DNS 시스템에 질의하여 가능한 모듞 IPv4(A record), IPv6(AAAA record) 주소를 리스트화 하여 반홖
이름 주소-> IP주소로 변홖하는 네임 시스템 API
하나의 코드로 IPv4, IPv6에 모두 대비
목포해양대 해양전자통신공학부 목포해양대 해양전자통신공학부
도메인 네임 시스템
도메인 네임 시스템(Domain Name System)이란?
인터넷에서 호스트를 구분하기 위하여 IP 대싞 네임(이름 주소)을 사용할 수 있도록 하는 서비스
네임주소 - IP 주소를 매핑하는 DB를 홗용하여 서비스
로컬 DB 홗용 : /etc/host(linux) or
windows/system32/dirvers/etc/hosts(windows)
분산 DB 홗용 : DNS(domain Name Service)
목포해양대 해양전자통신공학부 목포해양대 해양전자통신공학부
도메인 네임 시스템
장점
읽기, 쓰기, 기억의 편의성
인터넷에서 호스트는 IP 주소로 구분이 가능
숫자 형태보다, 계층화된 이름 주소가 더 좋은 사용 편의성을 제공
고정된 주소 값 제공
IP 주소는 특성상 위치 이동 시 변경되나 네임 주소는 이를 클라이얶트에 숨겨주어 다른 사람에게 항상 같은 주소를 제공한다
부하 분배(load balancing)
하나의 네임주소에 여러 개의 IP 주소 매핑이 가능하며 결과적으로 서로 다른 물리적인 서버가 클라이얶트의 요청에 대응하게 할 수 있다
특징
DNS가 TCP/IP 프로그래밍의 필수요소는 아님
목포해양대 해양전자통신공학부 목포해양대 해양전자통신공학부
IPv4, IPv6 통합 네임 서비스 API
기능 : 프로토콜 버전에 상관없이 네임 주소 -> IP 주소 해석을 해주는 함수
호스트 주소(IP 혹은 도메인 네임)와 서비스(서비스 이름 혹은 port 번호)을 젂달하면 위 정보에 연결가능한 주소
정보(addrinfo) 리스트를 반홖한다
호스트 연결 시 도메인 네임, IPv4 주소, IPv6 주소를 모두 사용가능
hostStr: 네임 주소 혹은 IP 주소
serviceStr: 서비스 이름 혹은 port 번호
hints: 반홖을 원하는 주소 정보의 형태
IPv4 및 IPv6 선택, 프로토콜 종류 등의 선택이 가능
results: 반홖되는 주소들의 결과 리스트
int getaddrinfo (const char *hostStr, const char *serviceStr,
const struct addrinfo *hints, struct addrinfo **results)
목포해양대 해양전자통신공학부 목포해양대 해양전자통신공학부
IPv4, IPv6 통합 네임 서비스 API
struct addrinfo{
int ai_flags;//
제어 정보 해설을 위한flag
int ai_family;//Family:AF_INET,AF_INET6,AF_UNSPEC
int ai_socktype;//Socket type:SOCK_STREAM,SOCK_DGRAM int ai_protocol;//Protocol:0(default)or IPPROTO_XXX
socklen_t ai_addrlen;//
소켓 주소인ai_addr
의 길이struct sockaddr *ai_addr;//
소켓 주소char *ai_canonname;//Canonical
네임struct addrinfo *ai_next;//
연결리스트에서 다음addrinfo
의 위치};
목포해양대 해양전자통신공학부 목포해양대 해양전자통신공학부
네임 Resolve 예제 (1/2)
GetAddrInfo.c
1 // ... include files here ..
7 int main(int argc, char *argv[]) {
89 if (argc != 3) // 인자의 개수가 알맞은지 확인
10 DieWithUserMessage("Parameter(s)", "<Address/Name> <Port/Service>");
1112 char *addrString = argv[1]; // 서버의 IP/도메인 네임 13 char *portString = argv[2]; // 서버의 포트/ 서비스 이름 1415 // 반홖 받을 주소의 형태를 지정
16 struct addrinfo addrCriteria; // 주소 형태 구조체
17 memset(&addrCriteria, 0, sizeof(addrCriteria)); // ‘0’으로 초기화
18 addrCriteria.ai_family = AF_UNSPEC; //임의의 주소 버젂 반홖(IPv4, IPv6모두) 19 addrCriteria.ai_socktype = SOCK_STREAM; // 스트림 프로토콜 반홖 요청 20 addrCriteria.ai_protocol = IPPROTO_TCP; // TCP 프로토콜 반홖 요청 2122 // 주어진 주소/서비스에 대한 주소 반홖을 요청
23 struct addrinfo *addrList; // 반홖 받을 주소가 저장될 리스트 24
목포해양대 해양전자통신공학부 목포해양대 해양전자통신공학부
네임 Resolve 예제 (2/2)
25 int rtnVal = getaddrinfo(addrString, portString, &addrCriteria, &addrList);
26 if (rtnVal != 0)
27 DieWithUserMessage("getaddrinfo() failed", gai_strerror(rtnVal));
2829 // 반홖된 주소 정보를 출력
30 for (struct addrinfo *addr = addrList; addr != NULL; addr = addr->ai_next) { 31 PrintSocketAddress(addr->ai_addr, stdout);
32 fputc('\n', stdout);
33 }34
35 freeaddrinfo(addrList); // getaddrinfo()에 의해 할당된 메모리를 해제 3637 exit(0);
38 }
목포해양대 해양전자통신공학부 목포해양대 해양전자통신공학부
네임 Resolve 실행 예시
<= 로컬 DB resolve
<= 분산 DB(DNS) resolve
<= 서비스 resolve
<= IPv6 resolve
<= 분산 DB(DNS) resolve, 등록된 모듞
IP반홖
목포해양대 해양전자통신공학부 목포해양대 해양전자통신공학부
getaddrinfo()를 활용한 주소 범용 (Generic) 코드
주소 범용(Generic) 코드란?
주소 버젂(IPv4, IPv6)에 관계없이 동작하는 코드
기존 코드의 문제점
주소 구조체를 지정하여 사용할 경우, 사용자의 각기 다른 주소 버젂의 입력에 유연하게 대처하지 못함
IPv4 코드는 IPv4 주소만 처리하고 IPv6 코드는 IPv6 주소만 처리
이유 : 각 주소 버젂에 맞는 구조체가 코드에 묶임
해결 방안
addrInfo를 처리하는 getaddrinfo 함수를 사용하여 resolving한 결과를 처리
사용자 주소 입력(IPv4 or IPv6 or DNS)
getaddrinfo를 수행하여 가용한 주소 젂부 반홖
각 개별 주소에 대하여 연결 혹은 연결 대기 시도
목포해양대 해양전자통신공학부 목포해양대 해양전자통신공학부
SetupTCPClientSocket(): 서버와 연결을 수행하고 연결된 소켓을 반환
//SetupTCPClientSocket()
//서버 호스트의 주소(IPv4, IPv6, DNS) 및 서비스 이름을 입력하면 서버와 연결된 소켓을 반홖 int SetupTCPClientSocket(const char *host, const char *service) {
struct addrinfo addrCriteria; // 반홖받을 주소의 형태를 담을 구조체 memset(&addrCriteria, 0, sizeof(addrCriteria)); // ‘0’으로 초기화 addrCriteria.ai_family = AF_UNSPEC; // IPv4와 IPv6 모두 반환 요청 addrCriteria.ai_socktype = SOCK_STREAM; // 스트리밍 소켓만 반홖 요청 addrCriteria.ai_protocol = IPPROTO_TCP; // TCP 프로토콜만 반홖 요청 struct addrinfo *servAddr; // 서버의 주소를 반홖 받을 구조 구조체 int rtnVal = getaddrinfo(host, service, &addrCriteria, &servAddr);
if (rtnVal != 0)
DieWithUserMessage("getaddrinfo() failed", gai_strerror(rtnVal));
int sock = -1;
for (struct addrinfo *addr = servAddr; addr != NULL; addr = addr->ai_next) { // TCP를 이용하여 안정된 소켓을 생성
sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sock < 0)
continue; // 소켓 생성 실패, 다음 주소로 시도 // 에코 서버에 연결 시도
if (connect(sock, addr->ai_addr, addr->ai_addrlen) == 0) break; // 소켓 연결 성공, 반복문을 탈출하고 소켓을 반홖 close(sock); // 소켓 연결 실패, 다음 주소로 다시 시도 sock = -1;
freeaddrinfo(servAddr); // getaddrinfo()의 결과로 반홖된 메모리를 회수} return sock;
}
목포해양대 해양전자통신공학부 목포해양대 해양전자통신공학부
SetupTCPServerSocket(): 서버의 주소를 획득하고 bind 및 listen수행(1/2)
SetupTCPServerSocket()
1 static const int MAXPENDING = 5; // 최대 연결 대기 수 23 int SetupTCPServerSocket(const char *service) {
4 // 서버 주소 구조체의 생성 5 struct addrinfo addrCriteria;
6 memset(&addrCriteria, 0, sizeof(addrCriteria));
7 addrCriteria.ai_family = AF_UNSPEC; // IPv4, IPv6 주소 모두 받아들임 8 addrCriteria.ai_flags = AI_PASSIVE;
9 addrCriteria.ai_socktype = SOCK_STREAM;
10 addrCriteria.ai_protocol = IPPROTO_TCP;
1112 struct addrinfo *servAddr;
13 int rtnVal = getaddrinfo(NULL, service, &addrCriteria, &servAddr);
14 if (rtnVal != 0)
15 DieWithUserMessage("getaddrinfo() failed", gai_strerror(rtnVal));
1617 int servSock = -1;
18 for (struct addrinfo *addr = servAddr; addr != NULL; addr = addr->ai_next) { 19 // TCP 소켓 생성
20 servSock = socket(servAddr->ai_family, servAddr->ai_socktype, 21 servAddr->ai_protocol);
22 if (servSock < 0)
23 continue; // 소켓 생성 실패, 다음주소로 재시도
목포해양대 해양전자통신공학부 목포해양대 해양전자통신공학부
SetupTCPServerSocket(): 서버의 주소를 획득하고 bind 및 listen수행(2/2)
SetupTCPServerSocket()
25 26 if ((bind(servSock, servAddr->ai_addr, servAddr->ai_addrlen) == 0) &&
27 (listen(servSock, MAXPENDING) == 0)) { 28 // 소켓의 지역 주소를 출력
29 struct sockaddr_storage localAddr;
30 socklen_t addrSize = sizeof(localAddr);
31 if (getsockname(servSock, (struct sockaddr *) &localAddr, &addrSize) < 0) 32 DieWithSystemMessage("getsockname() failed");
33 fputs("Binding to ", stdout);
34 PrintSocketAddress((struct sockaddr *) &localAddr, stdout);
35 fputc('\n', stdout);
36 break;
37 }
3839 close(servSock); // 소켓을 종료하고 다시 시도 40 servSock = -1;
41 }42
4344 freeaddrinfo(servAddr);
4546 return servSock;
47 }
목포해양대 해양전자통신공학부 목포해양대 해양전자통신공학부 AcceptTCPConnection(): 클라이언트의 연결을 처리
// AcceptTCPConnection()
//
클라이언트의 연결을 처리하고 연결된 소켓을 반환 1 int AcceptTCPConnection(int servSock) {2 struct sockaddr_storage clntAddr; // 클라이얶트 주소 3 // 클라이얶트 주소 구조체 길이 설정(입출력 파라미터) 4 socklen_t clntAddrLen = sizeof(clntAddr);
56 // 클라이얶트의 연결을 대기
7 int clntSock = accept(servSock, (struct sockaddr *) &clntAddr,
&clntAddrLen);
8 if (clntSock < 0)
9 DieWithSystemMessage("accept() failed");
1011 // 이때 clntSock는 클라이얶트에 연결됨 1213 fputs("Handling client ", stdout);
14 PrintSocketAddress((struct sockaddr *) &clntAddr, stdout);
15 fputc('\n', stdout);
1617 return clntSock;
18 }
목포해양대 해양전자통신공학부 목포해양대 해양전자통신공학부
IPv4-IPv6 상호 연결
상호 연결 조건
IPv4 젂용 프로그램의 경우
상호 종단갂 IPv4 지원
IPv6 젂용 프로그램의 경우
상호 종단갂 IPv6 지원
IPv4, IPv6 범용 프로그램의 경우
상호 종단 모두 IPv4를 사용하거나 상호 종단 모두 IPv6를 사용하는 경우