반응형

11 소켓

이 장에서는 소켓을 사용하여 프로세스간의 통신에 관한 GNU장치에 대해 서술한다.

소켓은 일반적인 프로세스간의 통신 채널이다. 파이프처럼 소켓은 파일 서술자처럼 표현된다. 그러나 파이프와는 달리 소켓은 관련 없는 프로세스간의 통신, 네트워크를 통하여 서로 다른 컴퓨터에서 수행되고 있는 프로세스간의 통신을 제공한다. 소켓은 주로 다른 컴퓨터간의 통신의 수단이다. telnet, rlogin, ftp, talk, 그리고 네트워크 프로그램들은 소켓을 사용한다. 모든 운영체제가 소켓을 지원하는 것은 아니다. GNU라이브러리에서 'sys/socket. h' 은 운영체제와 상관없이 존재한다. 그러나 만약 시스템이 소켓을 지원하지 않는다면, 소켓함수는 항상 실패한다.   미완성: 우리는 현재 인터넷 인터페이스 설정이나 브로드캐스트 메시지기능에 관하여는 문서화하지 못했다.


11. 1 소켓 개념

당신이 소켓을 만들 때, 반드시 당신이 어떤 통신 스타일을 쓸 것인지, 어떤 프로토콜을 사용할 것인지 명시해야 한다. 소켓의 통신 스타일은 소켓으로 데이터를 보내고 받는 유저레벨 구문을 정의한다. 통신 스타일을 선택하는 것은 다음과 같은 질문의 응답이 된다.

데이터 전송의 단위는 무엇인가?

어떤 통신 스타일은 큰 구조체를 가지지 않는, 바이트의 열로 된다. 다른 것들은 (패킷 같은) 레코드 안에 바이트를 묶어 놓는다.

보통 작업을 하는 동안에 데이터를 잃어버릴 수 있나?

어떤 통신 스타일에서 보내진 데이터가 (시스템이나 네트워크 충돌을 제외하고) 보내진 순서대로 모두 도달해야 한다. ; 다른 스타일은 때로는 보통 동작 시에도 데이터를 잃어버리거나, 하나 이상의 같은 패킷이 전달될 수도 있고, 순서가 뒤바뀔 수도 있다.
신뢰 있는 통신스타일을 사용하는 프로그램을 설계하는 것은 보통 데이터 손실이나, 순서가 뒤바뀌는 것을 검출하고 필요에 따라 재 전송하는 대책을 포함한다.

상대측과 완전한 통신을 하느냐?

어떤 통신스타일은 전화 거는 것과 같이 상대측과 원격 소켓 연결을 만들고 나서, 자유롭게 데이터를 교환한다. ; 다른 스타일은 편지 쓰는 것처럼 당신이 보내고자하는 메시지의 주소를 써서 보낸다.
당신은 또한 소켓의 이름공간에서 이름을 선택해야 한다. 소켓이름("주소")은 특정한 이름공간의 의미심장한 내용이 있다. 이름공간은 또한 "도메인"이라고 불린다. 그러나, 우리는 같은 용어의 다른 사용과 혼돈할 수 있어 그 말을 피하겠다. 각 이름공간은 'PF_'로 시작하는 상징적인 이름을 가진다. 'AF_'로 시작하는 유사한 상직적 이름은 그 이름공간에 주소 포맷을 가리킨다.
마지막으로, 당신은 통신을 제공하는 프로토콜을 선택해야 한다. 프로토콜은 데이터를 주고받는데 사용되는 저수준 메커니즘을 말한다. 각 프로토콜은 특정한 이름공간과 통신스타일에 대하여 유효하다. ; 이름공간은 종종 프로토콜 패밀리라고 불린다. 왜냐하면 이름 공간이 'PF_'로 시작하기 때문이다.
프로토콜의 규칙은 비록 다른 컴퓨터에서일지라도 두 프로그램간의 데이터 전송을 제공한다. 이러한 규칙의 대부분은 운영체제에 의하여 다루어지고, 당신이 그것에 대해 알 필요는 없다. 당신이 프로토콜에 대하여 알아야 할 것은 이것이다. :

두 소켓간의 통신을 위하여, 그것들은 반드시 같은 프로토콜을 나타내야 한다.

각 프로토콜은 특정한 스타일과 이름공간 결합에 의미를 가지며, 적당하지 않은 결합의 사용은 할 수 없다. 예를 들어 TCP 프로토콜은 바이트 스트림 스타일의 통신과 인터넷 이름공간에 맞추어져 있다.
스타일과 이름공간의 각 조합에서 프로토콜 번호를 0으로 나타내어 요구하면 그것은 기본프로토콜(디폴트 프로토콜)로 된다. 그리고, 당신은 보통 하는 것(디폴트를 사용하는 것)이 이런 것이다.


11. 2 통신스타일

GNU라이브러리는 각기 다른 특성을 지닌 몇 종류의 소켓을 지원하고 있다. 이 절에서는 지원되는 소켓 형태를 서술한다. 여기 나타난 심볼릭 상수는 'sys/socket. h'에 정의되어 있다.

int SOCK_STREAM Macro

SOCK_STREM스타일은 파이프와 같다. (10장[파이프와 FIFO] 을 보라). 그것은 특정한 원격 소켓과의 연결 후에 동작한다. 그리고 바이트의 스트림처럼 신뢰성 있는 데이터 전송을 한다. 이 스타일의 사용은 11. 8절에 자세히 나와 있다.

int SOCK__DGRAM Macro

SOCK_DGRAM 스타일은 개별적으로 주소가 쓰여진 패킷을 전달하는데 사용된다. 그것은 SOCK_STREAM과 정반대이다. 당신이 이 종류의 소켓에 데이터를 써넣는다. 그러면 그 데이터는 하나의 패킷이 된다. SOCK_DGRAM 소켓은 연결을 가지지 않는다. 당신은 각 패킷에 반드시 반복적으로 주소를 써넣어야 한다. 데이터를 전송하고자 하는 당신의 요구에 관하여 시스템의 책임은, 각 패킷이 가장 잘 배달될 수 있도록 시도하는 것이다. 그것은 네 번째와 다섯 번째 실패 후에 여섯 번째에 성공할 수 있다. ; 일곱 번째 패킷은 여섯 번째 전에 도착될 수도 있다. 그리고 여섯 번째 후에 두 번 도착할 수도 있다. SOCK_DGRAM은 일반적으로 응답해야할 시간내에 응답이 없을 때 간단히 패킷을 재 전송하는데 적합하다. 데이터그램 소켓을 사용하는 방법에 대한 자세한 정보는 11. 9절[데이터그램]을 보라

int SOCK__RAW Macro

이 스타일은 저수준 네트워크 프로토콜과 인터페이스를 억세스하도록 해 준다. 보통 사용자 프로그램은 이 스타일을 자주 사용할 필요가 없다.


11. 3 소켓 주소

소켓의 이름은 보통 "주소"라고 불린다. 소켓주소와 관계된 기호(symbol)와 함수들은, 때때로 "이름"이라는 말이나 "주소"를 사용되어, 어떤 규칙 없이 이름 지어진다. 당신은 소켓이 고려되는 곳에 이러한 말을 동일한 것처럼 생각할 수 있다. socket함수로 새롭게 만든 소켓은 주소를 가지지 않는다. 당신이 그것에게 주소를 주어야만, 다른 프로세스가 통신을 위한 주소를 찾을 수 있게 된다. 이것을 주소를 소켓에 묶는다고 하고, bind함수를 이용한다. 만약 다른 프로세스들이 주소일 찾고, 주소를 이용하여 통신을 시작하는 것이라면, 당신은 소켓의 주소를 생각해 볼 필요가 한다. 당신은 다른 소켓의 주소를 규명할 수 있지만, 그것은 보통 무의미하다; 처음, 소켓으로부터 데이터를 보내거나 연결을 초기화하기 위해 그것을 사용할 때, 만약 구체적으로 나타내진 것이 없다면, 시스템은 주소를 자동으로 설정한다.

서버는 주소에 기초하여 판별되기 때문에, 보통 클라이언트는 주소를 명시해야 한다. ; 예를 들어, rsh 와 rlogin 프로토콜은 클라이언트의 소켓주소를 살피고, 만약 그것이 IPPORT_RESER

VED보다 작지 않다면, 암호검사를 거쳐야 한다. ( 11.5.3절의 [포트] 을 보라)

소켓주소의 자세한 것은 당신이 사용하는 어떤 이름공간에 의존하여 변한다. 자세한 정보를 위해서는 11. 4절 [파일 이름공간] 을 보거나, 11. 5절 [인터넷 이름공간] 참조. 이름공간에 상관없이 , 소켓의 주소를 세트하고 사용하기 위해 당신은 같은 bind와 getsockname함수를 사용한다. 이러한 함수들은 주소를 받아들이기 위해 struct sockaddr *라는 위조 자료형(phony data type)을 사용한다. 실제적으로, 주소는 당신이 사용하는 주소포맷과는 다른 자료형의 구조로 되어 있다. 그러나, 당신은 그것을 bind하기 위해 넘겨줄 때, 그것의 주소를 struct sockaddr *로 자료형 변환(cast)을 해주어야 한다.


11. 3. 1 주소 포맷

bind와 getsockname함수는 소켓의 주소의 포인터를 의미하는 struct sockaddr *이라는 일반적인 자료형을 사용한다. 당신은 주소를 번역하거나 만들기 위해 이 자료형을 효율적으로 사용할 수 없다; 대신, 소켓의 이름공간을 위한 자료형을 사용해야만 한다. 그러므로, 보통 실제적인 사용은 적합한 이름공간의 형태의 주소를 만드는데 사용하고 나서, bind나 getsockname을 호출할 때 struct sockaddr *로 포인터형태를 변환한다. 당신이 struct sockaddr 자료형으로부터 얻을 수 있는 정보 중 하나는, 주소를 완전히 이해하기 위해 사용되는 자료형이 어떤 것인지 알려주는 "주소 포맷" 지정자이다. 이 절에서의 기호는 'sys/socket. h'라는 헤더파일안에 정의되어 있다.

struct sockaddr 자료형

struct sockaddr형 자체는 다음 멤버를 가지고 있다:

    short int sa_family

    이것은 이 주소의 주소 포맷용 코드이다. 그것은 다음에 나오는 데이터의 포맷을 지시한다.

    char sa_data[14]

    이것은 실제적인 소켓주소 데이터이다. 이것은 포맷에 의존한다. 그것의 길이는 또한 포맷에 의존하고 아마 14보다 클 것이다. sa_data의 14라는 길이는 원래 변할 수 있다. 각 주소포맷은 AF_로 시작하는 기호이름으로 시작된다. 그것들은 각각 이름공간에 대응되는 'PF_' 기호에 맞춰져 있다.

AF_FILE

이것은 파일 이름공간에 맞는 주소포맷이다. (PF_FILE은 이 이름공간의 이름이다. ) 이 주소 포맷의 정보를 보려면 11. 4. 2절 [파일 이름공간 구체적 내용] 을 보라.

AF_UNIX

이것은 호환성을 위한 AF_FILE과 같은 말이다. (PF_UNIX는 PF_FILE과 역시 동의어이다. )

AF_INET

이것은 인터넷 이름공간에 맞는 주소포맷이다. (PF_INET은 이 이름공간의 이름이다. ) 11. 5. 1절 [인터넷 주소 포맷] 을 보아라.

AF_UNSPEC

이것은 특정한 주소포맷을 지정하지 않는다. 그것은 "연결된" 데이터그램 소켓의 기본(default)목적지 주소를 지우는 것 같은 드문 경우에만 사용된다. 11. 9. 1절[데이터그램 보내기] 을 보아라. 여기에 맞는 이름 공간지정자 기호도 완전성을 위해 존재한다. 그러나, 그것을 프로그램에서 사용할 필요는 없다. 'sys/socket. h' 에서 대부분 실제로 구현되지 않은 많은 다른 종류의 네트워크에 'AF_'로 시작하는 기호를 정의하고 있다. 어떻게 그것을 사용하는 것에 관한 정보를 우리가 받으면, 우리는 그것을 정말로 문서화할 것이다.

11. 3. 2 소켓의 주소를 세팅하기

소켓에 주소를 할당하기 위하여 bind함수를 사용하라. bind의 프로토타입은 'sys/socket. h'헤더파일에 있다. 사용예는 11. 4절[파일 이름공간], 또는 11. 5. 7절[Inet 예제] 을 보라.

int bind (int socket, struct sockaddr *addr, size_t length) Function

bind함수는 "socket"소켓에 주소를 지정한다. addr과 length인수는 주소를 구체화한다. 자세한 주소포맷은 이름공간에 의존한다. 주소의 첫 번째 부분은 항상 포맷지정자이다. 포맷지정자는 이름공간을 명시하고, 그 주소가 그 이름공간의 포맷의 주소임을 말한다. 성공하면 0을 리턴하고, 실패하면 -1을 리턴한다.
다음은 이 함수에 정의되어 있는 errno 에러조건이다.

EBADF : 소켓 인수가 유효하지 않은 파일 지정자이다.

ENOTSOCK : 서술자 socket이 소켓이 아니다.

EADDRNOTAVAIL : 지시된 주소가 이 장치에서는 유효하지 않다.

