인터넷 초기화, 할당

interviewsangu·2020년 8월 1일
0

소켓 프로그래밍

목록 보기
5/8
post-thumbnail

인터넷 주소의 초기화와 할당

문자열 정보를 네트워크 바이트 순서의 정수로 변환

123.112.111.254와 같은 32바이트 IP주소를 정수형으로 변환해주는 함수가 있다. 해당 함수는 정수형으로 변환과 동시에 네트워크 바이트 순서로의 변환도 진행한다.

#include <arpa/inet.h>
in_addr_t inet_addr(const char *string);
// 성공 시 빅 엔디안 32비트 정수, 실패 시 INADDR_NONE 반환

위 함수의 반환형인 in_addr_t는 32비트 정수형으로 정의되어 있다.

  • example
    inet_addr.c
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
    char *addr1 = "1.2.3.4";
    char *addr2 = "1.2.3.256";

    unsigned long conv_addr = inet_addr(addr1);
    if(conv_addr == INADDR_NONE)
    	printf("Error occured! \n");
    else
    	printf("Network ordered integer addr: %#lx \n", conv_addr);

    conv_addr = inet_addr(addr2);
    if(conv_addr == INADDR_NONE)
    	printf("Error occured! \n");
    else
    	printf("Network ordered integer addr: %#lx \n", conv_addr);

    return 0;
}

위 코드의 실행 시 다음 결과를 얻을 수 있다.
Network ordered integer addr: 0x4030201
Error occured
1바이트 당 최대 크기의 정수는 255이므로 256에서 에러가 발생하였다.

다음 함수는 기능상으로 위의 함수와 같으나, 구조체 변수 in_addr의 사용이라는 차이점이 있다. inet_addr의 경우 변환된 주소를 sockaddr_in에 선언되어 있는 in_addr 구조체 변수에 대입하는 과정을 추가로 진행해야 하나 해당 함수의 경우 구조체를 전달과 동시에 저장이 되기 때문에 활용도의 경우 다음 함수가 더 높다.

#include <arpa/inet.h>
in inet_aton(const char *string, struct in_addr *addr);
// 성공 시 1, 실패 시 0 반환
  • example
    inet_aton.c
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
void error_handling(char *message);
int main(int argc, char *argv[]){
    char *addr = "127.232.124.79";
    struct sockaddr_in addr_inet;
    
    if(!inet_aton(addr, &addr_inet.sin_addr))
    	error_handling("Conversion error");
    else
    	printf("Network ordered integer addr: %#x \n",
        	addr_inet.sin_addr.s_addr);
    return 0;
}
void error_handling(char *message){
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

위의 코드는 다음의 실행결과를 보인다.
Network ordered integer addr: 0x4f7ce87f

다음의 함수는 inet_aton의 반대 기능을 가진 함수이다.

char * inet_ntoa(struct in_addr adr);
// 성공 시 변환된 문자열의 값, 실패 시 -1 반환

반환형이 char형 포인터이다. 즉, 문자열의 주소 값이 반환되는데, 재 호출시 이전에 할당된 주소가 지워질 수 있으니, 문자열 정보가 오래 필요한 경우 별도의 공간에 할당해두는 것이 좋다.

  • example
    inet_ntoa.c
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
int main(int argc, char *argv[]){
    struct sockaddr_in addr1, addr2;
    char *str_ptr;
    char str_arr[20];
    
    addr1.sin_addr.s_addr = htonl(0x1020304);
    addr2.sin_addr.s_addr = htonl(0x1010101);
    
    str_ptr = inet_ntoa(addr1.sin_addr);
    strcpy(str_arr, str_ptr);
    printf("Dotted-Decimal notation1: %s \n", str_ptr);
    
    inet_ntoa(addr2.sin_addr);
    printf("Dotted-Decimal notation1: %s \n", str_ptr);
    printf("Dotted-Decimal notation1: %s \n", str_arr);
    return 0;
}

위의 코드는 다음과 같은 실행 결과를 보인다.
Dotted_Decimal notation: 1.2.3.4
Dotted_Decimal notation: 1.1.1.1
Dotted_Decimal notation: 1.2.3.4

인터넷 주소의 초기화

인터넷 주소의 초기화 구조를 살펴보겠다. 다만 보일 코드에서 IP와 PORT를 직접 초기화하고 있는데, 이런 방식은 다른 컴퓨터에서 실행시마다 코드를 변경해야 하므로, 적절한 방법은 아니다.

struct sockaddr_in addr;
char *serv_ip = "211.217.168.13";
char *serv_port = "9190";
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; // 주소체계 지정
addr.sin_addr.s_addr = inet_addr(serv_ip); // 문자열 기반의 IP주소 초기화
addr.sin_port = htons(atoi(serv_port)); // 문자열 기반의 PORT번호 초기화

memset을 통해 addr 구조체를 전부 0으로 초기화 하는 이유는, sin_zero를 0으로 초기화하기 위함이다.

클라이언트 주소정보 초기화

위에서 등장한 인터넷 주소정보 초기화 과정은 서버 프로그램에서 주로 보인다. 이는 해당 아이피와 포트 번호를 할당 해 들어오는 데이터를 모두 가져오기 위함이다..
반면 클라이언트에서 생성하는 연결요청용 소켓은 할당된 아이피와 포트로 연결을 하도록 유도한다.
동작의 형태가 다르기 때문에 사용하는 함수가 다르며, 서버의 경우 sockaddr_in 구조체를 선언 후 서버 소켓이 동작하는 컴퓨터의 IP와 소켓에 부여할 PORT번호로 초기화한 후 bind 함수를 호출하는 반면, 클라이언트의 경우 sockaddr_in 선언 후 이를 연결할 서버 소켓의 IP와 PORT 번호로 초기화 후 connect 함수를 호출한다.

INADDR_ANY

addr.sin_addr.s_addr = htonl(INADDR_ANY);
직접 주소를 할당할 필요 없이 소켓이 동작하는 컴퓨터의 IP 주소를 자동으로 할당한다. Multi-homed 컴퓨터의 경우 포트 번호만 일치한다면 여러 IP 중 어떤 주소를 통해 데이터가 들어오더라도 이를 받을 수 있다.

이전에 실행 한 서버와 클라이언트 파일을 살펴보자.
해당 코드들을 실행 시
./hserver 9190
./hclient 127.0.0.1 9190 과 같은 커맨드로 실행을 했다.
서버의 경우 INADDR_ANY로 할당되어 있기 때문에, 포트 번호만 전달했고, 클라이언트의 경우 IP 주소까지 전달하고 있다.

소켓에 인터넷 주소 할당하기

초기화된 주소정보를 소켓에 할당하는 역할은 bind가 담당한다.

#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
// 성공 시 0, 실패 시 -1 반환

위의 함수 호출을 통해 첫 번째 인자에 해당하는 소켓 파일디스크립터에 주소정보가 전달된다.

  • summary
int serv_sock;
struct sockaddr_in serv_addr;
char *serv_port = "9190";

// 서버 소켓 생성
serv_sock = socket(PF_INET, SOCK_STREAM, 0);

// 주소정보 초기화
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(serv_port));

// 주소정보 할당
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

0개의 댓글