소켓 프로그래밍

소켓 프로그래밍 (4) 주소체계와 데이터 정렬

ddanss 2018. 10. 1. 00:07
728x90

IP : 인터넷상에서 데이터를 송수신할 목적으로 컴퓨터에게 부여하는 값

IPv4 기준의 4바이트 IP주소는 네트워크주소와 호스트주소로 나뉘며, 주소의 형태에 따라서 A,B,C,D,E클래스로 분류된다.

A:1byte-network&3byte-host, B:2byte-network&2byte-host, , C:3byte-network&1byte-host, B:4byte-network&0byte-host

PORT번호 : 프로그램상에서 생성되는 소켓을 구분하기 위해 소켓에 부여되는 번호

16비트로 표현되기 때문에 할당할 수 있는 범위는 0~65536이하다.

0~1023번까지는 Well-known Port라 해서, 특정 프로그램에 할당하기로 예약되어있기 때문에, 이 범위의 값을 제외한 다른 값을 할당해야 한다.

PORT번호는 중복이 불가능하지만, TCP소켓과 UDP소켓은 PORT번호를 공유하지 않기 때문에 중복되어도 상관 없다.

IP, PORT번호 내용 : 우리가 흔히 말하는 데이터 전송의 목적지 주소에는 IP주소뿐만 아니라 PORT번호도 포함이 된다. 그래야 최종 목적지에 해당하는 응용프로그램에까지(응용프로그램의 소켓까지) 데이터를 전달할 수 있기 때문이다.

 

여러 가지 자료형 from.POSIX(Portable Operation System Interface)

in <sys/types.h>

(1) int8_t : signed 8-bit int

(2) uint8_t : unsigned 8-bit int (unsigned char)

(3) int16_t : signed 16-bit int

(4) uint16_t : unsigned 16-bit int (unsigned short)

(5) int32_t : signed 32-bit int

(6) uint32_t : unsigned 32-bit int (unsigned long)

2. in <sys/socket.h>
(1) sa_family_t : 주소체계(address family)

(2) socklen_t : 길이정보(length of struct)

3. in <netinet/in.h>

(1) in_addr_t : IP주소정보, uint32_t로 정의되어 있음

(2) in_port_t : PORT번호정보, uint16_t로 정의되어 있음

 

IPv4기반의 주소표현을 위한 구조체

struct sockaddr_in

{
sa_family_t sin_family; //주소체계(Address Family)

uint16_t sin_port; //16비트 TCP/UDP PORT번호

struct in_addr sin_addr; //32비트 IP주소

char sin_zero[8]; //사용되지 않음;

};

 

struct in_addr

{

in_addr_t s_addr; //32비트 IPv4 인터넷 주소

};

 

구조체 sockaddr_in의 멤버에 대한 분석

멤버 sin_family 주소체계

AF_INET : IPv4 인터넷 프로토콜에 적용하는 주소체계

AF_INET6 : IPv6 인터넷 프로토콜에 적용하는 주소체계

AF_LOCAL : 로컬 통신을 위한 유닉스 프로토콜의 주소체계

멤버 sin_port 16비트 port번호 저장

[!!!!!네트워크 바이트 순서!!!!!]16비트 port번호 저장!

멤버 sin_Addr 32비트 IP주소정보를 저장

네트워크 바이트 순서로 32비트 IP주소정보를 저장

멤버 sin_zero 특별한 의미를 지니지 않음

단순히 구조체 sockaddr_in의 크기를 구조체 sockaddr와 일치시키기 위해 삽입된 멤버. 그러나 반드시 0으로 채워야함.

 

bind함수에서 중요 한 것은 두 번째 전달 인자!

struct sockaddr_in serv_addr;

- bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr))==-1) .....

bind함수는 주소체계, PORT번호, IP주소정보를 담고있는 sockaddr 구조체 변수의 주소값을 요구한다.

struct sockaddr

{

sa_family_t sin_family; //주소체계

char sa_data[14]; //주소정보

}

결국 bind 함수는 sockaddrsa_data에 한꺼번에 입력된걸 저장하기 어렵기 때문에 sockaddr_in으로 정의된 변수를 이용해서 sockaddr_in의 형식을 sockaddr_에 쏙 집어넣어서 bind함수를 완성시킨다. 주소체계를 제외한 아래내용을 sa_data에 넣는거라고 생각하자.

CPU가 데이터를 메모리에 저장하는 방식 (Host byte Order)

Big Endian(빅 엔디안) : 상위 바이트의 값을 작은 번지수에 저장하는 방식

Little Endian(리틀 엔디안) : 상위 바이트의 값을 큰 번지수에 저장하는 방식

 

Network Byte Order(네트워크 바이트 순서) : 네트워크를 통해서 데이터를 전송할 때에는 통일된 기준으로 데이터를 전송하기로 약속한 것

 