EADDRINUSE : 또 다른 소켓이 이 주소로 이미 사용되고 있다.

EINVAL : socket소켓이 이미 주소를 가지고 있다.

EACCESS

당신은 요구된 주소를 제어할 허가권을 가지고 있지 않습니다. (인터넷 도메인에서, 단지 슈퍼유저만이 0에서 IPPORT_RESERVED-1까지 범위의 포트넘버를 사용할 수 있다. 11. 5. 3절[ports] 보라. )

추가적인 조건은 소켓의 각각 이름공간에 의존한다.

11. 3. 3 소켓의 주소 읽기

getsockname함수를 사용하여, 인터넷 소켓의 주소를 확인할 수 있다. 이 함수의 프로토타입은 'sys/socket. h'라는 헤더파일에 있다.

int getsockname (int socket, struct sockaddr *addr, size_t *length_ptr) 함수

getsockname함수는 addr과 length_ptr 인수로 명시되는 곳의 socket이라는 소켓의 주소에 관한 정보를 리턴한다. length_ptr은 포인터임을 주목하라; 당신이 addr의 크기 할당을 초기화할 것이다. 당신은 addr의 할당크기를 초기화 할 것이고, 주소데이터의 실제크기를 포함한 것이 리턴될 것이다.
주소데이터의 포맷은 소켓 이름공간에 의존한다. 정보의 길이는 주어진 이름공간에 보통 맞추어진다. 그래서, 보통 당신은 필요한 공간이 정확히 얼마인지 알 수 있고, 그것만큼 제공할 수 있다. 실제적인 사용은 소켓의 이름공간에 적합한 자료형을 사용하는 값으로 공간을 할당하고 나서, 그것을 getsockname으로 넘기기 위해 그 주소를 struct sockaddr*로 형변환(cast)한다.
리턴 값 0은 성공이고, -1은 에러이다. 다음 에러조건은 이 함수에서 정의된 것이다.

EBADF : 소켓인수가 유효한 파일 서술자가 아니다.

ENOTSOCK : 서술자 socket이 소켓이 아니다.

ENOBUFS

동작을 수행할만한 충분한 버퍼가 없다. 당신은 이름공간의 소켓의 주소를 읽을 수 없다. 이것은 시스템의 나머지 사항에 일치한다. 일반적으로, 그 파일의 서술자로부터 파일의 이름을 얻어낼 방법은 없다.


11. 4 The File Namespace

이 부분은 상징적인 이름(소켓을 만들 때 요구하는) 은 PF_FILE이라는 파일 이름공간에 대해 상세히 설명한다.


11. 4. 1 파일 이름공간 개념들

파일이름공간에서, 소켓 주소는 파일 이름들이다. 당신은 소켓의 주소처럼 당신이 원하는 어떤 파일 이름을 명시할 수 있다. 그러나 당신은 반드시 그것을 포함하는 디렉토리에 쓰기 허가를 가지고 있어야 한다. 소켓에 연결하기 위해, 당신은 그것에 대한 읽기 허가도 가지고 있어야 한다. 보통 /tmp디렉토리 안에 그러한 파일들을 넣는다.

파일이름공간의 특별한 점 하나는 연결을 할 때 그 이름이 한 번만 사용된다는 것이다. 즉, 그것을 한 번 사용하고 나면, 그 주소는 의미가 없고, 존재하지 않을지도 모른다.

또 다른 특징은 만약 소켓의 이름을 포함하는 파일시스템을 다른 기기가 공유하지 않는다면 다른 기기의 어떤 소켓으로 연결할 수 없다는 것이다. 당신은 디렉토리 리스트 안에서 소켓을 볼 수 있으나, 그것에 연결하는 것은 불가능하다. 어떤 프로그램들은 클라이언트가 자기 프로세스 ID를 보내도록 요구하는 것과 프로세스ID를 사용하여 클라이언트간을 식별하는 것을 이용하는 것 등을 이용한다. 그러나, 우리는 당신이 설계하는 프로토콜들 안에서 이 방법을 사용하지 말기를 원한다. 우리는 같은 파일시스템을 사용하는 다른 기기로부터의 연결들을 허락해야 할지도 모르기 때문이다. 대신에, 만약 그것을 가지는 것을 원한다면, 각 클라이언트에게 식별번호를 보낸다.

파일 이름공간안의 소켓을 닫은 후에, 당신은 파일시스템으로부터 파일이름을 지워야 한다. unlink나 remove를 사용하여 이것을 하라. 9. 5절 [파일 삭제하기] 을 보라.

파일 이름공간은 단지 어떤 통신 스타일에 대해 하나의 프로토콜만 지원한다. 그것은 프로토콜 번호 0이다.


11. 4. 2 파일이름 공간의 자세한 것

파일 이름 공간에 소켓을 만들려면, socket또는 sockerpair의 이름공간 인수에 상수 PF_FILE을 사용하라. 이 상수는 'sys/socket. h'에 정의되어 있다.

int PF__FILE Macro

이것은 소켓주소가 파일이름인 파일이름 공간과 그것이 연합된 프로토콜 패밀리를 가리킨다.

int PF__UNIX Macro

이것은 호환성을 위한 PF_FILE의 동의어이다.
파일이름공간의 소켓이름을 명시하는 구조체는 'sys/un. h'라는 헤더파일에 정의되어 있다.

struct sockaddr__un Data Type

이 구조체는 파일이름공간을 소켓 주소로 명시하는데 사용된다.

short int sun_family

이것은 소켓주소를 위한 형태나 주소패밀리를 명시한다. 당신은 파일이름공간을 지정하기 위하여 AF_FILE값을 저장해야 한다. 11. 3절 [소켓주소] 을 보라.

char sun_path[108]

이것은 사용되는 파일이름이다.
미완성: 왜 108이 매직넘버인가? RMS는 이것을 길이가 0인 배열을 만드는 것과 파일이름의 길이에 기초하여 적합한 저장공간을 할당하기 위해 allocate를 사용하는 것과 같은 예를 꼬집어 제안한다.

당신은 sun_family요소의 크기와 파일이름문자열의 (할당 크기가 아닌)문자열 길이의 합인, 파일이름공간에 소켓주소를 위한 길이 매개변수를 계산해야 한다.


11. 4. 3 파일-이름공간 소켓의 예

여기 있는 것은 파일이름공간의 소켓을 만들고 이름지어주는 방법을 보여주는 예이다.

#include <stddef. h>
#include <stdio. h>
#include <errno. h>
#include <stdlib. h>
#include <sys/socket. h>
#include <sys/un. h>
int
make_named_socket (const char *filename)
{
struct sockaddr_un name;
int sock;
size_t size;
/* 소켓 만들기 */
sock = socket (PF_UNIX, SOCK_DGRAM, 0);
if (sock < 0) {
perror ("socket");
exit (EXIT_FAILURE);
}
/* Bind a name to the socket. */
name. sun_family = AF_FILE;
strcpy (name. sun_path, filename);
/* The size of the address is the offset of the start of the filename, plus its length, plus one for the terminating null byte. */
size = (offsetof (struct sockaddr_un, sun_path) + strlen (name. sun_path) + 1);
if ( bind (sock, (struct sockaddr *) &name, size) <0) {
perror ("bind");
exit (EXIT_FAILURE);
}
return sock;
}


11. 5 인터넷 이름공간

이 장에서는 프로토콜과 소켓이 인터넷 이름공간에서 사용되는 이름짓는 방법에 대해 서술한다. 인터넷 이름공간에서 소켓을 만들려면, socket이나 socketpair의 이름 공간인수로 이 이름공간의 PF_INET 기호를 사용한다. 이 매크로는 'sys/socket. h'에 정의되어 있다.

int PF__INET Macro

이것은 인터넷 이름공간을 지정하고 프로토콜의 연합된 패밀리를 지정한다.

인터넷 이름공간의 소켓 주소는 다음과 같은 성분을 포함한다:

당신이 연결하기 원하는 장치의 주소. 인터넷 주소는 몇 가지 방법으로 표현될 수 있다. 이러한 방법들은 11. 5. 1절 [호스트 주소]11. 5. 2. 4절 [호스트 이름] 에서 논의한다.

그 장치의 포트 번호. 11. 5. 3절 [포트] 을 보라

당신은 주소와 포트번호가 네트워크 바이트 순서라고 불리는 정규화 된 포맷으로 표현된다는 것을 알아야 한다. 이에 관한 정보는 11. 5. 5절 [바이트 순서] 을 보라


11. 5. 1 인터넷 소켓 주소 포맷

인터넷 이름공간에서, 소켓주소는 호스트의 주소와 그 호스트의 포트를 포함한다. 추가적으로 당신이 선택한 프로토콜은 주소의 일부분처럼 효과적으로 제공된다. 왜냐하면 지역적인 포트번호는 특정한 프로토콜 안에서 의미를 가지기 때문이다. 인터넷 이름공간안의 소켓 주소의 표현에 관한 자료형은 'netinet/in. h' 헤더파일에 정의되어 있다.

struct sockaddr__in Data Type

이것은 인터넷 이름공간안의 소켓주소를 표현하는데 사용되는 자료형이다. 그것은 다음 멤버를 가진다.

short int sin_family

이것은 소켓주소의 포맷이나 주소 패밀리를 지시한다. 당신은 이 멤버내에 AF_INET값을 저장해야 한다. 11. 3절 [소켓주소] 을 보라

struct in_addr sin_addr

이것은 호스트 장치의 인터넷 주소이다. 어떻게 여기에 넣을 값을 얻는지에 관하여는 11. 5. 2절 [호스트 주소]11. 5. 2. 4 [호스트 이름] 을 보라.

unsigned short int sin_port

이것은 포트번호이다. 11. 5. 3절 [포트] 을 보라.

당신이 bind나 setsockname을 호출할 때, 만약 당신이 인터넷 이름공간 소켓 주소를 사용한다면, '길이' 매개변수로 sizeof (struct sockaddr_in)을 명시해야 한다.


11. 5. 2 호스트 주소

인터넷의 각 컴퓨터는 하나 또는 그 이상의 인터넷 주소를 가진다. 주소는 인터넷의 모든 것들 사이에서 그 컴퓨터를 지시하는 숫자이다. 사용자는 통상 숫자로된 호스트 주소를 사용한다. 그것은 '128. 52. 46. 32'처럼 네 숫자로 되어 있고 점으로 나뉘어져 있다. 각 컴퓨터는 또한 하나 또는 그 이상의 호스트 이름을 가진다. 그것은 'churchy. gnu. ai. mit. edu'처럼 점으로 나뉘어진 단어의 문자열이다.

사용자가 호스트를 명시하게 하는 프로그램은 전형적으로 숫자주소와 호스트 이름 모두를 수용한다. 그러나 프로그램은 연결을 위해 숫자로된 주소를 필요로 한다. 즉 호스트 주소를 사용하기 위해, 당신은 그것이 상징하고 있는 숫자 주소로 변환해야 한다.


11. 5. 2. 1 인터넷 호스트 주소

인터넷 호스트 주소는 데이터의 4바이트를 포함하는 숫자이다. 그것들은 두부분으로 나뉘는데, 네트워크 번호와 그 네크워크내의 지역 네트워크 주소 번호가 그것이다. 네트워크 숫자는 처음 하나, 둘 또는 세 개의 바이트를 포함한다. 나머지 바이트는 지역 주소이다.

네트워크 번호들은 네트워크 정보 센터 (NIC, Network Information Center)에 등록되어 있다. 그것들은 세 개의 클래스 A, B, C로 분할된다. 각각 컴퓨터마다의 지역네트워크 주소는 각 네트워크의 관리자에 의해 등록되어 있다.

클래스 A 네트워크는 0에서 127범위의 한 바이트 숫자를 가진다. 클래스 A 네트워크의 수는 매우 작지만, 각각은 굉장히 많은 수의 호스트를 지원한다. 중간크기의 클래스 B 네트워크는 두바이트의 네트워크 번호를 가진다. 첫 번째 바이트의 범위는 128에서 191까지이다. 클래스 C 네트워크는 가장 작다. 그것들은 세 바이트의 네트워크 번호를 가지며, 첫 번째 바이트의 범위는 192에서 255까지이다. 그러므로, 인터넷 주소의 처음 1, 2, 3바이트는 네트워크를 명시하는 것이다. 인터넷 주소의 나머지 바이트는 네트워크 안에서의 주소를 명시한다. 클래스 A네트워크 0은 모든 네트워크에 브로드캐스트용으로 예약되어 있다. 추가적으로 각 네트워크 내에서의 호스트 번호가 0인 것은 그 네트워크 내의 모든 호스트들에게 방송(broadcast)하기 위해 예약되어 있다. 클래스 A 네트워크 127은 루프백(loopback)용으로 예약되어 있다. 당신은 그러므로 인터넷 주소 '127. 0. 0. 1'을 사용하면 자신의 호스트장치를 참조 할 수 있다.

단일 장치는 여러 개의 네트워크의 멤버가 될 수 있으므로, 그것은 여러 개의 호스트 주소를 가질 수 있다. 그러나, 같은 호스트 주소를 가진 장치가 하나 이상 있어서는 안 된다.

