네트워크 프로그래밍/연습문제

[네트워크 프로그래밍] 3장 연습문제

ttttki913 2024. 9. 26. 22:49
TCP/IP 소켓 프로그래밍 2판(한빛아카데미, 2022) 연습문제 풀이

 

01 호스트의바이트 정렬 방식을 확인할 수 있는 함수를 정의하시오.

bool IsLittleEndianQ;

// 리턴값: 리틀 엔디언이면 true, 그렇지 않으면 false를 리턴한다.

bool IsBigEndian();

// 리턴값: 빅 엔디언이면 true, 그렇지 않으면 false를 리턴한다.

 

#include <stdio.h>
#include <stdbool.h>

// 리틀 엔디언을 확인하는 함수
bool IsLittleEndian()
{
    unsigned int num = 1;
    // 첫 번째 바이트가 1이면 리틀 엔디언
    return (*(unsigned char *)&num == 1);
}

// 빅 엔디언을 확인하는 함수
bool IsBigEndian()
{
    return !IsLittleEndian();  // 리틀 엔디언이 아니면 빅 엔디언
}

int main()
{
    if (IsLittleEndian()) {
        printf("호스트는 리틀 엔디언입니다.\n");
    } else if (IsBigEndian()) {
        printf("호스트는 빅 엔디언입니다.\n");
    }

    return 0;
}

 

02 IPAdclr 예제의 IPv4 주소를 변환하는 코드에서 inet_pton() 함수를 inet_addr() 함수로 변경하시오. 단, IPv6 주소를 변환하는 코드는 그대로 둔다.

 

03 IPAddr 예제의 IPv4 주소를 변환하는 코드에서 inet_ntop() 함수를 inet_ntoa() 함수로 변 경하시오. 단. IPv6 주소를 변환하는 코드는 그대로 둔다.

- 윈도우

#include "..\..\Common.h"

int main(int argc, char *argv[])
{
    // 윈속 초기화
    WSADATA wsa;
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
        return 1;

    /*----------------*/
    /* IPv4 변환 연습 */
    /*----------------*/
    // 원래의 IPv4 주소 출력
    const char *ipv4test = "147.46.114.70";
    printf("IPv4 주소(변환 전) = %s\n", ipv4test);

    // inet_addr() 함수 사용 (inet_pton() 대신) 문제_02번
    struct in_addr ipv4num;
    ipv4num.s_addr = inet_addr(ipv4test);
    printf("IPv4 주소(변환 후) = %#x\n", ipv4num.s_addr);

    // inet_ntoa() 함수 사용 (inet_ntop() 대신) 문제_03번
    char *ipv4str = inet_ntoa(ipv4num);
    printf("IPv4 주소(다시 변환 후) = %s\n", ipv4str);
    printf("\n");

    /*----------------*/
    /* IPv6 변환 연습 */
    /*----------------*/
    const char *ipv6test = "2001:0230:abcd:ffab:0023:eb00:ffff:1111";
    printf("IPv6 주소(변환 전) = %s\n", ipv6test);

    // inet_pton() 함수 사용 (IPv6 변환)
    struct in6_addr ipv6num;
    inet_pton(AF_INET6, ipv6test, &ipv6num);
    printf("IPv6 주소(변환 후) = 0x");
    for (int i = 0; i < 16; i++)
        printf("%02x", ipv6num.s6_addr[i]);
    printf("\n");

    // inet_ntop() 함수 사용 (IPv6 변환)
    char ipv6str[INET6_ADDRSTRLEN];
    inet_ntop(AF_INET6, &ipv6num, ipv6str, sizeof(ipv6str));
    printf("IPv6 주소(다시 변환 후) = %s\n", ipv6str);

    // 윈속 종료
    WSACleanup();
    return 0;
}

- 리눅스 

#include "../Common.h"

