소켓에 할당되는 IP주소와 PORT번호
인터넷 주소(Internet Address)
인터넷 상에서 컴퓨터를 구분하는 목적으로 사용되는 주소.
4바이트 주소체계인 IPv4와 16바이트 주소체계인 IPv6가 존재한다.
네트워크 주소와 호스트 주소로 나뉜다. 네트워크 주소를 이용해서 네트워크를 찾고, 호스트주소를 이용해서 호스틀 구분한다.

네트워크 주소(네트워크ID)란 네트워크의 구분을 위한 IP주소의 일부를 말한다.
WWW. SEM I. COM 이라는 회사의 무대리에게 데이터를 전송한다고 가정해보자 . 그런데 이 회사의 컴퓨터는 하
나의 로컬 네트워크로 연결되어있다. 그렇다면 먼저 SEMI . COM 의 네트워크로 데이터를 전송하는 것이 우선이다. 즉,처음부터 4바이트 IP주소 전부를 참조해서 무대리의 컴퓨터로 데이터가 전송되는 것이아니라,4바이트 IP주소 중에서 네트워크 주소만을 참조해서 일단 SEM I. COM의 네트워크로 데이터가 전송된다. 그리고 SEMI.COM의 네트워크로 데이터가 전송되었다면 해당 네트워크는(네트워크를 구성하는 라우터는) 전송된 데이터의 호스트 주소(호스트 ID)를 참조하여 무대리의 컴퓨터로 데이터 를 전송해준다.

203.211.172.103과 203.211.217.202으로 데이터를 전송하고 있다. 그런데 이중에서 203.211.172와 203.211.217이 네트워크 주소이다. 따라서 해당 네트워크로 데이터가 전송된다. 단, 네트워크로 데이터가 전송된다는 것은 네트워크를 구성하는 라우터(Router) 또는 스위치(Switch)로 데이터가 전송됨을 뜻한다. 그러면 데이터를 전송받은 라우터는 데이터에 적혀있는 호스트 주소를 참조하여 호스트에 데이터를 전송해준다.
라우터와 스위치
네트워크를 구성하려면 외부로부터 수신된 데이터를 호스트에 전달하고, 호스트가 전달하는 데이터를 외부로 송신해주는 물리적 장치가 필요하다. 이를 가르켜 라우터 또는 스위치라 하는데, 이것도 그냥 컴퓨터에 지나지 않는다. 다만 특수한 목적을 가지고 설계 및 운영되는 컴퓨터 이기 때문에 라우터 또는 스위치라는 별도의 이름을 붙여준 것이다. 때문에 우리가 사용하고 있는 컴퓨터도 적절한 소프트웨어만 설치 및 구성하면 라우터로 동작시킬 수 있다. 그리고 라우터보다 기능적으로 작은 것을 그리켜 스위치라 부르는데, 사실상 이 둘은 같은 의미로 사용이된다.
클래스 별 네트워크 주소와 호스트 주소의 경계
IP주소의 첫 번째 바이트만 보면 네트워크 주소가 몇 바이트인지 판단이 가능하다.
- 클래스 A의 첫 번째 바이트 범위 0이상 127이하
- 클래스 B의 첫 번째 바이트 범위 128이상 191이하
- 클래스 C의 첫 번째 바이트 범위 192이상 223이하
이는 다음과 같이 달리 표현할 수도 있다.
- 클래스 A의 첫 번째 비트는 항상 0으로 시작
- 클래스 B의 첫 두 비트는 항상 10으로 시작
- 클래스 C의 첫 세 비트는 항상 110으로 시작
소켓의 구분에 활용되는 PORT번호
- IP는 컴퓨터를 구분하는 용도로 사용되며, PORT 번호는 소켓을 구분하는 용도로 사용된다.
- 하나의 프로그램 내에서는 둘 이상의 소켓이 존재할 수 있으므로, 둘 이상의 PORT가 하나의 프로그램에 의해 할당 될 수 있다.
- PORT번호는 16비트로 표현, 따라서 그 값음 0이상 65535 이하
- 0~1023은 잘 알려진 PORT(Well-known PORT)라 해서 이미 용도가 결정되어 있으므로 사용 x