인터넷 주소를 위한 숫자와 이름 표현은 네 가지 형태가 있다.

  • a. b. c. d 이것은 각개별적으로 주소의 모든 네 개의 바이트를 나타낸다.
  • a. b. c 주소의 마지막 부분인 c는 2바이트로 취급된다. 이것은 네트워크 번호 a. b를 가진 클래스 B의 호스트 주소를 나타내는데 유용하다.
  • a. b 주소의 마지막 부분인 b는 3바이트로 취급된다. 이것은 네트워크 주소 a를 가진 클래스A의 호스트 주소를 표시하는데 유용하다.
  • a 만약 하나의 부분만 주어진다면, 이것은 호스트 주소에 직접 일치하게 된다.

주소의 각부분에서, 기수를 표시하기 위해 보통 C관례를 사용한다. 다른 말로 하면, '0x' 또는 '0X'로 시작하는 것은 16진수 기수를 내포한다. '0'으로 시작하는 것은 8진수를 내포하고, 다른 것은 10진수 기수로 가정한다.


11. 5. 2. 2 호스트 주소 자료형

인터넷 호스트 주소는 정수 형들(unsigned long int형)같은 형식으로 표현된다. 다른 말로 하면, 정수들은 struct in_addr의 형태의 구조체 안에 들어가게 된다. 만약 용법이 일치되면 좋을 것이나, 그렇지 못하다면, 구조체로부터 정수를 뽑아 내거나, 정수를 구조체에 넣는 것이 쉽지 않을 것이다. 다음 인터넷 주소에 관한 기본적인 정의는 'netinet/in. h'헤더 파일 안에서 볼 수 있다:

struct in_addr 자료형

이 자료형은 인터넷 주소를 포함하는 정확한 문맥에 사용된다. 그것은 단지 s_addr이라는 하나의 필드를 가진다. 그것은 unsigned long int 처럼 호스트 주소번호를 저장한다.

unsigned long int INADDR__LOOPBACK Macro

당신은 이 장치의 실제주소를 찾는 대신에, 이 상수를 사용할 수 있다. 그것은 보통 'localhost(로컬호스트)'라 불리는 인터넷 주소 '127. 0. 0. 1'이다. 이 특별한 상수는 당신의 장치의 주소를 살피는데 생기는 문제를 해결해 준다. 또한, 시스템은 한 장치에서 자기자신으로 전송하는 경우 어떤 네트워크전송을 피하기 위해, 주로 INADDR_LOOPBACK을 특별히 구현하고 있다.

unsigned long int INADDR__ANY Macro

주소를 바인딩할 때, "어떠한 오는 주소(any incoming address)"에 대한 것을 이 상수를 사용할 수 있다. 11. 3. 2절 [주소 설정] 를 보라. 이것은 당신이 인터넷 연결을 받아들이길 원할 때, sockaddr_in구조체의 sin_addr멤버에 주로 들어가는 주소이다.

unsigned long int INADDR__BROADCAST Macro

이 상수는 방송메시지를 보내는데 사용되는 주소이다.

unsigned long int INADDR__NONE Macro

이 상수는 어떤 함수들이 에러를 지시하기 위해 반환된다.

11. 5. 2. 3 호스트 주소 함수들

인터넷 주소를 조작하는 추가적인 함수들은 'arpa/inet. h'안에 선언되어 있다. 그것들은 네트워크 바이트 순서로 인터넷 주소를 표현한다. 즉 그것들은 네트워크 번호와 네트워크 안에서의 지역 주소를 호스트 바이트 순서로 표현한다. 네트워크와 호스트 바이트 순서에 관한 설명은 11. 5. 3절 [바이트 순서] 을 보라.

int inet__aton (const char *name, struct in_addr *addr) 함수

이 함수는 인터넷 호스트 주소 이름을 표준 숫자와 점 표시형식에서 이진데이터 형으로 변환한다. 그리고, 그것을 addr이 지시하고 있는 struct in_addr안에 저장한다.

unsigned long int inet__addr (const char *name) 함수

이 함수는 인터넷주소 이름을 숫자와 점으로 된 것에서 이진 데이터로 변환한다. 만약 입력이 유효하지 않으면, inet_addr은 INADDR_NONE를 반환한다. 이것은 위에서 서술한 inet_aton의 구식의 인터페이스이다. INADDR_NONE은 유효한 주소 ( 255. 255. 255. 255 ) 이기 때문에 그것은 쓸모 없이 되었다. 그리고 inet_aton 은 에러 반환을 지시하는 더 깔끔한 방법을 제공한다.

unsigned long int inet__network (const char *name) 함수

이 함수는 숫자와 점으로 표현되는 주소이름으로 부터 네트워크 번호를 뽑아낸다. 만약 입력을 제대로 하지 않으면, inet_network는 -1을 반환한다.

char * inet__ntoa (struct in_addr addr) 함수

이 함수는 인터넷 호스트 주소 addr을 숫자와 점으로 표현되는 문자열로 변환한다. 반환되는 값은 정적으로 할당된 버퍼의 포인터이다. 계속 호출하면 같은 버퍼에 덮어쓰기를 하므로, 만약 그것을 저장하기 위해서 그 문자열을 복사해두어야 한다.

struct in_addr inet__makeaddr (int net, int local) 함수

이 함수는 지역 네트워크 내의 지역 주소 local과 네트워크 번호 net를 묶어 인터넷 호스트 주소를 만든다.

int inet__lnaof (struct in_addr addr) 함수

이 함수는 인터넷 호스트 주소 addr의 일부인 네트워크 내에서의 지역 주소를 반환한다.

int inet__netof (struct in_addr addr) Function

이 함수는 인터넷 호스트 주소의 일부인 네트워크 번호를 반환한다.

 

11. 5. 2. 4 호스트 이름들

인터넷 주소를 위한 숫자와 점으로 표현하는 것 외에, 당신은 의미 있는 이름을 사용하여 호스트를 참조할 수 있다. 의미 있는 이름을 사용하는 것의 장점은 역시 외우기 쉽다는 것이다. 예를 들어 인터넷 주소 '128. 52. 46. 32'를 가진 장치는 'churchy. gnu. ai. mit. edu'로도 표현된다. 또한 'gnu. ai. mit. edu' 도메인내의 다른 컴퓨터들은 간단하게 'churchy'로 이 장치를 참조할 수 있다.

내부적으로, 시스템은 호스트 이름과 호스트 번호 사이의 매핑의 정보를 유지하는 데이터베이스를 사용한다. 이 데이터베이스는 보통 '/etc/hosts'이거나 네임서버에 의해 제공된다. 이 데이터 베이스에 접근하기 위한 함수와 기호들은 'netdb. h'에 선언되어 있다. 만약 당신이 'netdb. h'를 포함한다면, 그것들은 무조건적으로 정의되어 있는 BSD들의 특징이다.

struct hostent 자료형

이 자료형은 호스트 데이터베이스 안의 내용을 표현하는 데 사용된다. 그것은 다음 멤버를 가진다.

char *h_name

이것은 호스트의 "사무적인" 이름이다.

char **h_aliases

스트링의 null로 끝나는 벡터로 표현된 호스트의 또다른 이름이다.

int h_addrtype

이것은 호스트 주소의 형태이다. 실제적으로, 이것의 값은 항상 AF_INET이다. 원래 다른 종료의 주소도 인터넷 주소처럼 데이터베이스 안에 표현될 수 있다. 만약 그렇게 한다면, 당신은 이 필드에 AF_INET이 아닌 어떤 값을 찾아야 한다. 11. 3[소켓 주소들] 을 보라.

int h_length

이것은 각 주소의 바이트 길이다.

char **h_addr_list

이것은 호스트의 주소들의 벡터이다. (호스트들은 다중 네트워크에 연결되고 각각 여러 개의 다른 주소를 가진다는 것을 회상하라) 벡터는 널 포인터로 종료된다.

char *h_addr

이것은 h_addr_list[0]과 동의어이다. 다른 말로, 이것은 첫 번째 호스트 주소이다.
호스트 데이터베이스가 고려되는 것과는 달리, 각 주소는 h_length 바이트 길이의 메모리 블록이다. 그러나 다른 문맥에서, 그것들은, 당신이 이것을 in_addr 또는 unsigned long int로 변환할 수 있는 암시적인 가정이다. struct hostent 구조체의 호스트 주소들은 항상 네트워크 바이트 순서로 주어진다. 11. 5. 5절 [바이트 순서] 을 보라. 당신은 특정한 호스트에 관한 정보를 찾기 위해 호스트 데이터베이스를 검색하려면 gethostbyname 또는 gethostbyaddr를 사용할 수 있다. 그 정보는 정적으로 할당된 구조체에 반환된다. 만약 당신이 그것을 호출로부터 저장하기 위해서는 정보를 복사해야 한다.

struct hostent * gethostbyname (const char *name) 함수

gethostbyname함수는 name으로 이름지어진 호스트에 관한 정보를 반환한다. 만약 검색에 실패하였으면, 널 포인터를 반환한다.

struct hostent * gethostbyaddr (const char *addr, int length, int format) 함수

gethostbyaddr함수는 인터넷 주소 addr의 호스트에 관한 정보를 반환한다. 인수의 length는 addr 주소의 (바이트 단위의)크기이다. format는 주소형식을 명시한다. 즉 인터넷 주소라면 AF_INET값으로 명시하면 된다. 만약 검색에 실패를 하면, gethostbyaddr는 널 포인터를 반환한다. 만약 gethostbyname 또는 gethostbyaddr을 이용한 이름검색이 실패하면, 당신은 변수h_errno의 값을 살펴보고 그 원인을 찾을 수 있다. (그것은 그러한 함수가 errno를 세팅하도록 더 깔끔하게 설계되어 있다. 그러나 h_errno의 사용은 다른 시스템에 대해 호환성 있게 되어 있다. )
h_erro사용하기 전에, 당신은 다음과 같이 그것을 선언해 주어야 한다.

extern int h_errno; 아래는 h_errno안에서 찾을 수 있는 error코드들이다:

HOST_NOT_FOUND

어떤 호스트가 data base안에 없다.

TRY_AGAIN

이 상태는 네임서버에 연결되지 않을 때 발생한다. 만약 당신이 나중에 다시 시도한다면, 성공할 수도 있다.

NO_RECOVERY

복구할 수 없는 에러 발생.

NO_ADDRESS

호스트 데이터베이스는 이름에 대한 항목을 포함하나, 그것은 적합한 인터넷 주소를 가지지 않았다.
당신은 또한 sethostent, gethostent, endhostnet를 사용하여 한 번에 한 항목씩 전체 호스트 데이터베이스를 죽 살펴본다. 이러한 함수들을 사용할 때는 주의해야 한다. 왜냐하면 그것들은 재진입성을 가지지 않기 때문이다. (즉, 스케줄을 점유한다)

void sethostent (int stayopen) 함수

이 함수는 데이터베이스를 살펴보는 것을 시작하기 위해서 호스트 데이터베이스를 연다. 당신은 항목들을 읽기 위해 그러고 나서 gethostent를 호출해야 한다. 만약 stayopen인수가 0이 아니면, 다음으로 gethostbyname 또는 gethostbyaddr를 호출하는 것이 데이터베이스를 닫지 않도록 (그것들이 그래야 하는 것처럼) 이것은 플래그를 세트한다.

struct hostent * gethostent () 함수

이 함수는 호스트 데이터베이스 안의 다음 항목을 반환한다. 만약 더 이상 항목이 없으면, 널 포인터를 리턴한다.

void endhostent () 함수

함수는 호스트 데이터 베이스를 닫는다.

11. 5. 3 인터넷 포트

인터넷 이름공간에서의 소켓주소는, 장치의 인터넷 주소에 추가적으로 주어진 장치의 소켓을 구별하는 포트번호로 구성된다. 포트번호는 0에서 65535번까지의 범위이다. IPPORT_RESERVED보다 작은 포트번호는 표준서버에서 finger나 telnet과 같은 것을 위해 예약되어 있다. 시스템 내부에 이러한 것들을 유지하고 있는 데이터베이스가 있고, 당신은 getservbyname함수를 사용하여 서비스 이름과 포트번호의 맵을 만들 수 있다. ; 11. 5. 4절 [서비스 데이터베이스] 을 보라.

만약 데이터베이스 안에 정의된 표준 중의 하나가 아닌 서버를 작성하려면, 그것을 위한 포트번호를 선택해야 한다. IPPORT_USERRESERVED 보다 더 큰 숫자를 사용하라. 어떤 번호들은 서버들을 위해 예약되어 있고 시스템에 의해 자동으로 생성되지 않을 것이다. 다른 사용자들에 의한 서버사용과의 충돌을 피하는 것은 당신에게 달렸다.

당신이 그것의 주소를 명시하지 않고 소켓을 사용할 때, 시스템은 포트번호를 하나 생성한다. 이 번호는 IPPORT_RESERVED와 IPPORT_ USERRESERVED사이의 값이다.

인터넷에서, 같은 소켓주소(호스트 주소 + 포트주소)로 통신을 시도하지 않는다면, 같은 포트번호로 두 개의 다른 소켓을 가지는 것은 실제로 적당한 방법이다. 상위프로토콜이 그것을 요구할 때, 특별한 상황을 기대하고 같은 포트번호를 복사하지 말아라. 보통, 시스템은 그러한 일을 수행하지 않을 것이다. bind는 보통 다른 포트번호를 요구한다. 포트번호를 다시 사용하기 위해서는, 소켓옵션 SO_REUSEADDR를 세트하라. 11. 11. 2절 [소켓-수준 옵션]을 보라.