바이트 순서의 변환(Endian Conversions)를 돕는 함수

unsigned short htons(unsigned short);

short형 데이터를 호스트 바이트 순서에서 네트워크 바이트 순서로 변환해라.

unsigned short ntohs(unsigned short);

short형 데이터를 네트워크 바이트 순서에서 호스트 바이트 순서로 변환해라.

unsigned long htonl(unsigned long);

unsigned long ntohl(unsigned long);

 

문자열로 표현된 IP주소를 32비트 정수형으로 변환해 주는 함수

1.

#include <arpa/inet.h>

in_addr_t inet_addr(const char * string);

-> 성공 시 빅 엔디안으로 변환된 32비트 정수값, 실패 시 INADDR_NONE 반환

2.

#include <arpa/inet.h>

int inet_aton(const char * string, struct in_addr *addr);

-> 성공 시 1(ture), 실패 시 0(false) 반환

-> string : 변환할 IP주소 정보를 담고 있는 문자열의 주소 값 전달

-> addr : 변환된 정보를 저장할 in_addr 구조체 변수의 주소 값 전달

네트워크 바이트 순서로 정렬된 정수형 IP주소 정보를 우리가 눈으로 쉽게 인식할 수 있는 문자열의 형태로 변환해주는 함수

#include <arpa/inet.h>
char * inet_ntoa(struct in_addr adr);

-> 성공 시 변환된 문자열의 주소 값, 실패 시 1 반환

-> inet_ntoa 함수가 재호출되기 전까지만 반환된 문자열의 주소 값이 유효하니, 오랫동안 문자열 정보를 유지해야 한다면 별도의 메모리 공간에 복사를 해둬야 한다.

 

서버 프로그램은 sockaddr_in구조체 변수를 하나 선언해서 이를 서버 소켓이 동작하는 컴퓨터의 IP와 소켓에 부여할 PORT번호로 초기화 한다음에 bind함수를 호출함 으로서 호출이 이뤄진다.

클라이언트 프로그램은 sockaddr_in구조체 변수를 하나 선언해서, 이를 연결할 서버 소켓의 IPPORT번호로 초기화한 다음에 connect함수를 호출한다.

 

소켓의 IPINADDR_ANY를 사용해서 초기화할 경우, 소켓이 동작하는 컴퓨터의 IP 주소가 자동으로 할당되기 때문에 IP주소를 직접 입력하는 수고를 덜 수 있다.

 

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

#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);

-> 성공 시 0, 실패 시 1 반환

-> sockfd : 주소정보를(IPPORT) 할당할 소켓의 파일 디스크립터

-> myaddr : 할당하고자 하는 주소정보를 지니는 구조체 변수의 주소 값

-> addrlen : 두 번째 인자로 전달된 구조체 변수의 길이정보

 

WSAStringToAddress & WSAAddressToString

inet_ntoa, inet_addr 함수와 기능은 같으나 다양한 프로토콜에 적용이 가능하다.

하지만 다른 운영체제로의 이식성이 떨어져서 잘 쓰지 않는다.

#include<winsock2.h>

INT WSAStringToAddress(LPTSTR AddressString, INT AddressFamily, LPWSAPROTOCOL_INFO lpPtorocolInfo, LPSOCKADDR lpAddress, LPINT lpAddressLength);

-> 성공 시 0, 실패 시 SOCKET_ERROR반환

-> AddressString : IPPORT번호를 담고 있는 문자열의 주소 값 전달

-> AddressFamily : 첫 번째 인자로 전달된 주소정보가 속하는 주소체계 정보 전달

-> lpProtocolInfo : 프로토콜 프로바이더 설정, 일반적으로 NULL전달

-> lpAddress : 주소정보를 담을 구조체 변수의 주소 값 전달

-> lpAddressLength : 네 번째 인자로 전달된 주소 값의 변수 크기를 담고 있는 변수의 주소 값 전달

 

#include<winsock2.h>
INT WSAAdressToString(LPSOCKADDR lpsaAddress, DWORD dwAddressLength, LPWSAPROTOCOL_INFO lpProtocolInfo, LPTSTR lpszAddressString, LPDWORD lpdwAddressStringLength);

-> 성공 시 0, 실패 시 SOCKET_ERROR반환

-> lpsaAdress : 문자열로 변환할 주소정보를 지니는 구조체 변수의 주소 값 전달

-> dwAddressLength : 첫 번째 인자로 전달된 구조체 변수의 크기 전달

-> lpProtocolInfo : 프로토콜 프로바이더 설정, 일반적으로 NULL 전달

-> lpszAddressString : 문자열로 변환된 결과를 저장할 배열의 주소 값 전달

-> lpdwAddressStringLength : 네 번째 인자로 전달된 주소 값의 배열 크기를 담고 있는 변수의 주소 값 전달

 

반응형