PORT번호는 하나의 운영체제 내에서 소켓을 구분하는 목적으로 사용된다. 때문에 하나의 운영체제 내에서 동일한 PORT번호를 둘 이상의 소켓에 할당할 수 없다. 우리가 흔히 말하는 데이터 전송의 목적지 주소에는 IP주소 뿐만 아니라 PORT번호도 포함이 된다. 그래야 최종 목적지에 해당하는 응용프로그램에까지(응용프로그램의 소켓까지) 데이터를 전달할수 있기 때문이다.
주소정보의 표현
IPv4기반의 주소푠현을 위한 구조체
주소정보를 담을 때는 다음 세가지 물음에 답이 되도록 담아야 한다.
- 질문 1 어떠한 주소체계를 사용하나요?
- 답변 1 IPv4 기반 주소체계를 사용합니다.
- 질문 2 IP주소가 어떻게 되나요?
- 답변 2 211.204.214.76입니다.
- 질문 3 PORT번호는 어떻게 되나요?
- 답변 3 2048번입니다.
위의 질문에 답이 될수 있도록, 다음의 형태로 구조체가 정의되었다. 이 구조체는 잠시 후에 소개하는 bind함수에 주소정보를 전달하는 용도로 사용된다.
struct sockaddr_in
{
sa_famaily_t sin_family; //주소체계(Address Family)
uint16_t sin_porth; //16비트 TCP/UDP PORT번호
struct in_addr sin_addr; //32비트 IP주소
char sin_zero[8] //사용되지 않음
};
그리고 위의 구조체 정의에 사용된 또 다른 구조체 in_addr은 다음과 같이 정의되어 있다. 이는 32비트 IP주소정보를
담을 수 있도록 정의되어 있다.
struct in_aeer
{
in_addr_t s_addr; //32비트 IPv4 인터넷 주소
};

확장성을 고려하여 int32_t라는 자료형을 사용한다면, 이는 어떠한 경우에도 4바이트 자료형임을 보장받을 수 있다. 이후 자료형 int가 64비트로 표현된다 해도 말이다.
구조체 sockaddr_in의 멤버에 대한 분석
멤버 sin_family
- 주소체계 정보 저장
멤버 sin_port
- 16비트 PORT번호를 저장.
- '네트워크 바이트 순서'로 저장.
멤버 sin_addr
- 32비트 IP주소정보를 저장한다.
- '네트워크 바이트순서'로 저장해야 한다.
- 32비트 정수자료형으로 인식해도 괜찮다.
멤버 sin_zero
특별한 의미를 지니지 않는 멤버이다. 단순히 구조체 sockaddr_in의 크기를 구조체 sockaddr와 일치시키기 위해 삽입된 멤버이다. 그러나 반드시 0으로 채워야 한다. 만약에 0으로 채우지 않으면 원하는 결과를 얻지 못한다.
구조체 sockaddr_in의 활용의 예
struct sockaddr_in serv_addr;
if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr))==-1)
error_handling("bind() erro");
구조체 변수 sockaddr_in bind 함수의 인자로 전달되는데, 매개변수 형이 sockaddr이므로 형 변환을 해야만 한다.
struct sockaddr
{
sa_family_t sin_family; //주소체계(Address Family)
char sa_data[14]; //주소정보
};
구조체 sockaddr은 다양한 주소체계의 주소정보를 담을 수 있도록 정의되었다. 그래서 IPv4의 주소정보를 담기가 불편하다. 이에 동일한 바이트 열을 구성하는 구조체 sockaddr_in이 정의되었으며, 이를 이용해서 쉽게 IPv4의 주소정보를 담을 수 있다.
네트워크 바이트 순서와 인터넷 주소변환
CPU에 따라 달라지는 정수의 표현

