반응형

(앞서 5장과 이 6장은 상대적으로 내용이 딱딱합니다. 소켓 프로그래밍을 하기 위한 기본적인

내용을 언급하고 있기 때문이죠. 실제로 소켓프로그래밍을 시작하기 전에 기반을 다진다는

개념으로 여러번 읽어주셔서 숙지하시기 바랍니다.)

 

 

Internet Address

 

IP 주소란, 인터넷 상에 존재하는 호스트들을 구분하기 위한 32비트 주소 체계를 의미합니다.

일반적으로 점이 찍힌 십진수 표현 방식(Dotted-decimal Notation)을 사용해서 IP 주소를 표현하는데,

점에 의해 구분되는 각각의 십진수 값은 1바이트로 표현됩니다. 따라서 총 4바이트를 사용하게 됩니다.

4바이트 IP 주소는 네트워크 주소와 호스트 주소로 나뉘며, 주소의 형태에 따라 A, B, C, D, E 클래스로 분류할 수 있습니다. class E는 일반적이지 않은, 예약되어 있는 주소이므로 생략하겠습니다.

 

class   <       1byte       ><       1byte       ><       1byte       ><       1byte       >

  A      0  Network ID       |                                Host ID                               |

  B      10              Network ID                 |                    Host ID                   |

  C      110                         Network ID                              |       Host ID      |

  D      1110                                Multicast Address

 

네트워크 주소란 네트워크를 구분하여 주는 ID를 의미합니다. 예를 들어서 MP 라는 회사의 권 대리에게 데이터를

전송한다고 가정해 봅시다. 이 회사는 하나의 로컬 네트워크로 이루어져 있습니다. 이러한 회사의 권 대리가 사용하는

컴퓨터에 데이터를 전송하기 위해서, 일단은 MP의 네트워크로 데이터를 전송해 주는 것이 우선입니다.

즉 처음부터 IP주소 4바이트 모두를 참조해서 권 대리의 컴퓨터로 바로 찾아 가는 것이 아니라, 4바이트 IP 주소 중에서

네트워크 주소만을 참조해서 일단 MP의 네트워크를 먼저 찾아간다.

MP의 네트워크를 찾았다면 이제는 호스트 주소를 참조해서 무 대리의 컴퓨터로 찾아 갈 차례가 됩니다.

즉 같은 네트워크 상에서는 모든 컴퓨터가 동일한 Network ID를 가지고 각각의 컴퓨터가 서로다른 Host ID를 가집니다.

 

이제 저 위에 클래스를 각각 분석해봅시다.

 

class A

제일앞의 8비트가 Network ID인데 그 8비트의 제일 앞부분이 0으로 시작합니다.

즉 00000000~01111111 의 표현 범위를 가지며 10진수로는 0 ~ 127 까지 입니다.

하지만 제일처음 0과 제일마지막인 127은 사용하지 않습니다.

Host ID 부분도 모두0일때와 모두1일때는 사용하지 않습니다.

 

class B

 

제일 앞의 16비트가 Network ID인데 그 16비트의 제일 앞부분이 10으로 시작합니다.

8비트 단위로 끊어서 생각햇을때, 모두 0인경우와 모두 1인경우는 사용하지 않습니다.

즉 Network ID는 [128 . 1 . x . x ~ 191 . 254 . x . x] 의 범위를 가집니다.

 

class C

제일 앞의 24비트가 Network ID인데 그 24비트의 제일 앞부분이 110으로 시작합니다.

마찬가지로 모두 0인경우와 모두 1인경우는 사용하지 않으므로

Network ID는 [192. 0 . 1 . x ~ 223. 255 . 254. x] 의 범위를 가집니다.

 

 

즉 클래스 구분에는

class   IP주소                       가능컴퓨터수        서브넷 마스크          사설 IP                             주요 사용처

A       1~126.xxx.xxx.xxx        16777214          255.0.0.0              10.0.0.0 ~ 10.255.255.255          국가나 대형망

B      128~191.aaa.xxx.xxx       65534             255.255.0.0          172.16.0.0 ~ 172.32.255.255      학교 등 중대규모

