Presentation is loading. Please wait.

Presentation is loading. Please wait.

얇지만 얇지 않은 TCP/IP 소켓 프로그래밍 C 2판

Similar presentations


Presentation on theme: "얇지만 얇지 않은 TCP/IP 소켓 프로그래밍 C 2판"— Presentation transcript:

1 얇지만 얇지 않은 TCP/IP 소켓 프로그래밍 C 2판
(TCP/IP Sockets in C 2/e, Morgan Kaufmann) 마이클 도나후(Michael J. Donahoo) 케네스 칼버트(Kenneth L. Calvert)

2 Chapter 03 Of Names and Address Families
제 3장 도메인 네임 서비스와 주소 패밀리(IPv4-IPv6 서비스) 3.1 도메인 네임 주소를 숫자 주소로 매핑하기 3.2 IP 버전에 무관한 주소-범용 코드의 작성 3.3 숫자 주소에서 도메인 네임 주소 획득하기

3 변환(도메인 이름IP주소) 함수1 #include <netdb.h>
struct hostent* gethostbyname(const char* name);

4 int main(int argc, char **argv)
{ struct hostent *host; struct sockaddr_in addr; int i; memset(&addr, 0, sizeof(addr)); addr.sin_addr.s_addr=inet_addr(argv[1]); host=gethostbyaddr((char*)&addr.sin_addr, 4, AF_INET); printf("Officially name : %s \n\n", host->h_name); puts("Aliases "); for(i=0;host->h_aliases[i]; i++){ puts(host->h_aliases[i]); } printf("Address Type : %s \n", host->h_addrtype==AF_INET? "AF_INET":"AF_INET6"); puts("IP Address "); for(i=0; host->h_addr_list[i]; i++){ puts( inet_ntoa( *(struct in_addr*)host->h_addr_list[i] )); return 0;

5 Struct hostent struct hostent { char* h_name; char **h_aliases;
int h_addrtype; int h_length; char **h_addr_list; } Official name Alias list Host address type Length of address List of address

6 Struct hostent 구조체 변수 in_addr 구조체

7 변환(IP주소도메인 이름) 함수2 #include <netdb.h>
in_addr 구조체 #include <netdb.h> struct hostent* gethostbyaddr(const char* addr, int len, int type);

8 int main(int argc, char **argv){
int i; struct hostent *host; host=gethostbyname(argv[1]); printf("Officially name : %s \n\n", host->h_name); puts("Aliases "); for(i=0;host->h_aliases[i]; i++){ puts(host->h_aliases[i]); } printf("Address Type : %s \n", host->h_addrtype==AF_INET? "AF_INET":"AF_INET6"); puts("IP Address "); for(i=0; host->h_addr_list[i]; i++){ puts( inet_ntoa( *(struct in_addr*)host->h_addr_list[i] )); return 0;

9 기존 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에 모두 대비

10 도메인 네임 시스템 도메인 네임 시스템(Domain Name System)이란?
인터넷에서 호스트를 구분하기 위하여 IP 대신 네임(이름 주소)을 사용할 수 있도록 하는 서비스 네임주소 - IP 주소를 매핑하는 DB를 활용하여 서비스 로컬 DB 활용 : /etc/host(linux) or windows/system32/dirvers/etc/hosts(windows) 분산 DB 활용 : DNS(domain Name Service)

11 도메인 네임 시스템 장점 읽기, 쓰기, 기억의 편의성 고정된 주소 값 제공 부하 분배(load balancing) 특징
인터넷에서 호스트는 IP 주소로 구분이 가능 숫자 형태보다, 계층화된 이름 주소가 더 좋은 사용 편의성을 제공 고정된 주소 값 제공 IP 주소는 특성상 위치 이동 시 변경되나 네임 주소는 이를 클라이언트에 숨겨주어 다른 사람에게 항상 같은 주소를 제공한다 부하 분배(load balancing) 하나의 네임주소에 여러 개의 IP 주소 매핑이 가능하며 결과적으로 서로 다른 물리적인 서버가 클라이언트의 요청에 대응하게 할 수 있다 특징 DNS가 TCP/IP 프로그래밍의 필수요소는 아님

12 IPv4, IPv6 통합 네임 서비스 API int getaddrinfo (const char *hostStr, const char *serviceStr, const struct addrinfo *hints, struct addrinfo **results) 기능 : 프로토콜 버전에 상관없이 네임 주소 -> IP 주소 해석을 해주는 함수 호스트 주소(IP 혹은 도메인 네임)와 서비스(서비스 이름 혹은 port 번호)을 전달하면 위 정보에 연결가능한 주소 정보(addrinfo) 리스트를 반환한다 호스트 연결 시 도메인 네임, IPv4 주소, IPv6 주소를 모두 사용가능 hostStr: 네임 주소 혹은 IP 주소 serviceStr: 서비스 이름 혹은 port 번호 hints: 반환을 원하는 주소 정보의 형태 IPv4 및 IPv6 선택, 프로토콜 종류 등의 선택이 가능 results: 반환되는 주소들의 결과 리스트

13 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의 위치 };

14 네임 Resolve 예제 (1/2) GetAddrInfo.c 1 // ... include files here .
7 int main(int argc, char *argv[]) { 8 if (argc != 3) // 인자의 개수가 알맞은지 확인 10 DieWithUserMessage("Parameter(s)", "<Address/Name> <Port/Service>"); 11 12 char *addrString = argv[1]; // 서버의 IP/도메인 네임 13 char *portString = argv[2]; // 서버의 포트/ 서비스 이름 14 15 // 반환 받을 주소의 형태를 지정 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 프로토콜 반환 요청 21 22 // 주어진 주소/서비스에 대한 주소 반환을 요청 23 struct addrinfo *addrList; // 반환 받을 주소가 저장될 리스트 24

15 네임 Resolve 예제 (2/2) 25 int rtnVal = getaddrinfo(addrString, portString, &addrCriteria, &addrList); 26 if (rtnVal != 0) 27 DieWithUserMessage("getaddrinfo() failed", gai_strerror(rtnVal)); 28 29 // 반환된 주소 정보를 출력 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()에 의해 할당된 메모리를 해제 36 37 exit(0); 38 }

16 네임 Resolve 실행 예시 <= 로컬 DB resolve <= 분산 DB(DNS) resolve
<= IPv6 resolve <= 분산 DB(DNS) resolve, 등록된 모든 IP반환

17 getaddrinfo()를 활용한 주소 범용(Generic) 코드
주소 버전(IPv4, IPv6)에 관계없이 동작하는 코드 기존 코드의 문제점 주소 구조체를 지정하여 사용할 경우, 사용자의 각기 다른 주소 버전의 입력에 유연하게 대처하지 못함 IPv4 코드는 IPv4 주소만 처리하고 IPv6 코드는 IPv6 주소만 처리 이유 : 각 주소 버전에 맞는 구조체가 코드에 묶임 해결 방안 addrInfo를 처리하는 getaddrinfo 함수를 사용하여 resolving한 결과를 처리 사용자 주소 입력(IPv4 or IPv6 or DNS) getaddrinfo를 수행하여 가용한 주소 전부 반환 각 개별 주소에 대하여 연결 혹은 연결 대기 시도

18 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;

19 SetupTCPServerSocket(): 서버의 주소를 획득하고 bind 및 listen수행(1/2)
1 static const int MAXPENDING = 5; // 최대 연결 대기 수 2 3 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; addrCriteria.ai_protocol = IPPROTO_TCP; 11 struct addrinfo *servAddr; int rtnVal = getaddrinfo(NULL, service, &addrCriteria, &servAddr); if (rtnVal != 0) DieWithUserMessage("getaddrinfo() failed", gai_strerror(rtnVal)); 16 int servSock = -1; for (struct addrinfo *addr = servAddr; addr != NULL; addr = addr->ai_next) { // TCP 소켓 생성 servSock = socket(servAddr->ai_family, servAddr->ai_socktype, servAddr->ai_protocol); if (servSock < 0) continue; // 소켓 생성 실패, 다음주소로 재시도

20 SetupTCPServerSocket(): 서버의 주소를 획득하고 bind 및 listen수행(2/2)
25 if ((bind(servSock, servAddr->ai_addr, servAddr->ai_addrlen) == 0) && (listen(servSock, MAXPENDING) == 0)) { // 소켓의 지역 주소를 출력 struct sockaddr_storage localAddr; socklen_t addrSize = sizeof(localAddr); if (getsockname(servSock, (struct sockaddr *) &localAddr, &addrSize) < 0) DieWithSystemMessage("getsockname() failed"); fputs("Binding to ", stdout); PrintSocketAddress((struct sockaddr *) &localAddr, stdout); fputc('\n', stdout); break; } 38 close(servSock); // 소켓을 종료하고 다시 시도 servSock = -1; 41 } 42 43 44 freeaddrinfo(servAddr); 45 46 return servSock; 47 }

21 AcceptTCPConnection(): 클라이언트의 연결을 처리
// 클라이언트의 연결을 처리하고 연결된 소켓을 반환 1 int AcceptTCPConnection(int servSock) { 2 struct sockaddr_storage clntAddr; // 클라이언트 주소 3 // 클라이언트 주소 구조체 길이 설정(입출력 파라미터) 4 socklen_t clntAddrLen = sizeof(clntAddr); 5 6 // 클라이언트의 연결을 대기 7 int clntSock = accept(servSock, (struct sockaddr *) &clntAddr, &clntAddrLen); 8 if (clntSock < 0) 9 DieWithSystemMessage("accept() failed"); 10 11 // 이때 clntSock는 클라이언트에 연결됨 12 13 fputs("Handling client ", stdout); 14 PrintSocketAddress((struct sockaddr *) &clntAddr, stdout); 15 fputc('\n', stdout); 16 17 return clntSock; 18 }

22 IPv4-IPv6 상호 연결 상호 연결 조건 듀얼 스택(dual stack) 시스템 IPv4 전용 프로그램의 경우

23 소켓 연결의 우아한 종료 저자 윤 성 우

24 입력 및 출력 스트림 입력 스트림 : 데이터 수신을 위한 스트림 출력 스트림 : 데이터 전송을 위한 스트림 출력 스트림

25 Close 함수의 호출 : 입력 출력 스트림 완전 종료. 2. 일방적인 방식의 완전종료는 경우에 따라서 문제가 될 수 있다.
소켓 연결 종료의 문제점 Close 함수의 호출 : 입력 출력 스트림 완전 종료. 2. 일방적인 방식의 완전종료는 경우에 따라서 문제가 될 수 있다.

26 Half-close : 입력 및 출력 스트림 중 하나의 스트림만 종료하는 행위.

27 Half-close 기능의 함수 #include <sys/socket.h>
int shutdown(int s, int how); 상수값 모드 정의 SHUT_RD 입력 스트림 종료 1 SHUT_WR 출력 스트림 종료 2 SHUT_RDWR 입 출력 스트림 종료

28 출력 스트림의 종료의 필요성 출력 스트림을 종료하게 되면, 연결되어 있던 호스트로 EOF 메시지 전달.
EOF 전송 시, 상대 호스트의 데이터 수신 함수(read, recv)는 0을 리턴.


Download ppt "얇지만 얇지 않은 TCP/IP 소켓 프로그래밍 C 2판"

Similar presentations


Ads by Google