CPU에 따라서 상위 바이틀 하위 메모리 주소에 저장하기도 하고, 상위 바이트를 상위 메모리 주소에 저장하기도 한다. 즉, CPU마다 데이터를 표현 및 해석하는 방식이다르다.
바이트 순서(Order)와 네트워크 바이트 순서
빅 엔디안(Big Endian)
- 상위 바이트의 값을 작은 번지수에 저장
리틀 엔디안(Little Endian)
- 상위 바이트의 값을 큰 번지수에 저장

호스트 바이스 순서
- CPU별 데이터 저장방식을 의미함
네트워크 바이트 순서
- 통일된 데이터 송수신 기준을 의미함
- 빅 엔디안이 기준이다.

바이트 순서의 변환
바이트 변환함수
unsigned short htons(unsigned short);
unsigned short ntohs(unsigned short);
unsigned long htonl(unsigned long);
unsigned long ntohl(unsigned long);
- htons에서 h는 호스트(host)바이트 순서를 의미
- htons에서 n은 네트워크(network) 바이트 순서를 의미
- htons에서 s는 자료형 short를 의미
- htonl에서 l은 자료형 long을 의미
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
unsigned short host_port=0x1234; //2바이트 데이터를 변수에 저장. 예제를 실행하는 시스템의 cpu에 따라 저장되는 바이트 순서는 달라짐
unsigned short net_port;
unsigned long host_addr=0x12345678;//4바이느 데이터
unsigned long net_addr;
net_port=htons(host_port); //변수 host_port와 host_addr에 저장된 데이터를 네트워크 바이트 순서로 변환하고 있다. 리틀 엔디안 기준으로
net_addr=htonl(host_addr); //정렬하는 CPU상에서 실행된다면, 바이트 순서가 바뀐 값이 반환되어 변수에 저장된다.
printf("Host ordered port: %#x \n", host_port);
printf("Network ordered port: %#x \n", net_port);
printf("Host ordered address: %#lx \n", host_addr);
printf("Network ordered address: %#lx \n", net_addr);
return 0;
}
실행 결과

인터넷 주소의 초기화와 할당
문자열 정보를 네트워크 바이트 순서의 정수로 변환하기
inet_addr
#include <arpa/inet.h>
in_addr_t inet_addr(const char *string);
성공 시 빅 엔디안으로 변환된 32비트 정수 값, 실패시 INADDR_NOME 반환
211.214.107.99와 같이 점이 찍힌 10진수로 표현된 문자열을 전달하면, 해당 문자열 정보를 참조해서 IP주소정보를 32비트 정수형으로 반환.
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
char *addr1="127.212.124.78";
char *addr2="127.212.124.256"; //1바이트당 표현할 수 있는 최대 크기의 정수는 255이므로 이는
//잘못된 ip주소이다. 이 잘못된 주소를 이용해서 inet_addr함수의 오류 검출
//능력을 확인하고자 하였다.
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); // 15행의 함수호출은 정상적인 결과로 이어지지 않음을 확안하기 바란다.
if(conv_addr==INADDR_NONE)
printf("Error occureded \n");
else
printf("Network ordered integer addr: %#lx \n\n", conv_addr);
return 0;
}

inet_aton
#include <arpa/inet.h>
int inet_aton(const char * string, struct in_addr * addr);
성공 시 1(true), tlfvotl 0(false)반환
string 변환할ip주소 정보를 담고 있는 문자열의 주소 값 전달.
addr 변환된 정보를 저장할 in_addr구조체 변수의 주소 값 전달.
실행결과