C      192~223.aaa.bbb.xxx         254              255.255.255.0     192.168.0.0 ~ 192.168.255.255   소규모회사,isp업체

D      224~239      멀티캐스팅용

E       실험용

127는 자신의 컴퓨터 시스템의 TCP/IP 모듈을 테스트 하기 위해 사용되는 용도

참고 ) 공인 아이피는 인터넷에 있는 라우터를 통과할 수 있는 주소고 사설 아이피는 같은 네트워크에서는 통신이 되지만, 인터넷에 있는 라우터를 통과할 수 없는 주소입니다. 사설 아이피는 학교나, 기숙사같은 곳에서 주로 사용합니다.

 

 

Port 란 무엇인가?

 

일반적으로 하나의 컴퓨터 안에서, 여러 개의 어플리케이션이 동시에 네트워크를 통해서 다른 컴퓨터와 데이터를

주고 받습니다. 예를 들자면 우리가 인터넷 강의를 보면서 친구들과 메신저 프로그램으로 대화를 하는 것 입니다.

현재 이 두개의 프로그램이 인터넷을 통해서 데이터를 주고 받고 잇다고 말할 수 있습니다.

 

일반적으로 개인이 사용하는 컴퓨터는 하나의 물리적 연결 장치(네트워크 가드)를 통해서 네트워크에 연결되어 있습니다. 따라서 하나의 IP 주소를 가지게 됩니다. 즉 인터넷을 통해서 데이터를 주고 받는 프로그램이 현재 여려 개 실행되고

있더라도 데이터를 송.수신하는 길(통로)는 하나밖에 존재하지 않게 되는 것입니다.

 

그렇다면 컴퓨터는 어떻게 수신한 데이터를 구분하여 각각의 프로그램에게 전달해 줄 수 있는 것일까요?

IP주소로는 인터넷에 연결되어 있는 컴퓨터들을 구분하여 줄 수는 있어도 컴퓨터 안에서 실행되는 프로그램까지 구분하지는 못합니다.

즉 IP 주소만으로는 받은 데이터 패킷을 메신저 프로그램에 전달할 것인지, 인터넷 강의 프로그램에 전달할 것인지 구분하지 못한다는 것입니다.

 

이것을 구분하기 위해 필요한 것이 바로 Port입니다.

32비트 IP 주소로는 네트워크상에 존재하는 호스트를 구분하게 되고,

16비트 Port 정보로는 호스트 내에서 실행되고 있는 프로그램을 구분하게 됩니다.

이것은 물리적인 개념의 할당이 아니라, 동일 호스트 내에서의 논리적인 할당일 뿐입니다.

하드웨어적으로 구현되어 있는 것이 아니라 소프트웨어적으로 구현놓았다는 뜻으로 이해하시면 되겠습니다.

 

Port는 2바이트로 표현되므로 가질 수 있는 값의 범위가 0 ~ 65535 까지 입니다.

그러나 0 ~ 1023번 까지는 '잘 알려진 Port (well-known Port)' 라고 해서 예악되어 있는 Port 이므로 사용이 제한됩니다.

사용할 수 없는 것이 아니라 사용되는 용도를 미리 약속해 놓았다는 의미입니다. 또한 Port는 중복될 수 없으나,

TCP 소켓과 UDP 소켓은 Port를 서로 공유하지 않으므로 중복되어도 상관 없습니다.

 

즉 TCP 소켓을 생성할 때 9314 Port 사용했다면, 다른 TCP 소켓은 9314 Port를 사용할 수 없지만, UDP 소켓은 9314 Port를 사용할 수 있습니다.

결론적으로 데이터 전송의 최종 목적지는 호스트가 아니라 메모리상에 올라와 실행 중에 있는 프로그램입니다.

그러므로 데이터를 보내기 위해서는 데이터 패킷 내에 IP 주소 정보뿐만 아니라 Port 정보도 함께 포함을 시켜야 합니다

 

참고 ) 프로세스(process)

프로세스를 한마디로 말하면 "메모리상에 올라와 있는 실행되고 있는 프로그램" 이라고 정의할 수 있습니다.