int main(int argc, char *argv[])
{
    /*----------------*/
    /* IPv4 변환 연습 */
    /*----------------*/
    const char *ipv4test = "147.46.114.70";
    printf("IPv4 주소(변환 전) = %s\n", ipv4test);

    // inet_addr() 함수 사용 (inet_pton() 대신) 문제_02번
    struct in_addr ipv4num;
    ipv4num.s_addr = inet_addr(ipv4test);
    printf("IPv4 주소(변환 후) = %#x\n", ipv4num.s_addr);

    // inet_ntoa() 함수 사용 (inet_ntop() 대신) 문제_03번
    char *ipv4str = inet_ntoa(ipv4num);
    printf("IPv4 주소(다시 변환 후) = %s\n", ipv4str);
    printf("\n");

    /*----------------*/
    /* IPv6 변환 연습 */
    /*----------------*/
    const char *ipv6test = "2001:0230:abcd:ffab:0023:eb00:ffff:1111";
    printf("IPv6 주소(변환 전) = %s\n", ipv6test);

    // inet_pton() 함수 사용 (IPv6 변환)
    struct in6_addr ipv6num;
    inet_pton(AF_INET6, ipv6test, &ipv6num);
    printf("IPv6 주소(변환 후) = 0x");
    for (int i = 0; i < 16; i++)
        printf("%02x", ipv6num.s6_addr[i]);
    printf("\n");

    // inet_ntop() 함수 사용 (IPv6 변환)
    char ipv6str[INET6_ADDRSTRLEN];
    inet_ntop(AF_INET6, &ipv6num, ipv6str, sizeof(ipv6str));
    printf("IPv6 주소(다시 변환 후) = %s\n", ipv6str);

    return 0;
}

 

04 (윈도우) IPAddr 예제에서 사용하는 inet_pton() 함수를 모두 WSAStringToAddress() 함수로 변경하시오.

A: WASStringToAddress()는 IP 주소 문자열을 sockaddr_in 구조체로 변환

#include "..\..\Common.h"

int main(int argc, char *argv[])
{
    // 윈속 초기화
    WSADATA wsa;
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
        return 1;

    /*----------------*/
    /* IPv4 변환 연습 */
    /*----------------*/
    const char* ipv4test = "147.46.114.70";
    printf("IPv4 주소(변환 전) = %s\n", ipv4test);

    // WSAStringToAddress() 함수 사용 (inet_pton() 대신)
    struct sockaddr_in ipv4num;
    int ipv4numSize = sizeof(ipv4num);
    WSAStringToAddressA((char*)ipv4test, AF_INET, NULL, (LPSOCKADDR)&ipv4num, &ipv4numSize); // (char*)로 변환

    printf("IPv4 주소(변환 후) = %#x\n", ipv4num.sin_addr.s_addr);

    /*----------------*/
    /* IPv6 변환 연습 */
    /*----------------*/
    const char* ipv6test = "2001:0230:abcd:ffab:0023:eb00:ffff:1111";
    printf("IPv6 주소(변환 전) = %s\n", ipv6test);

    // WSAStringToAddress() 함수 사용 (inet_pton() 대신)
    struct sockaddr_in6 ipv6num;
    int ipv6numSize = sizeof(ipv6num);
    WSAStringToAddressA((char*)ipv6test, AF_INET6, NULL, (LPSOCKADDR)&ipv6num, &ipv6numSize); // (char*)로 변환

    printf("IPv6 주소(변환 후) = 0x");
    for (int i = 0; i < 16; i++)
        printf("%02x", ipv6num.sin6_addr.s6_addr[i]);
    printf("\n");

    // 윈속 종료
    WSACleanup();
    return 0;
}

 

05 (윈도우) IPAddr 예제에서 사용하는 inet_ntop() 함수를 모두 WSAAddressToString() 함수로 변경하시오.

A: WASAddressToString()은 sockaddr_in6 구조체를 다시 IPv6 문자열로 변환

#include "..\..\Common.h"

int main(int argc, char *argv[])
{
    // 윈속 초기화
    WSADATA wsa;
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
        return 1;

    /*----------------*/
    /* IPv4 변환 연습 */
    /*----------------*/
    const char *ipv4test = "147.46.114.70";
    printf("IPv4 주소(변환 전) = %s\n", ipv4test);

    struct sockaddr_in ipv4num;
    int ipv4numSize = sizeof(ipv4num);
    WSAStringToAddress((LPSTR)ipv4test, AF_INET, NULL, (LPSOCKADDR)&ipv4num, &ipv4numSize);
    
    // WSAAddressToString() 함수 사용 (inet_ntop() 대신)
    char ipv4str[INET_ADDRSTRLEN];
    DWORD ipv4strSize = sizeof(ipv4str);
    WSAAddressToString((LPSOCKADDR)&ipv4num, ipv4numSize, NULL, ipv4str, &ipv4strSize);
    printf("IPv4 주소(다시 변환 후) = %s\n", ipv4str);
    printf("\n");

    /*----------------*/
    /* IPv6 변환 연습 */
    /*----------------*/
    const char *ipv6test = "2001:0230:abcd:ffab:0023:eb00:ffff:1111";
    printf("IPv6 주소(변환 전) = %s\n", ipv6test);

    struct sockaddr_in6 ipv6num;
    int ipv6numSize = sizeof(ipv6num);
    WSAStringToAddress((LPSTR)ipv6test, AF_INET6, NULL, (LPSOCKADDR)&ipv6num, &ipv6numSize);

    // WSAAddressToString() 함수 사용 (inet_ntop() 대신)
    char ipv6str[INET6_ADDRSTRLEN];
    DWORD ipv6strSize = sizeof(ipv6str);
    WSAAddressToString((LPSOCKADDR)&ipv6num, ipv6numSize, NULL, ipv6str, &ipv6strSize);
    printf("IPv6 주소(다시 변환 후) = %s\n", ipv6str);

    // 윈속 종료
    WSACleanup();
    return 0;
}

 