inet_ntoa
#include <arpa/inet.h>
char *inet_ntoa(struct in_addr adr);
성공 시 변환된 문자열의 주소 값, 실패시 -1반환
#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); //구조체 변수 addr1에 저장된 ip정보를 전달하며 inet_ntoa함수 호출
strcpy(str_arr, str_ptr);//함수호출로 반환된 주소값을 참조하여 주소값 복사
printf("Dotted-Decimal notation1: %s \n", str_ptr);
inet_ntoa(addr2.sin_addr); //inet_ntoa함수 다시 한번 호출 14행에서 반환된 주소값에는 다른 문자열 정보가 채워짐.
printf("Dotted-Decimal notation2: %s \n", str_ptr);//19행의 출력결과를 통해 확인 가능
printf("Dotted-Decimal notation3: %s \n", str_arr);//15행에서 문자열을 복사해두었기 때문에, 14행의 함수호출을 통해서 얻은 문자열을 재 출력 할 수 있음.
return 0;
}
실행결과

인터넷 주소의 초기화
struct sockaddr_in addr;
char *serv_ip="211.217.168.13"; //ip주소 문자열 선언
char *serv_port="9190" //PORT번호 문자열 선언
memset(&addr, 0, sizeof(addr)); //구조체 변수 addr의 모든 멤버 0으로 초기화
addr.sin_family =AF_INET; //주소체계 지정
addr.sin_addr.s_addr=inet_addr(serv_ip); //문자열 기반의 ip주소 초기화
addr. sin_port = htons(atoi(serv_port)); //문자열 기반의 PORT번호 초기화
일반적인 인터넷 주소의 초기화 과정
클라이언트 주소정보 초기화
서버에서 주소정보를 설정하는 이유
"IP 211.217.168.13, PORT 9190으로 들어오는 데이터는 내게로 다 보내라!"
클라이언트에서 주소 정보를 설정하는 이유
"IP 211.217.168.13, PORT 9190으로 연결을 해라!"
INADDR_ANY
struct sockaddr_ in addr;
char *serv_port= "9190";
memset(&addr,ø,sizeof(addr));
addr.sin_family=AF_INET;
addr . sin_addr.s_addr=htonl(INADDR_ANY);
addr.sin_port=htons(atoi(serv_port));
현재 실행중인 컴퓨터의 IP를 소켓에 부여할 때 사용되는 것이 INADDR_ANY이다. 이는 서버 프로그램의 구현에 주로 사용된다.
Chapter 01의 hello_server.c, hello_client.c의 실행에 대한 고찰
./hserver 9190
서버의 실행방식, 서버의 리스닝 소켓 주소는 INADDR_ANY로 지정을 하니, 소켓의 PORT번호만 인자를 통해 전달하면 된다.
./hclient 127.0.0.1 9190
클라이언트의 실행방식, 연결할 서버의 IP와 PORT번호를 인자로 전달한다. 127.0.0.1은 루프백 주소라 하며, 이는 클라이언트를 실행하는 컴퓨터의 IP주소를 의미한다. 루프백 주소를 전달한 이유는, 서버와 클라이언트를 한 대의 컴퓨터에서 실행시켰기 때문이다.
소켓에 인터넷 주소 할당하기
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
성공시 0, 실패시 -1 반환
sockfd 주소정보를 (IP와 PORT를)할당할 소켓의 파일 디스크립터.
myaddr 할당하고자 하는 중서정보를 지니는 구조체 변수의 주소 값.
addrlen 두번째 인자로 전달된 구조체 변수의 길이 정보
int serv_sock;
sturct sockaddr_in serv_addr;
char *serv_port="9190";
/*서버 소켓(리스닝 소켓) 생성*/
serv_sock-socket(PF_INET, SOCK_STREAM,0);
/*주소정보 초기화*/
meset(&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, (struck sockaddr*)&serv_addr, sizeof(serv_addr));'TCP&IP' 카테고리의 다른 글
| TCP/IP 5장 기반서버/클라이언트 2 (0) | 2024.09.04 |
|---|---|
| TCP/IP 4장 기반서버/클라이언트1 (0) | 2024.09.03 |
| TCP/IP 2.장 소켓의 프로토콜과 그에 따른 데이터 전송 특성 (0) | 2024.09.03 |
| TCP/IP 1.장 네트워크 프로그래밍과 소켓의 이해 (0) | 2024.09.03 |
| Tcp/ip 사전 학습 (0) | 2024.09.02 |
댓글