그렇다면 우리가 앞서 만들었던 Hello World 서버 프로그램도 프로세스라고 말할 수 있을까요? 아닙니다.

하드디스크에 저장되어 있는 프로그램 자체는 프로세스가 아닙니다.

메인 메모리에 올라와서 실행되고 있는 프로그램을 프로세스 라고합니다.

 

 

따라서 그 Hello World 서버를 컴파일하고, 실행하게 되면 커맨드 라인이 깜박 거리면서 더이상 진행되지 않고 대기 상태에 있는 것을 볼 수 있습니다. 클라이언트의 접속을 기다리는 상태가 됩니다. 이때 서버 프로그램을 프로세스 라고 합니다.

 

 

주소 정보의 표현

 

모든 프로토콜은 자신만의 고유한 주소 포맷이 있습니다. 예를 들어 IPv4 에서는 32비트 주소 체계를 사용하지만,

IPv6에서는 128비트 주소 체계를 사용합니다.

그러므로 프로토콜에 따라 주소 정보를 나타내는 데이터 타입이 독립적으로존재합니다.

우리는 IPv4를 위주로 살펴보겠습니다.

 

 

1. IPv4 의 주소 체계를 나타내는 구조체

 

처음보는 데이터 타입들이 나오고 있죠. 이러한 데이터 타입들은 POSIX 에서 그 근거를 찾을 수 있습니다

POSIX(Portable Operating System Interface)란 유닉스 계열의 운영 체제를 위해 표준화 해 놓은 인터페이스(API) 입니다.

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

Data type           Description                                        Header

int8_t                signed 8-bit int                                  <sys/types.h>

uint8_t              unsigned 8-bit int (unsigned char)          <sys/types.h>

int16_t              signed 16-bit int                                 <sys/types.h>

uint16_t            unsigned 16-bit int (unsigned short)        <sys/types.h>

int32_t              signed 32-bit int                                 <sys/types.h>

uint32_t            unsigned 32-bit int (unsigned long)         <sys/types.h>

sa_family_t        address family                                     <sys/socket.h>

socklen_t          length of struct                                    <sys/socket.h>

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

 

그런데 왜 이런 식으로 데이터 타입을 따로 선언해 놓은 것일까요? 앞서 언급했지만

확장성을 고려한 결과라고 생각하시면 됩니다. 즉 int32_t라는 데이터 타입을 사용한다면, 어떠한 경우에도

4바이트 데이터 타입이라는 것을 보장받을 수 있습니다.

 

다음은 Local Unix 프로도콜에서 사용되는 주소 정보 구조체 입니다.

 

2. sockaddr_in 구조체 정보

 

그럼 이제 sockaddr_in 구조체 안에 존재하는 변수들의 의미를 하나씩 살펴봅시다.

●sin_family : 프로토콜 체계마다 주소 체계가 다르다고 했었죠.

sin_family 변수에는 사용되는 주소 체계에 대한 정보를 대입해 줍니다.

 

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

주소체계(Address Family)      정의

AF_INET                             IPv4 인터넷  프로토콜

AF_INET6                           IPv6 인터넷 프로토콜

AF_LOCAL                         Local 통신을 위한 UNIX 프로토콜

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

●sin_port : 16비트 Port 정보를 대입해 준다. 네트워크 바이트 순서로 대입해야 한다.

●sin_addr : 32비트 IP 주소 정보를 대입해 준다. 역시 네트워크 바이트 순서로 대입해야 한다.

●sin_zero : 특별한 의미 없이 단순한 채워주기(padding) 위한 목적으로 사용되는 구조체 멤버이다.

 

 

네트워크 바이트 순서

 

sockaddr_in 구조체 변수에 값을 대입할 경우에는 네트워크 바이트 순서 (Network-byte Ordering)로 값을 변경한 다음에 대입해야 합니다. 그렇다면 네트워크 바이트 순서라는 것은 무엇을 의미할까요?

일단 바이트 순서의 의미에서부터 시작해 봅시다.