다음 매크로들은 'netinet/in. h'에 정의되어 있다.

 int IPPORT__RESERVED 매크로

IPPORT_RESERVED 보다 작은 값의 포트는 슈퍼유저용으로 예약되어 있다.

int IPPORT__USERRESERVED 매크로

명백히 사용하기 위해 예약된, IPPORT_USERRESERVED보다 크거나 같은 포트이다. 그것들은 보통 자동으로 할당된다.

 

11. 5. 4 서비스 데이터베이스

유명한 서비스의 경로를 유지하는 데이터베이스는 보통 '/etc/services'파일이거나 네임서버의 동등한 파일이다. 이 서비스 데이터베이스를 억세스하기 위해 'netdb. h'에 선언되어 있는 다음 유틸리티들을 사용할 수 있다.

struct servent 자료형

이 자료형은 서비스 데이터베이스로부터의 항목에 관한 정보를 나타낸다.

char *s_name

이것은 서비스의 "공식적"인 이름이다.

char **s_aliases

문자열의 배열로 표현된 서비스의 또다른 이름들이다. 이 배열은 마지막에 널 포인터가 들어간다.

int s_port

이것은 서비스의 포트 번호이다. 포트번호는 네트워크 바이트 순서로 되어 있다. ; 11. 5. 5절 [바이트 순서]를 보라.

char *s_proto

이것은 이 서비스와 사용되는 프로토콜의 이름이다. 11. 5. 6절 [프로토콜 데이터베이스]을 보라.

특정한 서비스에 관한 정보를 얻으려면, getservbyname이나 getservby port 함수를 사용하라. 이 정보는 정적으로 할당된 구조체 안으로 반환된다. 즉 그것을 여러 번 호출하는 것으로부터 보호하려면 그 정보를 복사해 놓아야만 한다.

struct servent * getservbyname (const char *name, const char *proto) 함수

getservbyname함수는 name이라고 proto라는 프로토콜을 사용하고 이름지어진 서비스에 관한 정보를 반환한다. 만약 그러한 서비스를 찾지 못하면, 널 포인터를 반환한다.
이 함수는 클라이언트들뿐만 아니라 서버들에서도 유용하다. 서버에서는 듣고자 할 때 어떤 포트를 사용할 것인가를 결정할 때, 이 함수를 사용한다. (듣는 것에 관하여서는 11. 8. 2절[듣기] 을 보라)

struct servent * getservbyport (int port, const char *proto) 함수

getservbyport함수는 proto프로토콜을 사용하고 port라는 포트를 사용하는 서비스에 관한 정보를 반환한다. 당신은 또한 setservent, getservent, endservent를 사용하여 서비스 데이터베이스를 조사할 수 있다. 이러한 함수들은 재진 입성을 가지지 않기 때문에, 사용할 때 주의해야 한다.

void setservent (int stayopen) 함수

이 함수는 당신이 조사하려는 서비스 데이터베이스를 연다.
만약 stayopen 인수가 0이 아니면, getservbyname또는 getservbyport를 다음에 호출을 하였을 때 데이터베이스를 닫지 않도록(보통은 닫는다), 플래그를 세트한다. 여러 번의 호출이 데이터베이스를 여러 번 열지 않도록 할 때, 이것은 더욱 효율적이다.

struct servent * getservent (void) 함수

이 함수는 서비스 데이터베이스 안의 다음 항목을 반환한다. 만약 더 이상의 항목이 없으면, 널 포인터를 반환한다.

void vndservent (void) 함수

이 함수는 서비스 데이터베이스를 닫는다.

11. 5. 5 바이트 순서 변환

컴퓨터의 종류가 틀리면, 한 워드 내에서 바이트의 순서가 틀리게 사용 될 수 있다. 어떤 컴퓨터들은 워드안에 상위 바이트가 먼저 오기도 하고 (이를 "큰 종결자(big-endian)"라 부른다), 다른 것들은 상위 바이트가 나중에 오기도 한다. (이를 "작은 종결자(little-endian)"라 한다)

다른 바이트 수를 사용하는 장치들이 통신하기 위해, 인터넷 프로토콜들은 네트워크 상에서 전송되는 데이터를 위해 규정된 바이트 수 명시하고 있다.

인터넷 소켓 연결을 설정할 때, 당신은 sockaddr_in구조체의 sin_port와 sin_addr멤버를 네트워크 바이트 수로 표현해야만 한다. 소켓을 통해 전송되는 정수 데이터를 코딩할 때 역시 그것을 네트워크 바이트 순서로 변환해야 한다. 만약 그렇게 하지 않는다면, 프로그램은 다른 종류의 장치와 통신을 하는데 실패할 것이다.

포트번호나 호스트 주소를 얻기 위해 getservbyname, gethostbyname, inet_addr를 사용한다면, 사용되는 값들은 이미 네트워크 바이트 순서로 되어 있는 것이다. 당신은 그것들을 sockaddr_in에 직접 복사할 수 있다.

그렇지 않은 경우, 값들을 정확히 변환하지 않으면 안 된다. sin_port멤버로 사용하기 위해 htons와 ntohs를 사용하여 값들을 변환하라. sin_addr멤버로 사용하기 위해 htonl과 ntohl를 사용하여 값들을 변환하라. (struct in_addr은 unsigned long int와 같은 것임을 기억하라) 이러한 함수들은 'netinet/in. h'안에 선언되어 있다.

unsigned short inthtons (unsigned short int hostshort) 함수

이 함수는 호스트 바이트 수 된 short형 정수 hostshort를 네트워크 바이트 순서로 변환한다.

unsigned short int ntohs (unsigned short int netshort) 함수

이 함수는 네트워크 바이트 수 된 short형 정수 netshort를 호스트바이트순서로 변환한다.

unsigned long int htonl (unsigned long int hostlong ) 함수

이 함수는 호스트 바이트순서로된 long형 정수 hostlong을 네트워크 바이트 수 변환한다.

unsigned long int ntohl (unsigned long int netlong ) 함수

이 함수는 네트워크 바이트 수 된 long형 정수 netlong을 호스트 바이트 수 변환한다.

 

11. 5. 6 프로토콜 데이터베이스

소켓에서 사용되는 통신 프로토콜은 어떻게 데이터를 교환할 것인가에 대한 저수준 세부사항을 제어한다. 예를 들면, 프로토콜은, 전송상의 에러검출을 위한 검사합계(checksum)같은 것이나, 메시지의 경로설정 명령 같은 것들을 구현한다. 일반 사용자 프로그램들은 이러한 세부사항들에 대해서 직접적인 영향을 받지는 않는다.

인터넷 이름공간의 기본 통신 프로토콜은 통신스타일에 의존한다. 스트림 통신의 기본 프로토콜은 TCP("전송 제어 프로토콜")이다. 데이터그램 통신의 기본 프로토콜은 UDP("유저 데이터그램 프로토콜")이다. 신뢰성 있는 데이터그램 통신의 기본 프로토콜은 RDP("신뢰성 있는 데이터그램 프로토콜")이다. 당신은 아마 거의 대부분 기본 프로토콜을 사용할 것이다.

인터넷 프로토콜은 일반적으로 숫자 대신 이름으로 명시된다. 호스트가 알고 있는 네트워크 프로토콜은 데이터베이스 안에 저장되어 있다. 이것은 보통 '/etc/protocols'파일 이에서 얻어지거나 , 네임서버에 의해 동등하게 제공될 수 있다. 당신은 getprotobyname함수를 사용하여 데이터베이스 내에서 프로토콜 이름에 연계된 프로토콜 번호를 찾아 낼 수 있다.

여기에 프로토콜 데이터베이스를 억세스하기 위한 유틸리티들의 자세한 정의가 있다. 이것들은 'netdb. h'에 선언되어 있다.

struct protoent 자료형

이 자료형은 네트워크 프로토콜 데이터베이스의 항목들을 표현하는데 사용된다. 그것은 다음 멤버를 가진다.

char *p_name

이것은 프로토콜의 공식적인 이름이다.

char **p_aliases

이것들은 프로토콜의 또다른 이름들이다. 문자열의 배열로 되어 있으며, 배열의 마지막 요소는 널 포인터이다.

int p_proto

이것은 (호스트 바이트 수 된) 프로토콜 번호이다. 소켓의 protocol인수로 이 멤버를 사용하라

구체적인 프로토콜에 관하여 프로토콜 데이터베이스를 검색하기 위해 getprotobyname과 getprotobynumber를 사용할 수 있다. 정적으로 할당된 구조체에 정보가 반환된다. 여러 번 호출되는 것으로부터 정보를 보호하기 위해 그것을 복사해야 놓아야 한다.

struct protoent * getprotobyname (const char *name) 함수

getprotobyname함수는 name이라는 이름의 네트워크프로토콜에 관한 정보를 반환한다. 만약 같은 이름의 프로토콜이 없으면, 널 포인터를 반환한다.

struct protoent * getprotobynumber (int protocol) 함수

getprotobynumber함수는 protocol이라는 숫자를 가진 네트워크 프로토콜에 관한 정보를 반환한다. 만약 이러한 프로토콜이 없다면 널 포인터를 반환한다. setprotoent, getprotoent, endprotoent를 사용하여 전체 프로토콜 데이터베이스를 한 번에 한 프로토콜씩 조사할 수 있다. 이들 함수는 재진 입성을 가지지 않기 때문에 주의해서 사용해야 한다.

void setprotoent (int stayopen) 함수

이 함수는 검색을 위해 프로토콜 데이터베이스를 연다. 만약 stayopen인수가 0이 아니면, getprotobyname 또는 getproto-bynumber의 호출이 데이터베이스를 (보통 닫는데) 닫지 않도록 플래그를 세트한다. 이것은 여러 번의 호출로 데이터베이스가 여러 번 열리지 않도록 하는데 더욱 유용하다

struct protoent * getprotoent (void) 함수

이 함수는 프로토콜 데이터베이스의 다음 정보를 반환한다. 만약 더 이상의 항목이 없으면 널 포인터를 반환한다.

void endprotoent (void) Function

이 함수는 프로토콜 데이터베이스를 닫는다.

11. 5. 7 인터넷 소켓 예제

여기에 인터넷 이름공간 안에 소켓을 만들고 이름짓는 방법을 보여주는 예제가 있다. 프로그램이 수행되는 장치에 새로 소켓이 만들어진다.

장치의 인터넷 주소를 찾고 사용하는 것 대신, 호스트 주소에는 INADDR_ANY를 명시하였다. 시스템은 장치의 실제주소로 그것을 대치한다.

#include <stdio. h>
#include <stdlib. h>
#include <sys/socket. h>
#include <netinet/in. h>
int
make_socket (unsigned short int port)
{
int sock;
struct sockaddr_in name;
/* 소켓을 만든다 */
sock = socket (PF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror ("socket");
exit (EXIT_FAILURE);
}
/* 소켓에 이름을 준다 */
name. sin_family= AF_INET;
name. sin_port = htons (port);
name. sin_addr. s_addr = htonl (INADDR_ANY);
if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
{
perror ("bind");
exit (EXIT_FAILURE);
}
return sock;
}
여기에 또다른 예제가 있다. 이것은 주어진 호스트 이름문자열과 포트번호를 sockaddr_in구조체 안에 어떻게 넣는 지를 보여준다:
#include <stdio. h>
#include <stdlib. h>
#include <sys/socket. h>
#include <netinet/in. h>
#include <netdb. h>
void
init_sockaddr (struct sockaddr_in *name, const char *hostname,
unsigned short int port)
{
struct hostent *hostinfo;
name->sin_family = AF_INET;
name->sin_port = htons (port);
hostinfo = gethostbyname (hostname);
if (hostinfo == NULL) {
fprintf (stderr, "Unknown host %s. \n", hostname);
exit (EXIT_FAILURE);
}
name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
}


11. 6 다름 이름공간들

다른 이름공간과 프로토콜패밀리들도 지원되지만 별로 사용되지 않으므로 아직 문서화되지 않았다. PF_NS는 Xerox 네트워크 소프트웨어 프로 콜에 관련된 것이다. PF_ISO 는 개방시스템상호연결(OSI)에 관련된 것이다. PF_CCITT는 CCITT의 프로토콜들에 관련된 것이다. 'socket. h'에 실제로 구현되지 않은 기호와 다른 이름의 프로토콜이 정의되어 있다.

PF_IMPLINK는 호스트와 인터넷 메시지 처리기와의 통신에 사용된다. 이에 관한 정보와 가끔 사용되는 지역 경로설정 프로토콜인 PF_ROUTE에 관하여는 (미래에 나올) GNU Hurd Manual을 보라.


11. 7 소켓 열고 닫기

이 절에서는 소켓을 열고 다는 실제적인 라이브러리 함수에 관하여 서술한다. 모든 이름공간과 연결스타일에 대해 같은 함수가 사용된다.

11. 7. 1 소켓 만들기

소켓을 만들기 위해서는 'sys/socket. h'에 선언되어 있는 소켓함수를 사용한다.