06 도메인 이름을 명령행 인수로 입력받아, 해당 호스트의 모든 별명과 모든 IPv4 주소를 출력하는 프로그램을 작성하시오.

A:

#include "..\..\Common.h"

// 도메인 이름 -> IPv4 주소 및 별명 출력
bool GetIPAddr(const char* name) {
    struct hostent* ptr = gethostbyname(name);
    if (ptr == NULL) {
        printf("gethostbyname() failed\n");
        return false;
    }

    if (ptr->h_addrtype != AF_INET) return false;

    // 도메인 이름 출력
    printf("Official Name: %s\n", ptr->h_name);

    // 별명(aliases) 출력
    char** aliases = ptr->h_aliases;
    if (*aliases) {
        printf("Aliases:\n");
        for (int i = 0; aliases[i] != NULL; i++) {
            printf("  %s\n", aliases[i]);
        }
    }
    else {
        printf("No aliases found.\n");
    }

    // 모든 IPv4 주소 출력
    char** addr_list = ptr->h_addr_list;
    printf("IP Addresses:\n");
    for (int i = 0; addr_list[i] != NULL; i++) {
        struct in_addr* addr = (struct in_addr*)addr_list[i];
        char ipstr[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, addr, ipstr, sizeof(ipstr));
        printf("  %s\n", ipstr);
    }

    return true;
}

int main(int argc, char* argv[]) {
    WSADATA wsa;
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return 1;

    // 명령행 인자 검사
    if (argc != 2) {
        printf("Usage: %s <domain name>\n", argv[0]);
        WSACleanup();
        return 1;
    }

    printf("도메인 이름(변환 전) = %s\n", argv[1]);

    // 도메인 이름 -> IP 주소 및 별명 출력
    if (GetIPAddr(argv[1])) {
        printf("Domain information retrieved successfully.\n");
    }
    else {
        printf("Failed to retrieve domain information.\n");
    }

    // 윈속 종료
    WSACleanup();
    return 0;
}

 

07 IP 주소를 명령행 인수로 입력받아, 해당 호스트의 모든 별명과 모든 IPv4 주소를 출력하는 프 로그램을 작성하시오.

A: 

#include "..\..\Common.h"

// IP 주소 -> 도메인 이름 및 별명 출력
void PrintHostInfo(const struct in_addr& addr) {
    struct hostent* ptr = gethostbyaddr((const char*)&addr, sizeof(addr), AF_INET);
    if (ptr == NULL) {
        printf("gethostbyaddr() 실패: %d\n", WSAGetLastError());
        return;
    }

    // 도메인 이름 출력
    printf("도메인 이름: %s\n", ptr->h_name);

    // 별명(aliases) 출력
    char** aliases = ptr->h_aliases;
    if (*aliases) {
        printf("별명:\n");
        for (int i = 0; aliases[i] != NULL; i++) {
            printf("  %s\n", aliases[i]);
        }
    }
    else {
        printf("별명이 없습니다.\n");
    }

    // 모든 IPv4 주소 출력
    char** addr_list = ptr->h_addr_list;
    printf("IPv4 주소:\n");
    for (int i = 0; addr_list[i] != NULL; i++) {
        struct in_addr* addr_ptr = (struct in_addr*)addr_list[i];
        char ipstr[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, addr_ptr, ipstr, sizeof(ipstr));
        printf("  %s\n", ipstr);
    }
}

