ddingz 2021. 12. 26. 12:37

소켓 프로그래밍에서 첫 번째로 해야 할 일은 통신 창구 역할을 하는 소켓을 만드는 것이다.
이것은 서버와 클라이언트에서 모두 필요한데 이를 위하여 socket() 함수를 이용한다.
socket()이 성공적으로 수행되면 새로 만들어진 소켓번호를 리턴하고 에러가 발생하면 -1이 리턴되며 이때 전역변수 errno에 에러코드가 들어간다.
socket()의 사용 문법은 다음과 같다.

#include <sys/socket.h>
int socket(
    int domain, // 프로토콜 체계
    int type, // 서비스 타입
    int protocol // 소켓에서 사용할 프로토콜
)

domain으로 지정할 수 있는 프로토콜 체계에는 다음과 같은 것이 있다.

  • PF_INET (인터넷 프로토콜 체계 사용)
  • PF_INET6 (IPv6 프로토콜 체계 사용)
  • PF_UNIX (유닉스 방식의 프로토콜 체계 사용)
  • PF_NS (XEROX 네트워크 시스템의 프로토콜 체계 사용)
  • PF_PACKET (리눅스에서 패킷 캡쳐를 위해서 사용)

socket()의 두 번째 인자 type은 서비스 타입을 말하는데, TCP를 이용하려면 SOCK_STREAM을, UDP를 이용하려면 SOCK_DGRAM을 선택하여야 한다.

  • SOCK_STREAM (TCP 소켓 생성)
  • SOCK_DGRAM (UDP 소켓 생성)
  • SOCK_RAW (RAW 소켓 생성)

socket()의 세 번째 인자 protocol은 이 소켓에서 어떤 프로토콜을 사용할지를 지정하는데 type에서 SOCK_STREAM이나 SOCK_DGRAM을 정한 경우에는 이미 프로토콜이 TCP나 UDP로 정해졌으므로 protocol 값은 사용되지 않으며 0을 입력하면 된다.
type에서 SOCK_RAW를 선택한 경우에는 protocol 필드가 구체적인 상위 프로토콜을 지정하는 데 사용된다.


open_socket.c

socket()을 호출하고 생성된 소켓번호를 출력하는 예제이다.
이 프로그램에서는 /etc/passwd 파일을 열고 리턴된 파일 디스크립터를 출력한 후, 소켓을 두 개 열어서 소켓번호가 어떤 값인지 확인한다.
다음에는 다른 파일(/etc/hosts)을 하나 더 열어서 파일 디스크립터를 출력한다.

#include <stdio.h> // 표준 라이브러리
#include <stdlib.h> // 표준 라이브러리
#include <sys/types.h> // 소켓 시스템 콜에 필요한 상수 선언
#include <sys/stat.h> // 파일의 상태에 대한 데이터 선언
#include <sys/socket.h> // 소켓 시스템 콜 선언
#include <fcntl.h> // open에 필요한 flag 선언

int main(void) {
    // 파일 및 소켓번호
    int fd1, fd2, sd1, sd2;

    // 파일 열기
    fd1 = open("/etc/passwd", O_RDONLY, 0);
    printf("/etc/passwd's file descriptor = %d\n", fd1);

    // 스트림형 소켓 열기
    sd1 = socket(PF_INET, SOCK_STREAM, 0);
    printf("stream socket descriptor = %d\n", sd1);

    // 데이터그램형 소켓 열기
    sd2 = socket(PF_INET, SOCK_DGRAM, 0);
    printf("datagram socket descriptor = %d\n", sd2);

    // 또 다른 파일 열기
    fd2 = open("/etc/hosts", O_RDONLY, 0);
    printf("/etc/host's file descriptor = %d\n", fd2);

    // 파일 및 소켓 닫기
    close(fd2);
    close(fd1);
    close(sd2);
    close(sd1);

    return 0;
}

실행 결과

파일을 open하여 얻는 파일 디스크립터가 3번부터 배정되는 이유는 시스템이 이미 0, 1, 2번 파일 디스크립터를 각각 표준 입력, 표준 출력, 그리고 표준 에러 출력으로 사용하기 때문이다.