int socket (int namespace, int style, int protocol) 함수

이 함수는 소켓을 만들고 통신스타일 style을 명시한다. 통신스타일은 11. 2절[통신스타일4] 에 열거된 소켓 스타일중 하나를 사용하여야 한다. namespace인수는 이름공간을 명시하고, PF_FILE이거나 PF_INET이어야 한다( 11. 4절[파일 이름공간], 11. 5절[인터넷 이름공간]을 보라). protocol은 프로토콜을 정한다 (11. 1절 [소켓개념]을 보라); protocol에는 보통 0이 사용된다.
socket으로부터의 반환 값은 새로운 소켓의 파일서술자이다. 에러가 발생하면 -1이 반환된다.
다음은 이 함수가 정의해 놓은 에러 상황 errno이다:

EPROTONOSUPPORT

The protocol or style is not supported by the namespace specified.

EMFILE : 프로세스가 이미 너무 많은 파일서술자들을 열어 놓았다.

ENFILE : 시스템이 이미 너무 많은 파일서술자들을 열어 놓았다.

EACCESS : 프로세스가 명시된 style이나 protocol에 맞는 소켓을 만들 권한을 갖고 있지 않다.

ENOBUFS : 시스템 내부 버퍼 공간이 부족하다.

socket 함수에 의해 반환되는 파일서술자는 읽기와 쓰기 기능 모두를 지원한다. 그러나 파이프 같은 소켓들은 파일 위치에 관한 동작은 하지 못한다. socket함수를 어떻게 호출하는 지에 대한 예는 11. 4절 [파일 이름공간], 또는 11. 5. 7절 [Inet 예제]을 보라.


11. 7. 2 소켓 닫기

소켓의 사용이 끝나면, 간단히 그 파일서술자를 close함수를 이용해 닫으면 된다. 8. 1절 [파일 열고 닫기]을 보라. 만약 연결을 통하여 전송되기를 기다리는 데이터가 여전히 있으면, 보통 close는 이 전송이 완료되도록 시도한다. SO_LINGER소켓 옵션을 사용하여 시간제한 주기를 명시해 주면, 이 과정을 제어할 수 있다. 11. 11절 [소켓 옵션] 을 보라.

당신은 'sys/socket. h'에 서술되어 있는 shutdown을 호출하므로 써 연결하의 송신만 또는 수신만을 종료시킬 수 있다.

int shutdown (int socket, int how ) 함수

shutdown함수는 socket이라는 소켓의 연결을 종료시킨다. how인수는 어떤 기능을 수행할 것인가를 명시한다.
0. 이 소켓으로부터 데이터 수신을 중단한다. 만약 더 이상의 데이터가 도착하면, 그것을 무시한다.
1. 이 소켓으로의 전송을 중단한다. 보내기 위해 대기중인 데이터는 취소된다. 이미 보낸 데이터의 응답을 기다리는 것도 중단한다. 만약 그것을 잃어버릴지라도 재전송하지 않는다.
2. 수신과 송신 모두를 중단한다.
성공하면 반환 값이 0이고, 실패면 -1이다. 다음은 이 함수에 정의되어 있는 errno 에러 상 황이다.
EBADF : socket이 유효한 파일 서술자가 아니다.
ENOTSOCK : socket이 소켓이 아니다.
ENOTCONN : socket이 연결되어 있지 않다

11. 7. 3 소켓 쌍

소켓 쌍은 연결된 (그러나 이름없는) 소켓의 쌍으로 구성된다. 그것은 pipe와 유사하고 무척 같은 방법으로 사용된다. 소켓 쌍은 'sys/socket. h'에 선언된 socketpair함수로 만들어진다. 소켓 쌍은 파이프와 대단히 유사하다:

주된 차이점은 소켓 쌍은 양방향이라는 것이다. 파이프는 하나의 입력만 되는 쪽과 출력만 되는 한 쪽만을 가지고 있다. (10장[Pipe와 FIFO]를 보라 )

int socketpair (int namespace, int style, int protocol, int filedes[2]) 함수

이 함수는 소켓 쌍을 만들며, filedes[0]과 filedes[1]안에 파일서술자를 반환한다. 소켓 쌍은 전이중(full-duplex)성 통신 채널이기 때문에 한쪽에서 읽거나 쓰기가 가능하다. namespace, style, protocol인자는 socket함수에서처럼 해석된다. style은 11. 2절[통신스타일]에 나열된 것이다. namespace인자는 이름공간을 명시하며, 반드시 AF_FILE이어야 한다. (11. 4절[파일이름공간] 을 참조하라); protocol은 통신 프로토콜을 서술하고 0도 의미 있는 값이다. 만약 style 이 비연결 통신스타일을 명시하면, 당신이 얻게 되는 두 소켓은 연결되지 않는다. 엄밀히 말해, 그들 각각은 서로의 목적지 주소를 알고 있어, 서로에게 패킷을 보낼 수 있게 된다.
socketpair함수는 성공시에 0을, 실패시에 -1을 반환한다. 다음은 이 함수에 정의되어 있는 errno에러 상황이다:

EMFILE : 프로세스가 너무 많은 파일 서술자를 열어 놓고 있다.

EAFNOSUPPORT : 명시된 이름공간이 지원되지 않는 것이다.

EPROTONOSUPPORT : 명시된 프로토콜이 지원되지 않는 것이다.

EOPNOTSUPP 명시된 : 프로토콜이 소켓 쌍을 만드는 것을 지원하지 않는다.


11. 8 연결하는데 소켓사용

대부분의 통신 스타일은 특정한 다른 소켓과 연결을 만들고 소켓과 소켓을 통하여 데이터를 교환한다. 연결을 만드는 것은 비대칭이다. ; 한쪽(클라이언트)은 다른 쪽(서버)이 소켓을 만들고 연결요구를 기다릴 때까지 연결을 요구하도록 되어 있다.

11. 8. 1절 [연결하기] 은 클라이언트 프로그램이 서버와 연결을 초기화하기 위해 무엇을 해야 하는지를 서술한다.

11. 8. 2절 [듣기]11. 8. 3[연결을 수락하기] 에서는 서버 프로그램이 클라이언트로부터 연결요구를 기다리고 받아들이는 것에 대한 서술을 한다.

11. 8. 5[데이터전송]은 어떻게 연결된 소켓을 통하여 데이터를 전송하는 방법을 서술한다.


11. 8. 1 연결 만들기

연결을 만들 때, 클라이언트는 서버가 연결을 기다리고 수락하는 동안 연결을 만들어야 한다. 여기에 클라이언트가 'sys/socket. h'에 선언된 connect함수를 이용하여 무엇을 해야 할 것인지를 논의하고 있다.

int connect (int socket, struct sockaddr *addr, size_t length) 함수

connect함수는 addr와 length의 길이로 명시된 주소를 가지는 소켓의 socket이라는 파일서술자를 이용하여 소켓으로부터 연결을 초기화 한다. (이 소켓은 전형적으로 다른 장치의 것이다. 그것은 서버처럼 이미 설정되어야 한다. ) 이 인수들이 어떻게 해석되는지에 관한 정보는 11. 3절[소켓주소]을 보라
보통, connect는 서버가 요구에 응답할 때까지 기다린 후 반환하게 된다. 응답을 기다리지 않고 즉시 반환하도록 소켓 socket을 블록킹되지 않도록(non-blocking) 설정할 수 있다. 블록킹되지 않는 모드에 관한 정보는 8. 10절 [파일 상태 플랙]을 보라.
connect로부터의 반환치는 보통 0이다. 만약 에러가 발생 시에는 -1을 반환한다.  
이 함수에 선언되어 있는 errno에러 상황은 다음과 같다.

EBADF : 소켓socket이 유효한 파일 서술자가 아니다.

ENOTSOCK : 소켓socket이 소켓이 아니다.

EADDRNOTAVAIL : 명시된 주소가 원격장치(상대방기기)에 사용할 수 없는 것이다.

EAFNOSUPPORT : addr의 이름공간이 이 소켓에서 지원하지 않는 것이다.

EISCONN : 소켓socket이 이미 연결되어 있다.

ETIMEDOUT : 연결설정을 시도하다가 시간을 초과하였다.

ECONNREFUSED : 서버가 연결설정을 직접 거절하였다.

ENETUNREACH : 주어진 addr의 네트워크는 이 호스트로부터 도달할 수 없다.

EADDRINUSE : 주어진 addr의 소켓주소가 이미 사용 중이다.

EINPROGRESS : 소켓socket이 블록 킹되지 않는 것이고, 연결을 즉시 설정할 수 없었다.

EALREADY : 소켓socket이 블록 킹되지 않는 것이고, 이미 진행중인 미결정의 연결이 있다.


11. 8. 2 연결 듣기

이제 서버프로세스가 소켓에서 연결을 수락하기 위해 무엇을 해야 하는지 고려해보자. 이것은 listen함수를 사용하여 소켓에서 연결요구를 가능하게 하고 나서, accept함수 (11. 8. 3절 [연결수락]을 보라)를 사용하여 요구를 받아들이는 것을 포함한다. listen함수는 비연결 통신 스타일을 지원하지 않는다. 연결 요구가 있을 때까지 수행을 시작하지 않도록 네트워크 서버를 작성할 수 있다. 11. 10. 1절[Inetd 서버들]을 보라.

인터넷 이름공간에서, 한 포트로의 연결에 접근을 제어하는 보호 메커니즘이 없다. 어떤 장치에서 어떤 프로세스가 당신의 서버에 연결을 만들 수 있다. 만약 당신이 당신의 서버에 접근을 제어하기를 원한다면, 연결요구에 연계된 주소를 검사하거나, 다른 협상과정이나 식별기능이 있는 프로토콜을 구현하면 된다.

파일 이름공간에서, 보통 파일 보호 비트는 소켓 연결에 접근하는 사람을 제어할 수 있다.

int listen (int socket, unsigned int n) 함수

listen함수는 소켓socket을 연결을 수락이 가능하도록 하여, 그것을 서버소켓으로 만든다.
인수 n은 미결된 연결의 큐의 길이를 명시한다.
listen함수는 성공시에 0을 실패시 -1을 반환하며, 이 함수에 정의된 errno에러 상황은 다음과 같다.

EBADF : 인수socket는 유효한 파일서술자가 아니다.

ENOTSOCK : 인수socket이 소켓이 아니다.

EOPNOTSUPP : 소켓socket는 이 기능을 지원하지 않는다.


11. 8. 3 연결 수락

서버가 연결요구를 수신하였을 때, 연결요구를 수락함으로써 연결이 끝날 수 있다. accept함수를 사용하여 그것을 수행한다. 서버로 설정된 소켓은 여러 클라이언트로부터의 연결요구를 수락할 수 있다. 서버의 원 소켓(original socket)은 연결의 부분이 되지 않는다. 대신에, accept는 연결에 특정한 새로운 소켓을 만든다. accpect는 이 소켓에 대한 서술자를 반환한다. 서버의 원 소켓은 다음 연결요구를 듣기 위하여 계속 남아있다.

서버소켓의 비결된 연결요구의 수는 제한되어 있다. 만약 클라이언트들로 부터의 연결 요구가 서버가 그것을 처리할 수 있는 거 보다 더 빠르다면, 큐가 가득 채워 질 것이고, 추가적인 요구는 ECONNREFUSED에러로 거절된다. 비록 시스템 내부의 큐가 제한되어 있을 지라도 listen함수의 인수로 이 큐의 최대 길이를 명시할 수 있다.

int accept (int socket, struct sockaddr *addr, size_t *length_ptr) 함수

이 함수는 서버소켓 socket의 연결요구를 수락하는데 사용된다. 만약 미결된 연결이 없고 소켓socket이 블록킹되도록 세트되어있으면 accept함수는 계속 기다린다. (블록킹 안되는 소켓으로 기다리게 하도록 선택할 수도 있다. ) 블록킹 안되는 모드에 관한 정보는 8. 10절 [파일 상태 플랙] 을 보라. addr과 length_ptr인수는 초기화된 연결의 클라이언트 소켓의 이름에 관한 정보를 반환하는데 사용된다. 11. 3절 [소켓주소]에 정보포맷에 관한 정보가 있다. 연결을 수락하는 것은 socket부분을 연결의 일부로 만들지 않는다. 대신에, 연결될 새로운 소켓을 생성한다.
accept의 정상적인 반환치는 새로운 소켓의 파일서술자이다. accept후에, 원래의 소켓인 socket은 열려 있고, 연결되지 않은 상태로 남아있게 된다. 그리고 그것이 닫힐 때까지 듣기를 계속할 것이다. accept를 호출하여 이 소켓을 통하여 추가적인 연결을 수락할 수 있다.
만약 에러가 발생하면 accept는 -1을 반환한다.
다음은 이 함수에 정의되어 있는 errno에러조건이다:

EBADF : socket인수가 유효한 파일서술자가 아니다.

ENOTSOCK : 서술자 socket인수가 소켓이 아니다.

EOPNOTSUPP : 서술자 socket이 이 기능을 지원하지 않는다.

EWOULDBLOCK : socket이 블록킹안되는 모드로 세트되어 있고, 즉시 연결될 수 있는 미결의 연결이 없다.

accept함수는 비연결성 통신 스타일의 소켓에는 사용될 수 없다.


