티스토리 뷰

프로그래밍/Network

Socket Introduction (2)

국윤창 2017. 5. 22. 12:01

3. Value-Result Arguments

이전에 언급했듯이, socket address 구조체가 socket function에 전달될 때, socket address 구조체는 항상 reference로 전달된다. 이 말은 구조체의 포인터가 전달된다는 얘기이다. 구조체의 길이 또한 매개변수로서 전달된다. 그러나 길이 매개변수가 넘겨지는 방향은 socket address 구조체가 넘겨지는 방향에따라 달라진다. (프로세스에서 커널로, 또는 반대로)


1) bind, connect, sendto 세개의 함수는 socket address 구조체를 프로세스에서 커널로 전달한다. 이 세개의 함수의 매개변수 중 하나는 socket address 구조체에 대한 포인터이며, 다른 매개변수 하나는 구조체의 integer 사이즈이다. 예를 들면 아래 그림 1과 같다.

[그림 1] the process to the kernel socket function


커널은 구조체의 포인터와 구조체의 크기를 둘 다 전달받기 때문에, 프로세스로부터 커널로 얼마만큼의 데이터를 복사 해야할 지 정확히 알고있다. 아래 그림 2는 이 시나리오를 보여준다. 

[그림 2] Socket address structure passed from process to kernel.


다음 챕터에서 socket address 구조체의 사이즈 데이터타입이 사실 int가 아니라 socklen_t임을 보게될 것이다. 그러나 POSIX 명세에는 socklen_t가 uint32_t로 정의되는 것을 추천한다.


2) accept, recvfrom, getsockname, getpeername 네개의 함수는 socket address 구조체를 커널에서 프로세스로 전달한다. 방금 전 시나리오와는 반대이다. 이 네개의 함수의 두 매개변수는 socket address 구조체의 포인터와 구조체의 크기를 담고있는 integer의 포인터이다. 예를 들면 아래 그림 3과 같다.

[그림 3] the kernel to the process socket function


사이즈의 타입이 integer에서 integer의 포인터로 바뀐 이유는 함수가 호출될 때는 value로 쓰이고(커널에게 구조체 사이즈를 알려준다.), 함수가 반환될 때는 result로 쓰이기 때문이다.(프로세스에게 커널이 구조체에 실제로 얼마만큼의 정보를 저장했는지 알려준다.) 이런 매개변수 타입을 value-result 매개변수라고 부른다. 아래 그림 4에 이 시나리오가 나와있다.

[그림 4] Socket address structure passed from kernel to process.


value-result 매개변수에 관한 예제는 챕터 4에서 보게될 것이다.

지금까지 socket address structure가 프로세스와 커널 사이에서 전달되는 것을 다뤘다. 모든 socket function들이 커널 내 시스템 호출인 4.4BSD와 같은 운영체제를 구현하기 위해선 이러한 방법이 맞다. 그러나 특히 System V같은 몇몇 운영체제에선 socket function들은 유저 프로세스의 일부로서 실행되는 라이브러리 함수일 뿐이다. 어떻게 이러한 함수의 interface가 커널 내 프로토콜 스택과 결합되었는지는 구현 세부사항이기 때문에 일반적으로 우리에게 영향을 주지 않는다. 그렇지만 간결함을 위해 이러한 구조체들을 계속 socket function들에 의해 프로세스와 커널 사이에서 전달되는 구조체로 간주한다. (부록 C.1에서 System V 구현이 사실 socket address 구조체를 프로세스와 커널 사이에서 전달하는 것을 볼 것이다. 그렇지만 STREAMS 메시지의 일부로 전달한다.)

두 개의 다른 함수 recvmsg, sendmsg도 socket address 구조체를 전달한다. 그러나 길이 필드가 함수의 매개변수가 아니라 구조체의 멤버인 것을 볼 것이다. (챕터 14에서)