바이트 순서라는 것은 시스템이 내부적으로 데이터를 표현하는 방법을 의미합니다.

그렇다고 해서 모든 시스템이 데이터 표현방식이 같은 것은 아닙니다.

크게 두 가지 방법으로 나뉘는데, 하나는 Big-Endian 방식이고 또 하나는 Little-Endian 방식입니다.

그렇다면 이제 이 두 가지 방법을 이용하여 0x12345678이라는 32비트 값을 표현해 봅시다.

 

● Big-Endian 표현 방식

0x12345678

0x12   0x34   0x56   0x78

낮은주소    --->    높은주소

 

● Little -Endian 표현 방식

0x12345678

0x78   0x56   0x34   0x12

낮은주소    --->    높은주소

 

시스템이 내부적으로 데이터를 처리하는데 있어서 Big-Endian 방식을 쓰느냐, Little-Endian 방식을 쓰느냐는

시스템의 CPU에 따라 달라집니다. 이를 '호스트 바이트 순서(Host Byte Order)' 라고 합니다

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

Little Endian         Big Endian

  intelx86                IBM

    AMD                SPARC

    EDC                Motorola

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

서로 다른 방식을 쓰는 CPU끼리 아무런 약속 없이 그냥 데이터를 주고받으면 문제가 발생하겠죠.

때문에 네트워크를 통해 데이터를 전송할 대는 통일된 방식을 이용해서 데이터를 전송하기로 약속을 하였는데,

이것이 바로 '네트워크 바이트 순서(Network Byte Order)' 입니다.

네트워크 바이트 순서는 Big-Endian 방식만을 사용합니다.

 

따라서 시스템이 Little-Endian 방식을 사용할 경우, 네트워크를 통해 데이터를 전송하기 전에

Big-Endian 방식으로 데이터를 변경해서 보내야만 하고,

받을 때도 Little-Endian 시스템은 전송되어 오는 데이터를 역순으로 조합해야 합니다.

또한 sockaddr_in 구조체 안에 존재하는 모든 값들은 '네트워크 바이트 순서' 즉 Big-Endian 방식으로 채워져야 합니다.

 

바이트 순서 전환

 

이번에는 위에 서 배웠던 두가지 형식의 바이트 순서를 변환해주는 함수에 대해서 살펴봅시다.

'h' : host byte order

'n' : network byte order

's' : short (16bit)

'l' : long (32bit)

 

위의 것들은 보고 함수를 살펴보면 함수의 의미를 충분히 짐작해 볼 수 있을 것입니다.

htonl이란? h to n 이므로 Host Byte 순서를 Network Byte 순서로 바꾸어 준다는 의미입니다.

또한 뒤에 붙는 l은 long 타입을 의미합니다.

따라서 32비트 데이터를 호스트 바이트 순서에서 네트워크 바이트 순서로 바꿔주는 함수입니다.

 

ntohs은 n to h 이므로 Network Byte 순서를 Host Byte 순서로 바꾸어 주는 함수입니다.

또한 s가 붙어 있으므로 short 타입의 데이터 변환을 의미합니다.

 

일반적으로 뒤에 s(short)가 붙은 함수는 s가 2바이트 데이터를 의미하므로

Port 정보의 바이트 순서를 변경하는데 사용되고,

 

l(long)이 붙은 함수는 l이 4바이트를 의미하므로 IP주소의 바이트 순서를 변경하는데 사용하게 됩니다.

자, 지금까지 언급해 왓던 변환 함수를 사용해서 임의의 데이터를 네트워크 바이트 순서로 출력해 보고,

호스트 바이트 순서로도 출력해 봅시다

만약에 두 출력결과가 다르다면, 당신의 시스템은 Little Endian 표기법을 사용하는 시스템일 것입니다.

 

(거의 대부분 이러한 결과를 얻게 될 것입니다. intel 이니깐)

 

5번째줄과 8번째줄에서 각각 2바이트(포트), 4바이트(IP) 데이터를 16진수 표현방식으로 대입했습니다.

물론 여러분들의 컴퓨터가 어떤 CPU냐에 따라 저장되는 바이트 순서는 다를 것 입니다.

 