11. 8. 4 누가 나에게 연결되어 있나

int getpeername (int socket, struct sockaddr *addr, size_t *length_ptr) 함수

getpeername함수는 socket이 연결되어 있는 소켓의 주소를 반환한다; addr과 lengh_ptr에 의해 명시된 메모리 공간에 그 주소가 저장된다. *length_ptr안에 주소의 길이를 저장한다.
주소의 포맷에 관하여는 11. 3절 [소켓 주소]을 보라. 어떤 운영체제에서, getpeername은 단지 인터넷 도메인의 소켓에서만 동작한다.
성공시에 0을 실패시에 -1을 반환한다. 다음은 이 함수에 정의된 errno에러조건이다:

EBADF : socket인수가 유효한 파일 서술자가 아니다.

ENOTSOCK : socket서술자가 소켓이 아니다.

ENOTCONN : socket소켓이 연결되어 있지 않다.

ENOBUFS : 충분한 내부 버퍼가 없다.


11. 8. 5 데이터 전송

일단 소켓이 상대측과 연결이 되면, 데이터를 전송하는 일반적인 write와 read 동작을 수행할 수 있다. (8. 2절 [I/O 추상]을 보라) 소켓은 양방향 채널이므로 한쪽에서 읽고 쓰기가 가능하다. 소켓의 동작을 명시하는 몇 가지 I/O모드가 있다. 그러한 모드를 명시하려면, 더 일반적인 read와 write대신에 recv와 send 함수를 사용해야 한다. recv와 send함수는 특별한 I/O모드를 제어하기 위한 여러 가지 플랙을 명시하는데 사용되는 추가적인 인수를 필요로 한다. 예를 들어 out-of-band 데이터를 읽거나 쓰기 위하여 MSG_OOB플랙을 명시할 수 있고, 입력을 엿보기 위하여 MSG_PEEK를 세트하거나, 출력시 라우팅정보의 포함을 제어하기 위하여 MSG_DONTROUTE플랙을 명시할 수 있다.


11. 8. 5. 1 데이터 보내기

send함수는 'sys/socket. h'에 선언되어 있다. 만약 flag인수를 0으로 하는 경우면, send대신에 write를 쓸 수 있다. 8. 2절 [I/O 추상]을 보라. 만약 소켓이 연결되어 있는 상태라면, send나 write를 사용할 때 SIGPIPE라는 시그널을 얻을 것이다. (21. 2. 6절 [여러 가지 시그널]을 보라)

int send (int socket, void *buffer, size_t size, int flags) 함수

send함수는 write와 비슷하지만, 추가적인 flags라는 플랙을 가지고 있다. flags에 가능한 값들은 11. 8. 5. 3절[소켓 데이터 옵션]에 서술되어 있다. 이 함수는 전송된 바이트의 수를 반환하며, 실패시 -1을 반환한다. 만약 소켓이 블록 킹되지 않는 모드라면, send가 (write처럼) 데이터의 일부만 전송한 뒤 반환될 것이다. 블록 킹되지 않는 모드에 관하여는 8. 10절 [파일 상태 플랙] 을 보라. 어쨌거나, 성공시의 반환치는 에러 없이 전송된 메시지를 가리킬 뿐, 에러 없이 수신되는 것처럼 필수적인 것이 아님에 주의하라.
다음은 이 함수에 정의된 errno에러 조건이다.

EBADF : socket인수가 유효한 파일 서술자가 아니다.

EINTR

데이터를 보내기 전에, 시그널에 의해 인터럽트가 걸렸다. 21. 5절 [인터럽트 추상]을 보라.

ENOTSOCK : socket서술자가 소켓이 아니다.

EMSGSIZE : 소켓종류가 메시지를 작게 보낼 것을 요구하는데, 이 메시지가 너무 크다.

EWOULDBLOCK

블록 킹되지 않는 모드가 소켓에 세트되어 있으나, 쓰기 동작이 블록 킹될 것이다. (보통 send는 동작이 끝날 수 있을 때까지 블록 킹된다. )

ENOBUFS : 충분한 내부버퍼공간이 없다.

ENOTCONN : 당신은 이 소켓에 연결되어 있지 않다.

EPIPE

이 소켓은 연결되어 있지만, 연결이 깨져있다. 이 경우 send는 SIGPIPE시그널을 먼저 생성한다. 만약 시그널이 무시되거나 블록 킹되거나, 핸들러가 반환된다면, send는 EPIPE로 실패를 알리게 된다.

11. 8. 5. 2 데이터 수신하기

recv함수는 'sys/socket. h' 헤더 파일에 선언되어 있다. 만약 flags인수가 0이면 recv대신에 read를 써도 된다. 8. 2절 [I/O추상] 을 참조하라.

int recv (int socket, void *buffer, size_t size, int flags) 함수

recv함수는 read와 유사하지만, 추가적으로 flags라는 플랙을 가지고 있다. flags에 가능한 값은 11. 8. 5. 3절 [소켓 데이터 옵션] 에 서술되어 있다.
만약 블록 킹되지 않는 모드로 소켓에 세트되어 있고, 읽을 데이터가 없으면, recv는 기다리지 않고 즉시 실패로 끝난다. 블록 킹하다 않는 모드에 관한 정보는 8. 10절 [파일 상태 플랙]을 보라.
이 함수는 수신된 바이트 수를 반환하거나, 실패시 -1을 반환한다.
다음은 이 함수에 선언된 errno에러 조건이다.

EBADF : socket인수가 유효한 파일서술자가 아니다.

ENOTSOCK : socket서술자가 소켓이 아니다.

EWOULDBLOCK

소켓에 블록 킹하다 않는 모드가 세트되어 있고, 읽기 동작이 블록될 것이다. (보통 recv는 읽기 가능한 입력이 있을 때까지 블럭킹된다. )

EINTR

데이터를 읽기도 전에 시스널에 의해 인터럽트가 걸렸다. 21. 5절[인터럽트 추상]을 보라.

ENOTCONN : 당신은 이 소켓에 연결되어 있지 않다.


11. 8. 5. 3 소켓 데이터 옵션

send와 recv에 사용되는 flags인수는 비트마스크이다. 당신은 이 인수에 값을 얻기 위해 아래 매크로로 비트별-OR연산을 수행할 수 있다. 'sys/socket. h'헤더파일에 모두 정의되어 있다.

int MSG__OOB 매크로

out-of-band데이터를 송신하거나 수신한다. 11. 8. 8절 [Out-of-band 데이터]을 보라.

int MSG__PEEK 매크로

수신된 큐로부터 데이터를 보기만 하고 그것을 지우지는 말아라. 이것은 send가 아닌 recv같은 입력함수에서만 의미가 있다. send.

int MSG__DONTROUTE 매크로

이 메시지에 라우팅정보를 포함하지 마라. 이것은 출력 함수에서만 의미를 갖는다. 그리고 분석이나 라우팅 프로그램에서만 흥미롭게 사용된다. 그것에 대하여는 여기서 더 이상 설명하지 않는다.

11. 8. 6 Byte Stream 소켓 예제

이것은 인터넷 이름공간의 byte stream을 위한 연결을 만드는 프로그램의 예제이다. 한 번 서버에 연결하고 특별한 일을 하지 않는다. 그것은 단지 텍스트 문자열을 서버에게 보내고 종료한다.

#include <stdio. h>
#include <errno. h>
#include <stdlib. h>
#include <unistd. h>
#include <sys/types. h>
#include <sys/socket. h>
#include <netinet/in. h>
#include <netdb. h>
#define PORT 5555
#define MESSAGE "Yow!!! Are we having fun yet?!?"
#define SERVERHOST "churchy. gnu. ai. mit. edu"
void
write_to_server (int filedes)
{
int nbytes;
nbytes =write (filedes, MESSAGE, strlen (MESSAGE) + 1);
if (nbytes < 0) {
perror ("write");
exit (EXIT_FAILURE);
}
}
int
main (void)
{
extern void init_sockaddr (struct sockaddr_in *name,
const char *hostname,
unsigned short int port);
int sock;
struct sockaddr_in servername;
/* 소켓 만들기 */
if ((sock = socket (PF_INET, SOCK_STREAM, 0)) < 0) {
perror ("socket (client)");
exit (EXIT_FAILURE);
}
/* 서버에 연결 */
init_sockaddr (&servername, SERVERHOST, PORT);
if (connect (sock,(struct sockaddr *) &servername,sizeof (servername)) < 0) {
perror ("connect (client)");
exit (EXIT_FAILURE);
}
/* 서버에 데이터 보내기 */
write_to_server (sock);
close (sock);
exit (EXIT_SUCCESS);
}

11. 8. 7 Byte Stream Connection 서버 예제

서버 쪽은 좀더 복잡하다. 여러 클라이언트가 동시에 서버에 연결할 수 있도록 하기를 원하므로, 간단히 하나의 read나 recv를 호출하여 하나의 클라이언트로부터 입력을 기다려서는 안 된다. 대신에, 모든 열린 소켓으로부터 입력을 기다리도록(8. 6절 [I/O 기다리기]), select를 사용하는 것이 올바른 것이다. 이것은 또한 추가적인 연결 요구를 다루도록 허락한다.

이 특별한 서버는 클라이언트로부터 메시지를 한 번 받는 다는 것 외에 특별히 흥미로운 일을 하지 않는다. 그것은 (클라이언트가 연결을 종료하는 결과로써) 파일의 끝(end-of-file)조건을 검출하였을 때, 그 클라이언트에 대한 소켓을 닫는다.

이 프로그램은 소켓주소를 세트하기 위해 make_socket과 init_sockaddr를 사용하였다. ; 11. 5. 7 [Inet 예제]을 보라

#include <stdio. h>
#include <errno. h>
#include <stdlib. h>
#include <unistd. h>
#include <sys/types. h>
#include <sys/socket. h>
#include <netinet/in. h>
#include <netdb. h>
#define PORT 5555
#define MAXMSG 512
int
read_from_client (int filedes)
{
char buffer[MAXMSG];
int nbytes;
nbytes = read (filedes, buffer, MAXMSG);
if (nbytes < 0) { /* 읽기 에러 */
perror ("read");
exit (EXIT_FAILURE);
} else if (nbytes == 0) /* 파일의 끝 */
return -1;
else { /* 데이터 읽기 */
fprintf (stderr, "Server: got message: `%s'\n", buffer);
return 0;
}
}
int
main (void)
{
extern int make_socket (unsigned short int port);
int sock;
int status;
fd_set active_fd_set, read_fd_set;
int i;
struct sockaddr_in clientname;
size_t size;
/* Create the socket and set it up to accept connections. */
sock = make_socket (PORT);
if (listen (sock, 1) < 0) {
perror ("listen");
exit (EXIT_FAILURE);
}
/* Initialize the set of active sockets. */
FD_ZERO (&active_fd_set);
FD_SET (sock, &active_fd_set);
while (1)
{
/* Block until input arrives on one or more active sockets. */
read_fd_set = active_fd_set;
if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0)
{
perror ("select");
exit (EXIT_FAILURE);
}
/* Service all the sockets with input pending. */
for (i = 0; i < FD_SETSIZE; ++i) {
if (FD_ISSET (i, &read_fd_set)) {
if (i == sock) {
/* Connection request on original socket. */
size = sizeof (clientname);
if (accept (sock,(struct sockaddr *) &clientname, &size) < 0) {
perror ("accept");
exit (EXIT_FAILURE);
}
fprintf (stderr,
"Server: connect from host %s, port %hd. \n",
inet_ntoa (clientname. sin_addr),
ntohs (clientname. sin_port));
FD_SET (status, &active_fd_set);
} else {
/* Data arriving on an already-connected socket. */
if (read_from_client (i) < 0) {
close (i);
FD_CLR (i, &active_fd_set);
}
} /* if 의 끝 */
} /* if 의 끝 */
} /* for 의 끝 */
} /* while 의 끝 */
} /* main 의 끝 */

11. 8. 8 Out-of-Band 데이터

연결을 가진 스트림은 일반 데이터보다 높은 우선 순위로 배달되는 데이터인 out-of-Band데이터를 가능하게 한다. 전형적으로 out-of-band데이터를 전송하는 이유는 예외적인 상황의 통보 때문이다. out-of-band데이터를 보내는 방법은 MSG_OOB플랙을 세트한 (11. 8. 5. 1절[데이터 전송]참고) send를 사용하는 것이다.

수신 프로세스가 순서대로 그것을 받지 않으므로, out-of-band 데이터는 더 높은 우선 순위로 받아진다. 즉, 다음 가능한 out-of-band 데이터를 읽으려면, MSG_OOB플랙을 세트하여 recv를 사용한다. (11. 8. 5. 2절 [데이터 수신]을 보라). 보통 읽기 명령은 out-of-band데이터를 읽지 않는다. 그것들은 단지 일반 데이터만 읽는다. 소켓이 out-of-band데이터가 있음을 알게 되면, 소유프로세스나 소켓의 프로세스 그룹에 SIGURG시그널을 보낸다. F_SETOWN명령으로 fcntl함수를 사용하여 소유자를 명시할 수 있다. 8. 12절 [인터럽트 입력]을 보라. 또한 이 시그널의 핸들러를 설정할 수 있는데, out-of-band데이터를 읽는 등의 적절한 동작을 취하기 위해, 21장[시그널 핸들링]을 보라.