socket address 구조체의 길이를 value-result 매개변수로 사용할 때, 만약 socket address 구조체가 고정길이이면, 커널에서 반환되는 값은 항상 고정 크기이다. (예를들면, IPv4 sockaddr_in : 16, IPv6 sockaddr_in6 : IPv6) 그러나 가변길이 socket address 구조체(예를 들면, Unix domain sockaddr_un)일 때, 반환되는 값은 구조체의 최대 크기보다 작을 수 있다. (챕터 15의 그림 15.2에서 볼 수 있다.)

네트워크 프로그래밍에서 가장 흔한 value-result 매개변수 예제는 반환되는 socket address 구조체의 길이이다. 그러나 이 책에서 우리는 다른 value-result 매개변수들을 보게 될 것이다.

* select 함수의 중간에 3개 매개변수 (Section 6.3)

* getsockopt 함수의 길이 매개변수 (Section 7.2)

* recvmsg 함수를 사용할 때 msghdr 구조체의 msg_namelen, msg_controllen 멤버변수 (Section 14.5)

* ifconf 구조체의 ifc_len 멤버변수 (챕터 17의 그림 17.2)

* sysctl 함수의 두개의 길이 매개변수 중 첫 번째 (Section 18.4)


4. Byte Ordering Functions

2바이트짜리 정수가 있다고 가정하자. 이 정수를 저장하는 방법에는 두 가지가 있다. 하나는 낮은 순서(low-order)가 시작 주소에 오는 것이고 little-endian byte order로 알려져있다. 또 하나는 높은 순서(high-order)가 시작 주소에 오는 것이고 big-endian byte order로 알려져있다. 아래 그림 5에 이 두가지 방법이 나와있다.

[그림 5] Little-endian byte order and big-endian byte order for a 16-bit integer.


위 그림에서 little-endian byte order는 메모리 주소가 오른쪽에서 왼쪽으로 증가하는 것을 볼 수 있고, big-endian byte order는 왼쪽에서 오른쪽으로 증가하는 것을 볼 수 있다. 그리고 위 그림에서 가장 왼쪽에 있는 비트인 most significant bit(MSB)와 가장 오른쪽에 있는 비트인 least significant bit(LSB)를 볼 수 있다.

"little-endian"과 "big-endian" 용어는 multibyte value의 시작주소의 값이 작은지 큰지를 지칭한다.

문제는 이 두가지 byte ordering 사이에 표준이 없다는 것이다. 그리고 앞으로 이 두가지 포멧을 만나게 될 것이다. 주어진 시스템에서 사용하는 byte order를 host byte order라고 부를 것이다. 아래 그림 6에 나와있는 프로그램은 host byte order를 출력한다.

[그림 6] Program to determine host byte order


위 프로그램은 2바이트의 값 0x0102를 short integer에 저장하고, byte order를 확인하기 위해 연속된 2 바이트 c[0](그림 5의 주소 A), c[1](그림 5의 주소 A+1)를 살펴본다.

문자열 CPU_VENDOR_OS는 이 책 내의 소프트웨어가 구성되고 CPU 타입, 제조사, OS release가 식별될 때, GNU autoconf 프로그램에 의해 결정된다. 아래 그림 7에 이 프로그램을 다양한 시스템에서 실행했을 때 출력이 나와있다.

[그림 7] CPU_VENDOR_OS with various systems


여기서는 16bit integer를 다뤘지만 32bit integer의 경우도 똑같다.

최근에는 little-endian, bit-endian 바이트 순서 사이를 시스템이 reset할 때 또는 실행 중에, 왔다갔다 할 수 있는 다양한 시스템이 있다.

우리는 반드시 이런 byte order 차이를 네트워크 프로그래머로서 다뤄야한다. 왜냐하면 network protocol들은 반드시 byte order를 명시해야하기 때문이다. 예를들어, TCP에선 16bit의 port와 32bit의 IPv4 주소가 있다. protocol stack을 송신, 수신하는 것은 반드시 그 byte order에 동의해야한다. Internet protocol은 multibyte interger에 대해서 bit-endian byte order를 사용한다.