11번째줄과 12번째 줄에서 호스트 바이트 순서에서 네트워크 바이트 순서로 변환하고 있습니다.

만약 CPU 내부 표현방식이 Big-Endian 방식이면 아무일도 일어나지 않을 것입니다.

왜냐하면 이럴경우 호스트 바이트 순서와 네트워크 바이트 순서가 같기 때문이죠.

단 Little-Endian 의 경우에는 함수 내에서 바이트 순서가 바뀌어 버립니다.

 

바뀌어 버렸죠..

 

만일 여러분들의 CPU가 Big-Endian 방식을 스고 있다 하더라도, 반드시 변환 함수를 사용해서 네트워크 바이트 순서로

변환하는 과정을 거쳐야 합니다. 그 이유는 '이식성'을 좋게 하기 위해서 입니다.

우리가 만든 프로그램이 Big-Endian 시스템에서만 사용된다는 보장을 할 수 없기 때문이죠.

 

인터넷 주소 조작하기

sockaddr_in 안에서 주소를 나타내기 위해 선언되어 있는 멤버의 데이터 타입이 unsigned long 이었죠.

따라서 우리는 IP 주소 정보를 할당하기 위해 unsigned long 타입으로 IP 주소를 표현할 수 있어야 합니다.

 

자 그렇다면 "203.249.68.52"의 unsigned long 값은 얼마가 될까요? 답이 바로 나오는 분들이 있을까요? 대부분 연습장이나 계산기를 필요로 합니다.

 

우리는 "203.249.68.52" 와 같은 점이 찍힌 십진수 표현 방식(이하 Dotted-Decimal Notation 으로 표현하겠습니다.)

에는 익숙하지만 unsigned long 타입으로 표현하는 것은 어쩐지 조금 어색하죠.

 

여기 인터넷 주소를 조작해 주는 여러 함수들을 소개하겠습니다.

Dotted-Decimal Notation의 주소 값을 unsigned long 타입으로 변환해 줄 뿐만 아니라,

네트워크 바이트 순서로의 변환도 알아서 해 줍니다.

 

inet_addr() : Dotted-Decimal Notation을 Big-Endian32비트 값으로 변환

성공 시 Big_Endian 32비트 값을 리턴하고, 오류 발생시 INADDR_NONE을 리턴합니다.

이번 예제는 Dotted-Decimal Notation 문자열을 unsigned long 타입의 데이터로 변경하는 예제입니다.

7번째 줄에는 inet_addr 함수의 오류 처리 능력을 확인하기 위해서 일부러 틀린 주소를 넣었습니다.

 

 

실행 결과, 잘못된 주소 전달시 INADDR_NONE이라는 오류가 리턴되고 변환은 이루어지지 않았음을 볼수 있습니다.

또한 INADDR_NONE은 실제로 -1이라는 값을 가짐도 확인했고,

결과값이 (0)4 03 02 01 로 Big-Endian 형식으로 변환됨을 알수 있습니다.

 

inet_aton() : inet_addr 함수 보다 개선된 데이터 변환 함수

성공 시 0이 아닌 값(True)를 리턴하고, 실패 시 0(false)이 리턴됩니다.

어떠한 의미에서 개선된 방법이냐.. 하고 물으신다면,

inet_addr 함수를 사용할 경우 변환된 데이터가 unsigned long타입의 값으로 리턴되기 때문에,

리턴된 값을 주소 정보 구조체 sockaddr_in 안에 선언되어 있는 in_addr구조체 안에 대입하는

과정을 거쳐야 합니다. 즉 변환된 값을 얻어오고 다시 대입하는 과정을 거쳐야 합니다.

 

그러나 inet_aton 함수를 사용할 경우 대입하는 과정을 따로 거치지 않아도 됩니다.

인자로 in_addr 구조체의 포인터를 전달하기 때문에 자동적으로 변환된 값이 대입됩니다.

개선이라기 보다는 편리한 함수죠.

다음 예제에서는 inet_aton 함수의 사용법을 익히는데 중심을 두기 바랍니다.

 

 

 