int main(int argc, char* argv[]) {
    WSADATA wsa;
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return 1;

    // 명령행 인자 검사
    if (argc != 2) {
        printf("사용법: %s <IP 주소>\n", argv[0]);
        WSACleanup();
        return 1;
    }

    struct in_addr addr;

    // IP 주소 변환
    if (inet_pton(AF_INET, argv[1], &addr) <= 0) {
        printf("유효하지 않은 IP 주소입니다: %s\n", argv[1]);
        WSACleanup();
        return 1;
    }

    // IP 주소 -> 도메인 이름 및 별명 출력
    PrintHostInfo(addr);

    // 윈속 종료
    WSACleanup();
    return 0;
}

 

08 NameResolution 예제에서 정의한 GetIPAddr() 함수와 사용법이 같은 GetIPInfo() 함수를 정의하고 테스트하시오. 단, 내부적으로는 getaddrinfo() 함수를 사용하여 도메인 이름에 대응 하는 IPv4 주소를 얻어야 한다.

A:

#include "..\..\Common.h"
// 도메인 이름 -> IPv4 주소
bool GetIPInfo(const char* name, struct in_addr* addr) {
    struct addrinfo hints, * result;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;  // IPv4

    // getaddrinfo 호출
    if (getaddrinfo(name, NULL, &hints, &result) != 0) {
        printf("getaddrinfo() 실패\n");
        return false;
    }

    // IPv4 주소 가져오기
    struct sockaddr_in* ipv4 = (struct sockaddr_in*)result->ai_addr;
    memcpy(addr, &ipv4->sin_addr, sizeof(struct in_addr));

    freeaddrinfo(result);  // 메모리 해제
    return true;
}

int main() {
    // 소켓 초기화
    WSADATA wsa;
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
        printf("WSAStartup() 실패\n");
        return 1;
    }

    struct in_addr addr;
    // 도메인 이름에 대한 IP 주소 정보 가져오기
    if (GetIPInfo("www.google.com", &addr)) {
        char str[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &addr, str, sizeof(str));
        printf("IP 주소: %s\n", str);
    }
    else {
        printf("IP 주소를 가져오는 데 실패했습니다.\n");
    }

    // 소켓 종료
    WSACleanup();
    return 0;
}

09 NameResolution 예제에서 정의한 GetDomainName( ) 함수와 사용법이 같은 GetDomainInfo() 함수를 정의하고 테스트하시오. 단. 내부적으로는 getnameinfo() 함수를 사용하여 IPv4 주소에 대응하는 도메인 이름을 얻어야 한다.

A:

#include "..\..\Common.h"

// 도메인 이름 -> IPv4 주소
bool GetIPInfo(const char* name, struct in_addr* addr) {
    struct addrinfo hints, * result;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;  // IPv4

    // getaddrinfo 호출
    if (getaddrinfo(name, NULL, &hints, &result) != 0) {
        printf("getaddrinfo() 실패\n");
        return false;
    }

    // IPv4 주소 가져오기
    struct sockaddr_in* ipv4 = (struct sockaddr_in*)result->ai_addr;
    memcpy(addr, &ipv4->sin_addr, sizeof(struct in_addr));

    freeaddrinfo(result);  // 메모리 해제
    return true;
}

// IPv4 주소 -> 도메인 이름
bool GetDomainInfo(struct in_addr addr, char* name, size_t namelen) {
    struct sockaddr_in sa;
    sa.sin_family = AF_INET;
    sa.sin_addr = addr;
    sa.sin_port = 0;  // 포트 번호는 사용하지 않음

    // getnameinfo 호출
    if (getnameinfo((struct sockaddr*)&sa, sizeof(sa), name, namelen, NULL, 0, 0) != 0) {
        printf("getnameinfo() 실패\n");
        return false;
    }

    return true;
}

int main() {
    // 소켓 초기화
    WSADATA wsa;
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
        printf("WSAStartup() 실패\n");
        return 1;
    }

    struct in_addr addr;
    // 도메인 이름에 대한 IP 주소 정보 가져오기
    const char* domain_name = "www.google.com";
    if (GetIPInfo(domain_name, &addr)) {
        char str[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &addr, str, sizeof(str));
        printf("IP 주소: %s\n", str);

        // IP 주소에 대한 도메인 이름 정보 가져오기
        char domain[NI_MAXHOST];
        if (GetDomainInfo(addr, domain, sizeof(domain))) {
            printf("도메인 이름: %s\n", domain);
        }
        else {
            printf("도메인 이름을 가져오는 데 실패했습니다.\n");
        }
    }
    else {
        printf("IP 주소를 가져오는 데 실패했습니다.\n");
    }

    // 소켓 종료
    WSACleanup();
    return 0;
}