다른 것으로, select함수를 사용하여, 미결의 out-of-data를 검사하거나 out-of-band데이터가 있을 때까지 기다릴 수 있다. ; 그것은 소켓 예외 조건을 기다릴 수 있다. select에 관한 많은 정보는 8. 6절 [I/O를 기다리기]을 보라.

(SIGURG를 사용하거나 select를 사용할 것이던지) out-of-band 데이터의 통고는 out-of-band데이터가 진행 중에 있음을 가리킨다. ; 즉 데이터는 아직 실제로 도착하지 않았다. 만약 그것이 완전히 도착하기 전에 out-of-data를 읽으려고 하면, recv 는 EWOULDBLOCK에러로 실패하게 된다.

out-of-band데이터 전송은 자동으로 일반 데이터의 흐름 안에 "마크"를 둔다. 그것은 어디에 out-of-band데이터가 어디에 있을지를 보여준다. out-of-band 데이터가 먼저 보내진 모든 것들을 취소시킨다는 의미를 가질 때 유용하다. 여기에 수신 프로세스에서 마크전에 보내진 보통 데이터인지 아닌지 어떻게 테스트하는지가 나와있다.

success = ioctl (socket, SIOCATMARK, &result);

이 함수는 out-of-band 마크 앞의 일반 데이터를 무시하는 것이다.
int
discard_until_mark (int socket)
{
while (1)
{
/* This is not an arbitrary limit; any size will do. */
char buffer[1024];
int result, success;
/* If we have reached the mark, return. */
success = ioctl (socket, SIOCATMARK, &result);
if (success < 0)
perror ("ioctl");
if (result)
return;
/* Otherwise, read a bunch of ordinary data and discard it This is guaranteed not to read past the mark if it starts before the mark. */
success = read (socket, buffer, sizeof buffer);
if (success < 0)
perror ("read");
}
}

만약 마크앞의 일반 데이터를 무시하지 않으려면, 그것을 어떻게든 읽기 위해, out-of-band데이터를 위한 내부시스템버퍼 공간을 만들어야 한다. 만약 out-of-band데이터를 읽으려 하는데 EWOULDBLOCK에러를 받는다면, 약간의 일반데이터를 (그것을 얻었을 때 사용할 수 있도록 저장을 하며) 읽기를 시도하라. 그리고 그것이 공간을 만드는지를 보아라. 여기 예제가 있다.

struct buffer
{
char *buffer;
int size;
struct buffer *next;
};
/* Read the out-of-band data from SOCKET and return it as a `struct buffer', which records the address of the data and its size.
It may be necessary to read some ordinary data in order to make room for the out-of-band data. If so, the ordinary data is saved as a chain of buffers found in the `next' field of the value. */
struct buffer *
read_oob (int socket)
{
struct buffer *tail = 0;
struct buffer *list = 0;
while (1) {
/* This is an arbitrary limit. Does anyone know how to do this without a limit? */
char *buffer = (char *) xmalloc (1024);
struct buffer *link;
intsuccess;
int result;
/* Try again to read the out-of-band data. */
success = recv (socket, buffer, sizeof buffer, MSG_OOB);
if (success >= 0) {
/* We got it, so return it. */
struct buffer *link
=(struct buffer*) xmalloc(sizeof(struct buffer));
link->buffer = buffer;
link->size = success;
link->next = list;
return link;
}
/* If we fail, see if we are at the mark. */
if (ioctl (socket, SIOCATMARK, &result) < 0)
perror ("ioctl");
if (result) {
/* At the mark; skipping past more ordinary data cannot help. So just wait a while. */
sleep (1);
continue;
}
/* Otherwise, read a bunch of ordinary data and save it. This is guaranteed not to read past the mark if it starts before the mark. */
if (read (socket, buffer, sizeof buffer) < 0) /* Save this data in the buffer list. */
{
struct buffer *link
= (struct buffer *) xmalloc(sizeof (struct buffer));
link->buffer = buffer;
link->size = success;
/* Add the new link to the end of the list. */
if (tail)
tail->next = link;
else
list = link;
tail = link;
} /* if 의 끝 */
} /* while 의 끝 */
}


11. 9 데이터그램 소켓 명령

이번 절에서는 어떻게 연결 없는 통신스타일(SOCK_DGRAM과 SOCK_RDM스타일)을 사용하는지에 대해 서술한다. 이러한 스타일을 사용하여 패킷 안에 데이터를 묶어 넣고, 각 패킷은 독립적인 통신을 하게 된다. 그리고 각 패킷마다 목적지를 명시해야 한다.

데이터그램 패킷은 편지와 같다. 당신은 개별적으로 하나씩 목적지를 명시하여 보낸다. 그것들은 순서가 바뀌어 도착할 수도 있다.

listen과 accept함수는 비연결 통신 스타일을 사용하는 소켓에서 사용할 수 없다.

11. 9. 1 데이터그램 전송

데이터그램소켓으로 데이터를 보내는 보통 방법은 'sys/socket. h'에 선언되어 있는 sendto함수를 사용하는 것이다. 데이터그램 소켓에 connect를 호출할 수 있지만, 나중에 데이터전송을 위해 기본경로를 한 번 명시한다. 소켓이 기본경로를 가지면, send를 사용할 수 있다. (11. 8. 5. 1절 [데이터 전송]을 보라) 또한 패킷을 전송하기 위해 write를 사용할 수 있다. (8. 2절 [I/O 추상] 을 보라) addr인수에 AF_UNSPEC의 주소포맷을 사용하여 connect를 호출함으로써 기본경로를 취소할 수 있다. connect함수에 관한 더 많은 정보는 11. 8. 1절 [연결하기] 을 보라.

int sendto (int socket, void *buffer size_t size, int flags, struct sockaddr *addr, size_t length) 함수

sendto함수는 목적지가 addr과 length인수로 명시되어 있는 socket을 통하여 버퍼에 있는 데이터를 전송한다. size인수는 전송할 바이트 수를 말한다.
flags는 send에서와 같은 방법으로 해석된다. 11. 8. 5. 3절 [소켓 데이터 옵션]을 보라.
반환치와 에러상태도 역시 send와 비슷하다. 그러나 시스템이 에러를 검출하게 할 수는 없다. 대부분의 에러는 패킷이 없어지거나 수신할 주소가 명시되어 있지 않은 것이다. 그래서 당신의 OS는 보통 에러가 난 것을 알지 못할 것이다. 예전의 호출에 관련된 문제 때문에 sendto는 에러가 났다고 할 수도 있다.

11. 9. 2 데이터그램 수신

recvfrom함수는 데이터그램 소켓으로부터 패킷을 읽고 또한 어디서부터 온 것인지 알려준다. 이 함수는 'sys/socket. h'에 선언되어 있다.

int recvfrom(int socket, void *buffer, size_t size, int flags, struct sockaddr *addr, size_t *length_ptr)함수

recvfrom함수는 socket소켓으로부터 패킷하나를 읽어 buffer라는 버퍼에 넣는다. size인수는 한 번 읽을 때 최대의 바이트 수이다. 만약 패킷이 size바이트보다 더 크면, 패킷의 앞부분만 얻을 수 있고 specify flags를 원하지 않으면 사용된다. (8. 2절[I/O Primitives]를 보라).
만약 (어디서부터 올 것인지 아는 경우, 누가 보냈던지 똑같이 취급하기 때문에) 누가 패킷을 보냈는지 알 필요가 없다면 recvfrom대신에 평탄한 recv를 쓸 수 있다. (11. 8. 5. 2절 [데이터 수신] 을 보라)

11. 9. 3 데이터그램 소켓 예제

여기에 파일이름공간의 데이터그램 스트림을 통해 메시지를 보내는 예제프로그램의 세트가 있다. 클라이언트와 서버프로그램들 모두가 소켓을 만들고 이름짓기 위하여, 11. 4절 [파일 이름공간]에 설명되어 있는 make_named_socket 함수를 사용한다.

먼저 여기 서버프로그램이 있다. 그것은 메시지가 도착할 때까지 계속 기다리고 메시지가 오면 다시 송신자에게 되돌려주는 것을 반복한다. 분명히 특별히 유용한 프로그램이 아니지만, 일반적인 아이디어를 보여준다.

#include <stdio. h>
#include <errno. h>
#include <stdlib. h>
#include <sys/socket. h>
#include <sys/un. h>
#define SERVER "/tmp/serversocket"
#define MAXMSG 512
int
main (void)
{
int sock;
char message[MAXMSG];
struct sockaddr_un name;
size_t size;
int nbytes;
/* Make the socket, then loop endlessly. */
sock = make_named_socket (SERVER);
while (1)
{
/* Wait for a datagram. */
size = sizeof (name);
nbytes = recvfrom (sock, message, MAXMSG, 0,
(struct sockaddr *) & name, &size);
if (nbytes < 0)
{
perror ("recfrom (server)");
exit (EXIT_FAILURE);
}
/* Give a diagnostic message. */
fprintf (stderr, "Server: got message: %s\n", message);
/* Bounce the message back to the sender. */
nbytes = sendto (sock, message, nbytes, 0, (struct sockaddr *) & name, size);
if (nbytes < 0)
{
perror ("sendto (server)");
exit (EXIT_FAILURE);
}
}
}

11. 9. 4 데이터그램 읽기 예제

여기 위에 있는 서버프로그램에 맞는 클라이언트 프로그램이 있다.

그것은 서버에게 데이터그램을 송신하고 나서 응답을 기다린다. 클라이언트를 위한 소켓에는 (서버와 같이) 주어진 이름을 가지고 있음을 주시하라. 그것은 서버가 메시지를 바로 클라이언트에게 되돌리게 한다. 소켓이 연계된 연결상태를 가지지 않기 때문에, 서버는 클라이언트의 이름을 참조하여 그렇게 할 수 있는 것이다.

#include <stdio. h>
#include <errno. h>
#include <unistd. h>
#include <stdlib. h>
#include <sys/socket. h>
#include <sys/un. h>
#define SERVER "/tmp/serversocket"
#define CLIENT "/tmp/mysocket"
#define MAXMSG 512
#define MESSAGE "Yow!!! Are we having fun yet?!?"
int
main (void)
{
externint make_named_socket (const char *name);
int sock;
char message[MAXMSG];
struct sockaddr_un name;
size_t size;
int nbytes;
/* Make the socket. */
sock = make_named_socket (CLIENT);
/* Initialize the server socket address. */
name. sun_family = AF_UNIX;
strcpy (name. sun_path, SERVER);
size = strlen (name. sun_path) + sizeof (name. sun_family);
/* Send the datagram. */
nbytes = sendto (sock, MESSAGE, strlen (MESSAGE) + 1, 0,
(struct sockaddr *) & name, size);
if (nbytes < 0) {
perror ("sendto (client)");
exit (EXIT_FAILURE);
}
/* Wait for a reply. */
nbytes = recvfrom (sock, message, MAXMSG, 0, NULL, 0);
if (nbytes < 0) {
perror ("recfrom (client)");
exit (EXIT_FAILURE);
}
/* Print a diagnostic message. */
fprintf (stderr, "Client: got message: %s\n", message);
/* Clean up. */
remove (CLIENT);
close (sock);
}

데이터그램소켓 통신은 비신뢰성임을 명심하라. 이 예제에서, 메시지가 서버에 도달하지 못하거나 서버가 응답을 하지 않을 지라도, 클라이언트 프로그램은 막연히 기다린다. 만약 원한다면, 그것을 종료시키고 리셋 시키는 프로그램을 유저가 수행하여야 한다. 자동적인 해결법은 select(8. 6절 [I/O기다리기])를 사용하여 응답의 시간제한을 설정하여, 시간제한이 초과되면 메시지를 재 전송하거나 소켓을 끝내고 종료하는 것이다.


11. 10 inetd 데몬

위에서 듣기를 하는 서버프로그램을 작성하는 법을 설명했다. 어떤 서버는 그것에 접속하기 위해 미리 수행되고 있어야 한다. 인터넷포트를 위한 서비스를 제공하는 다른 방법은 inetd 데몬 프로그램을 이용하여 듣기를 하도록 하는 것이다. inetd는 명시된 포트의 세트에서의 메시지를 (select를 사용하여) 기다리며 수행되는 프로그램이다. 그것이 메시지를 받았을 때, (만약 소켓스타일이 연결이 필요하면) 연결을 수락하고 나서, 해당 서버프로그램을 수행하도록 자식프로세스를 만들어 낸다. '/etc/inetd. conf'파일 내에 포트들과 프로그램들을 명시한다.


11. 10. 1 inetd 서버들

inetd에 의해 수행되는 서버프로그램을 작성하는 것은 매우 간단하다. 누군가 해당 포트로 연결을 연결하려는 때마다, 새로운 서버 프로세스가 시작된다. 연결은 단지 이 순간에만 존재한다. 소켓은 서버프로세스에서 표준 입력 서술자와 표준 출력 서술자 (서술자 0과 1)처럼 된다. 가끔은 프로그램이 일반적인 I/O기능을 필요로 할 때도 있다. ; 실제로, 소켓에 대해 아무 것도 모르는 범용 필터 프로그램도 inetd에 의해 바이트 스트림 서버처럼 동작할 수 있다.