이론적으로, 이러한 세부사항에 대한 걱정을 막기 위해서, 네트워크 구현은 socket address 구조체의 필드들을 host byte order로 저장할 수 있고 protocol header로 보내거나 받을 때 network byte order로 변환할 수 있다. 역사적으로나 POSIX 명세를 봤을 때, socket address 구조체의 특정 필드들은 반드시 network byte order로 존재해야한다. 그러므로 우리는 host byte order를 어떻게 network byte order로 변환하냐를 고민해야한다. 아래 그림 8에 두 byte order 사이에서 변환하는 4개의 함수가 나와있다.

[그림 8] Converting byte order functions

 

함수 이름에서 h는 host를 나타내고, n은 network, s는 short, l은 long을 나타낸다. "short"와 "long"라는 용어는 4.2BSD Digital VAS 구현의 역사적인 유물이다. 우리는 대신 s를 16-bit 값으로 생각해야 하고(TCP 또는 UDP 포트 넘버와 같은) l은 32-bit 값으로 생각해야 한다.(IPv4 주소와 같은) 사실 64-bit Digital Alpha에선, long integer가 64bit를 차지하지만 htonl, ntohl 함수는 32-bit 기준으로 동작한다.

이러한 함수를 사용할 때, host byte order와 network byte order이 big-endian인지 little-endian인지 신경 쓸 필요 없다. 우리가 꼭 해야하는 것은 host, network byte order 사이에서 주어진 값을 변환하는 적절한 함수를 호출하는 것이다. Internet protocol(bit-endian)과 같이 byte order가 같은 시스템에선 이 네개의 함수는 보통 null macro로 정의된다.

 

프로토콜 헤더 안의 필드들 말고 네트워크 패킷 안의 데이터의 byte ordering 문제에 대해 Section 5.18과 Exercise 5.8에서 설명할 것이다.

 

우리는 아직 byte라는 용어에 대해 정의하지 않았다. 모든 컴퓨터 시스템이 byte를 8-bit로 쓰기 때문에, byte를 8-bit 크기로 생각한다. 대부분의 Internet 표준들은 8-bit 크기를 의미하기 위해 byte 대신 octet이라는 용어를 쓴다. 이 용어는 TCP/IP의 초창기에 쓰기 시작했다. TCP/IP의 초기 작업의 대부분은 DEC-10같은 byte를 8-bit로 쓰지 않는 시스템에서 완료되었기 때문이다.

 

Internet 표준에서 또다른 중요한 관습은 bit ordering이다. 많은 Internet 표준에서 packet들의 "그림"을 아래 그림 9와 같은 형태로 보게 될 것이다. (그림 9는 RFC 791 IPv4 header의 처음 32bit를 보여준다.)

[그림 9] the first 32 bits of IPv4 header from RFC 791

위 그림 9은 4바이트를 순서대로 와이어 위에 표시한 것이다. 가장 왼쪽 비트가 MSB(most significant bit)이다. 그러나, MSB가 0으로 시작한다. RFC안의 프로토콜 정의들을 쉽게 읽기 위해서는 이런 표기법을 잘 알고 있어야한다.

 

1980년대의 흔한 네트워크 프로그래밍 오류 중 하나는 Sun workstation(bit-endian Motorola 68000s)에서 코드를 개발하고 이 네개의 함수 호출을 깜빡하는 것이었다. 이런 workstation에서는 코드가 잘 작동하지만, little-endian 기계(예를들면 VAXes)로 옮기면 작동을 안하는 문제가 발생하기 때문이다.

 

'프로그래밍 > Network' 카테고리의 다른 글

Socket Introduction (3)  (0) 2017.06.22
Socket Introduction (1)  (0) 2017.04.05
Transport Layer (3)  (0) 2017.03.13
Transport Layer (2)  (0) 2017.02.24
Transport Layer (1)  (0) 2017.02.21
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/03   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함