inet_ntoa() : 네트워크 바이트 순서의 32비트 값을 Dotted-Decimal Notation으로 변환

 

이쯤 오시면 또 대충 감이 오죠? 'n' 은 위에서 소개했던 Network byte order 고 'a' 는 Dotted-Decimal Notation을 의미합니다.

성공 시 변환된 해당 문자열의 포인터를 리턴, 실패 시 -1을 리턴

주의해야 할 사항은 리턴 타입이 문자열의 포인터라는 데에 있습니다. 이것이 왜 주의사항이 될까요?

그렇다면 한번 생각해 보죠.

문자열의 포인터가 리턴된다는 의미는 문자열을 저장할 장소가 어딘가에 존재한다는 의미가 되는데,

우리는 직접 제공하지 않고 이 함수를 호출하게 됩니다.

그리고 문자열의 포인터만 얻어내게 됩니다.

 

그렇다면 변환된 문자열이 저장된 저장소는 어디란 말인가요?

문자열의 저장소는 함수 내부에 선언되어 있는 static 버퍼가 됩니다.

C에서 static 키워드가 지니는 의미를 상기해 봅시다.

 

따라서 만약에 다시 한번 다른 주소 정보를 가지고 inet_ntoa 함수를 호출하게 되면

이 버퍼는 다른 데이터로 채워지게 됩니다.

결국 다시 한번 inet_ntoa 함수가 호출되기 전까지만 리턴된 포인터가 유효하다고 생각할 수 있습니다.

 

예제를 한번 살펴봅시다.

 

 

12번째 줄에서 addr1에 있던 주소 정보를 인자로 넘기면서 inet_ntoa 함수를 호출하고 있습니다.

또한 함수 호출 후에 결과로 리턴되는 포인터를 str에 대입하여 주고, 그 포인터가 가리키는 문자열을 13번째 줄에서 출력하고 있습니다.

15번째 줄에서는 addr2에 잇던 주소정보를 가지고 inet_ntoa 함수를 호출하고 있습니다. 그리고 나서

16번째 줄에서 다시 str 포인터가 가리키는 문자열을 출력해 주고 있습니다. 중요한 점은 우리는 결코 str 포인터를

12번째 줄에서 한번 지정해 준 다음에 바꿔 준 적이 없다는데 있습니다.

그러나 출력 결과를 보면 다른 결과가 출력됩니다.

 

 

같은 포인터가 가리키는 문자열을 두 번 출력 했음에도 불구하고 출력 결과는 다릅니다.

다시 한번 언급하지만 inet_ntoa 함수는 변경된 문자열을 내부에 선언한 static 버퍼에 저장하기 때문에 이러한 일이 발생합니다. 따라서 변경된 문자열 정보를 계속 유지해야 하는 경우에는 따로 복사를 하던가 해야합니다.

 

인터넷 주소 초기화

이제 지금까지 살펴본 내용을 가지고 인터넷 주소 정보를 나타내는 구조체 변수를 선언하고 초기화해 봅시다.

아마도 다음과 같은 코드가 일반적으로 사용 될 것입니다.

 

1번째줄에서 인터넷 주소 정보를 나타내는 구조체 변수 생성

2, 3번째줄에서는 IP주소와 Port를 선언

4번째줄에서는 memset 함수를 호출해서 인자로 전달된 구조체 변수를 0으로 초기화합니다. 구조체 변수의 모든 멤버를

적절한 값으로 초기화해 주는 경우 모든 멤버를 0으로 초기화할 필요는 없으나,

불필요한 데이터를 쓰레기 값으로 남겨두는 것은 문제의 소지가 될 수도 있고,

나중에 디버깅하는데 어려움을 줄 수도 있습니다.

5번째줄에서 프로토콜 체계 설정

6번째줄에서 IP주소를 설정해 주는데, 스트링을 네트워크 바이트 순서로 된 32비트 IP값으로 변환해서 대입해 주고 있습니다. sockaddr_in 멤버에는 네트워크 바이트 순서로 된 값을 대입해야 함을 잊지 맙시다.