비연결성 통신 스타일의 서버에도 inetd를 사용할 수 있다. 연결이 필요 없으므로, 이러한 서버들을 위해 inetd는 연결을 수락하려는 시도를 하지 않는다. 그것은 바로 서술자 0으로부터 수신된 데이터그램패킷을 읽을 수 있는 서버프로그램을 시작한다. 서버프로그램은 한 요구를 다룰 수 있고 종료할 수 있다. 또는 더 이상의 도착이 없을 때까지 요구를 읽는 것을 계속하고 나서 종료할 수도 있다. inetd를 설정할 때, 위의 두 가지 기술 중 어떤 것을 사용할 것인지 명시해야 한다.


11. 10. 2 inetd 설정

'/etc/inetd. conf'파일은 inetd에게 들을 포트번호와 수행될 서버프로그램이 무엇인지 말해준다. 보통 이 파일의 각 요소는 한 줄씩이다. 그러나, 그것을 화이트스페이스로 시작하는 요소의 첫 번째 줄을 제외한 모든 줄로 나눌 수 있다. '#'로 시작하는 줄은 주석 문이다.

여기 '/etc/inetd. conf'안의 두 기본적인 요소가 있다.

ftp stream tcp nowait root /libexec/ftpd ftpd
talk dgram udp wait root /libexec/talkd talkd

한 요소는 다음 형식을 가진다.

서비스 스타일 프로토콜 대기 사용자명 프로그램 인수

서비스 부분은 이 프로그램이 어떤 서비스를 제공하는 지를 말한다. 그것은 '/etc/services'안에 서비스의 이름이 정의되어 있을 것이다. inetd는 이 요소를 위해 들을 어떤 포트를 사용할 것인가 결정하는데 서비스를 사용한다.

스타일과 프로토콜 부분은 통신스타일과 소켓을 듣는데 사용되는 프로토콜을 명시한다. 스타일은 'SOCK_'이 빠지고, 소문자로 변환된 통신스타일의 이름이다. 예를 들면 stream 또는 dgram이다. 프로토콜은 '/etc/protocols'에 나열된 프로토콜 중 하나이다. 전형적인 프로토콜이름은 바이트 스트림 통신을 위한 'tcp'와 비신뢰성 데이터그램을 위한 'udp'가 있다.

대기 부분은 'wait' 또는 'nowait' 중 하나이다. 만약 통신스타일이 비연결성이고, 서버가 시작시에만 많은 다중 요구를 다룬다면 'wait'을 사용하라. 만약 inetd가 각 메시지나 요구가 들어 올 때마다 새로운 프로세스를 시작할 경우 'nowait'를 사용하라. 만약 스타일이 연결성이면 대기 부분은 'nowait'를 써라.

사용자명은 서버를 수행하는 사용자의 이름이다. inetd는 root로서 수행된다. 그래서 마음대로 그 자식의 사용자ID를 세트할 수 있다. 가능하면 사용자명에 'root'를 피하는 것이 좋다. ; 그러나 telnet과 ftp같은 어떤 서버는 사용자 이름과 암호를 읽는다. 이러한 서버들은, 네트워크로부터 오는 데이터에 의한 명령 같이 로그인 을 할 수 있도록 처음에 root가 될 필요가 있다.

프로그램 부분과 인수 부분은 서버를 시작할 때 수행되는 명령어를 명시한다. 프로그램은 수행 가능한 파일의 절대 경로의 파일이름이어야 한다. 인수는 화이트스페이스로 구분된 단어로 구성되어, 프로그램의 명령어라인 인수가 된다. 인수의 첫 번째 단어는 인수 0으로 (디렉토리 없이) 프로그램의 이름 그 자체로 된다.

만약 '/etc/inetd. conf'를 수정하면, inetd프로세스에게 SIGHUP신호를 inetd에게 파일을 다시 읽고, 새로운 내용을 얻게 하여야 한다. ps를 사용하여 inetd의 프로세스ID가 바뀌었는지 확인하면 된다.


11. 11 소켓 옵션

이 장에서는 소켓의 행동과 그 밑에 있는 통신 프로토콜을 변경하는 여러 가지 옵션을 설정하고 읽는 방법을 서술한다. 소켓옵션을 다룰 때, 옵션이 속하는 레벨을 명시해 주어야 한다. 이것은 옵션이 소켓인터페이스에 적용되는 지, 저수준 통신 프로토콜 인터페이스에 적용되는 지를 서술한다.


11. 11. 1 소켓 옵션 함수

여기에 소켓옵션을 변경하고 시험하는 함수가 있다. 'sys/socket. h'안에 그것들이 선언되어 있다.

int getsockopt (int socket, int level, int optname, void *optval, size_t *optlen_ptr) 함수

getsockopt함수는 socket소켓의 level레벨의 optname 옵션의 옵션 값에 대한 정보를 얻는다.
옵션 값은 optval이 가리키는 버퍼에 저장된다. 호출 전에, 이 버퍼의 크기인 *optlen_ptr안에 제공해야 한다. 반환할 때, 실제로 버퍼에 저장된 정보의 바이트 수를 담고 있다.
대부분의 옵션은 optval버퍼를 정수 값으로 해석된다.
성공시 getsockopt의 실제반환값은 0이고 실패시 -1이다.  
다음에 errno에러조건이 정의되어 있다.

EBADF : socket인수가 유효한 파일 서술자가 아니다.

ENOTSOCK : 서술자 socket이 소켓이 아니다.

ENOPROTOOPT : optname이 주어진 레벨에 맞지 않다.

int setsockopt (int socket, int level, int optname, void *optval, size_t optlen) 함수

이 함수는 socket소켓의 level레벨에 소켓옵션 optname을 설정하는데 사용된다. 옵션의 값은 optlen크기를 가진 optval버퍼에 전달된다. setsockopt의 반환치와 에러코드는 getsockopt과 같다.

11. 11. 2 소켓-레벨 옵션

int SOL__SOCKET 상수

이 절에 서술되어 있는 소켓레벨 옵션을 다루려면, getsockopt 나 setsocket의 level인수로 이 상수를 사용하라. 여기에 socket-level 옵션 이름의 표가 있다. 모두 'sys/socket. h'헤더 파일에 정의되어 있다.

SO_DEBUG

하위 프로토콜 모듈의 디버깅 정보의 저장할 것인가 여부를 설정한다. 그 값은 int형을 가진다. 0이 아닌 값은 저장하겠다는 것을 의미한다.

SO_REUSEADDR

이 옵션은 bind( 11. 3. 2절 [주소 설정]을 보라)가 이 소켓의 지역주소를 재 사용하는 것을 허락할 것인지 여부를 제어한다. 만약 이 옵션을 가능하도록 한다면, 같은 인터넷 포트로부터 두 소켓을 가질 수 있게 된다; 그러나, 시스템은 인터넷을 혼란시키는 두 별개의 이름을 가진 소켓을 사용하는 것을 허락하지 않는다. 이 옵션이 있는 이유는 FTP같은 상위 프로토콜이 같은 소켓번호의 재사용을 유지하기를 원하기 때문이다. 값은 int형을 가지며, 0이 아닌 수는 "그렇다(yes)"를 의미한다.

SO_KEEPALIVE

이 옵션은 하위 프로토콜이 주기적으로 메시지를 연결된 소켓으로 전송할 것인지 여부를 제어한다. 만약 서로 이러한 메시지의 응답을 실패하면 연결이 끊어지도록 한다. 값은 int형이고 0이 아닌 값은 "그렇다"를 의미한다.

SO_DONTROUTE

이 옵션은 송신하는 메시지가 일반 메시지 라우팅 기능을 우회할 것인지 여부를 제어한다. 만약 설정되면, 메시지는 직접 네트워크 인터페이스로 보내진다. 값은 int형이며 0이 아닌 값은 "그렇다"의 의미이다.

SO_LINGER

이 옵션은 신뢰도 있는 전달을 보장하는 어떤 형태의 소켓이 닫힐 때 여전히 전송되지 못한 메시지들을 가지고 있을 때, 무엇이 일어날 것인지를 명시한다. 11. 7. 2절 [소켓 닫기]을 보라. 값은 구조체 linger이다.

struct linger 자료형

이 구조체는 다음 멤버를 가진다:
int l_onoff
이 부분은 참거짓으로 해석된다. 만약 0이 아니면, close는 데이터가 전달되거나 시간제한이 경과될 때까지 프로그램의 수행이 중단된다.
int l_linger
이것은 초단위로 시간제한을 명시한다.

SO_BROADCAST

이 옵션은 데이터그램이 소켓으로부터 브로드캐스트될 것인지 여부를 제어한다. 값은 int형이고 0이 아닌 값은 "그렇다"를 의미한다.

SO_OOBINLINE

만약 이 옵션이 설정되면, 소켓에 수신된 out-of-band 데이터가 보통 입력 큐에 위치하게 된다. 이것은 MSG_OOB플랙 명시 없이 read나 recv를 사용하여 그것을 읽을 수 있도록 한다. 11. 8. 8절 [Out-of-Band 데이터]을 보라. 값은 정수형이며 0이 아닌 값은 "그렇다"를 의미한다.

SO_SNDBUF

이 옵션은 출력버퍼의 크기를 얻거나 설정한다. 값은 size_t형이고, 바이트 크기이다.

SO_RCVBUF

이 옵션으 입력버퍼의 크기를 얻거나 설정한다. 값은 size_t형이고, 바이트 크기이다.

SO_STYLE, SO_TYPE

이 옵션은 getsockopt에만 사용될 수 있을 것이다. 그것은 소켓의 통신 스타일을 얻는데 사용된다. SO_TYPE은 전통적인 이름이고 SO_STYLE은 GNU에서 채택한 이름이다. 값은 int형이고, 통신스타일을 나타낸다. 11. 2절 [통신 스타일]을 보라.

SO_ERROR

이 옵션은 getsockopt에만 사용할 수 있을 것이다. 소켓의 에러상태를 리셋 하는데 사용된다. 값은 int형이고 전번의 에러 상태를 나타낸다.


11. 12 네트워크 데이터베이스

많은 시스템은 시스템 개발자들에게 알려진 네트워크 리스트를 기록하는 데이터베이스를 가지고 있다. 이것은 보통 '/etc/networks' 또는 네임서버의 같은 파일을 유지한다. 이 데이터베이스는 route같은 라우팅 프로그램에서 유용하다. 그러나 네트워크를 통해 단순히 통신하는 프로그램에게는 별 필요가 없다. 우리는 'netdb. h'에 선언된 이 데이터베이스 접근 함수를 볼 것이다.

struct netent 자료형

이 자료형은 네트워크 데이터 베이스의 요소에 관한 정보를 표현하는데 사용된다. 그것은 다음 멤버를 가진다.

char *n_name

이것은 네트워크의 공식적인 이름이다.

char **n_aliases

네트워크의 또다른 이름들이 문자열의 벡터처럼 표현되어 있다. 널 포인터로 배열이 끝난다.

int n_addrtype

이것은 네트워크 번호의 종류이다. ; 이것은 인터넷 네트워크에 대해 항상 AF_INET이다.

unsigned long int n_net

이것은 네트워크 번호이다. 네트워크 번호는 호스트 바이트 순서로 반환된다. 11. 5. 5절 [바이트 순서]을 보라.

특정 네트워크에 관한 정보를 위해 네트워크 데이터베이스를 검색하기 위해 getnetbyname 또는 getnetbyaddr함수를 사용하라. 정보는 정적으로 할당된 구조체에 정보가 반환된다. ; 만약 그것을 저장할 필요가 있으면 그 정보를 복사해두어야 한다.

struct netent * getnetbyname (const char *name) 함수

getnetbyname함수는 name이라는 네트워크에 관한 정보를 반환한다. 만약 그와 같은 네트워크가 없으면 널 포인터를 반환한다.

struct netent * getnetbyaddr (long net, int type) 함수

getnetbyaddr함수는 type라는 종류와 net라는 숫자를 가진 네트워크에 관한 정보를 반환한다. 인터넷 네트워크의 종류를 위해서 AF_INET값을 명시해야 한다. getnetbyaddr은 만약 그러한 네트워크가 없으면 널 포인터를 반환한다. 또한 setent, getnetent 와 endnetent를 사용하여 네트워크 데이터 베이스를 일일이 살필 수 있다. 이러한 함수를 사용할 때는 주의하라. 그것들은 재진입성이 없기 때문이다.

void setnetent (int stayopen) 함수

이 함수는 네트워크 데이터베이스를 맨 앞부터 읽을 수 있도록 한다. 만약 stayopen인수가 0이 아니면, 플랙을 세트하여 추후의 getnetbyname이나 getnetbyaddr 호출이 (보통 그 함수들이 닫으므로)데이터베이스를 닫지 않도록 한다. 만약 각 호출시 데이터베이스를 다시 여는 것을 피하여 그러한 함수를 여러 번 호출할 때 매우 유용하다.

struct netent * getnetent (void) 함수

이 함수는 네트워크 데이터베이스의 다음 요소를 반환한다. 만약 더 이상의 요소가 없으면 널 포인터를 반환한다.

void endnetent (void) 함수

이 함수는 네트워크 데이터 베이스를 닫는다.

+ Recent posts