7번째줄에서 atoi 함수를 사용하여 스트링을 정수 값으로 바꾸어주고, 다시 htons 함수를 통하여 네트워크 바이트 순서로 변환 해 주고 있습니다.

 

위의 코드에서는 2, 3번째 줄에 IP와 Port 정보를 코드에 직접 넣어주고 있지만,

사실 이러한 방법은 그리 좋은 방법이 아닙니다.

이렇게 구현하게 되면 다른 컴퓨터에서 실행할 때다마 코드를 변경해 주고 나서 다시 컴파일 해야 하기 때문입니다.

 

프로그램 실행 시 main 함수에 인자 값을 전달하는 방법이 이보다는 좋은 방법입니다.

다음과 같은 형식으로 주소 정보 구조체를 초기화할 수도 있습니다.

 

 

조금 전에 소개한 방법과의 가장 큰 차이점은 INADDR_ANY 상수를 통해서 주소를 할당하고 있다는 것입니다.

이러한 초기화 방법을 사용할 경우 현재 시스템의 IP 주소를 자동으로 찾아서 할당해 주기 때문에

시스템의 IP를 찾는 수고를 덜 수 있습니다.

뿐만 아니라, 만약에 시스템 내에 두 개 이상의 IP를 할당받아서 사용하는 경우

(MultiHomed 컴퓨터라 하며 일반적으로 라우터가 이에 해당한다.)

어떠한 주소를 통해서 들어오는 데이터도 모두 받아들이게 됩니다.

 

따라서 서버프로그램을 구현하는 경우에 많이 선호되는 방법입니다.

 

주소 정보 할당하기(bind 함수 해부)

주소 정보 구조체 변수를 생성하고 초기화하는 방법까지 살펴 보았으니,

이제는 소켓에 주소를 할당하는 일만 남았습니다.

다음에 소개되는 bind 함수가 소켓에 주소 정보를 할당하는 일을 합니다.

 

성공시 0을 리턴하고, 실패시 -1을 리턴합니다

● sockfd : 주소를 할당하고자 하는 소켓의 파일 디스크립터를 인자로 전달한다.

● myaddr : 할당하고자 하는 주소 정보를 지니고 있는 sockaddr_in 구조체 변수의 포인터를 인자로 전달한다.

전달되는 것은 sockaddr_in 구조체 변수의 포인터인데, 함수 선언에서 보면 sockaddr 구조체 변수의 포인터를 전달하라고 선언되어 있다.

● addrlen : 인자로 전달된 주소 정보 구조체의 길이를 전달한다.

 

함수 호출이 성공하면 sockfd가 가리키는 소켓에 myaddr이 가리키는 주소 정보가 할당됩니다. 

bind 함수 선언에서 두 번째 인차 타입이 sockaddr* 로 선언되어 있음을 다시 한번 확인하고 다음 예제를 봅시다.

 

특이한 점은 bind 함수의 두 번째 인자 타입이 sockaddr*로 선언되어 있다는 것입니다.

그래서 위의 예제에서 bind 함수 호출 시 형 변환을 통해서 인자를 전달했습니다.

일단 sockaddr구조체가 어떻게 생겼는지 보고 나서 그 이유를 알아봅시다.

 

다음은 sockaddr 구조체의 선언입니다. 

 

프로토콜 체계에 따라 주소 체계가 다르다고 한 것을 기억하시죠? socket 함수와 bind 함수는 특정 프로토콜

체계를 위한 함수가 아니기 대문에 (프로토콜에 독립적이기 때문에) 일반적으로 (범용적으로 사용할수 있게)

선언되어야 합니다.

  

즉, bind 함수는 인터넷 프로토콜을 위한 sockaddr_in 구조체의 포인터도 인자값으로 받을 수 있어야 하지만,

Local UNIX 프로토콜을 위한 sockaddr_un 구조체의 포인터도 인자값으로 받을 수 있어야 합니다.

따라서 sockaddr이란 일반적인(범용으로 사용 가능한) 구조체를 선언하고 인자 타입으로 지정해 준 것입니다.

 

반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기