소켓의 생성
int serv_fd = socket(PF_INET, SOCK_STREAM, 0);
C
복사
•
c언어에서는 위처럼 socket() 함수를 통해 소켓을 생성할 수 있다. socket() 함수는 프로토콜 체계, 데이터 전송방식, 프로토콜 정보 순으로 매개변수를 전달 받고, 성공 시에는 파일 디스크립터를 반환하며 실패 시에는 -1을 반환한다.
•
첫 번재 인자인 프로토콜 체계를 전달하는 PF_INET의 경우에는 IPv4를 의미하며 AF_INET을 사용하여도 된다. 차이는 프로토콜 체계를 의미하는가와 프로토콜 패밀리 정보를 의미하는가의 차이이다. 다만 프로토콜 체계를 설정하는 곳에는 PF_INET을, 주소 체계를 설정하는 곳에는 AF_INET을 사용하는 것이 권장된다.
•
두 번째 인자로 오는 SOCKET_STREAM의 경우에는 생성하는 소켓이 TCP 소켓인 것을 의미한다. UDP 소켓을 생성하고 싶으면 SOCK_DGRAM을 사용하면 된다.
•
세 번재 인자는 프로토콜 정보인데, 앞선 두 인자만으로 프로토콜을 특정할 수 없을 경우에만 따로 명시해주면 된다.
소켓 주소 할당
•
프로토콜과 소켓 유형을 지정하여 생성한 소켓을 이용해 다른 네트워크의 클라이언트와 통신하기 위해서는, IP 주소와 port 번호를 할당해주어야한다. IP 주소를 통해 네트워크 상에서 컴퓨터를 특정하고, port 번호를 통해 프로세스를 찾아가기 위함이다.
struct sockaddr_in {
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
C
복사
•
c언어의 sockaddr_in 구조체를 통해 IP 주소와 port 번호를 지정해줄 수 있다. 구조체는 위와 같이 구성되어 있다.
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(6667);
C
복사
•
sin_family는 IPv4나 IPv6, 로컬통신 등 주소체계를 가지는 부분이다. 위에서 설명했듯이 주소체계를 가지므로 AF_INET의 사용이 권장된다.
•
sin_addr는 IP 주소를 할당하는 부분으로, 주소를 할당할 수 있는 여러 함수들이 있고 그 중에 적절한 것을 골라서 사용하면 된다. inet_addr의 경우에는 문자열로 된 IP 주소를 32비트의 값으로 바꾸어주는 함수이고, 그 외에도 htonl과 같은 함수도 있다. 구조체를 타고 들어가다보면 _uint32_t로 선언되어 있는 것을 알 수 있다. 이는 IP 주소가 32비트를 의미하는데, 32bit = 4byte 이므로 IPv4에 해당한다.
•
sin_port의 경우에는 16비트의 port 번호를 받는 변수이다. 위에서 언급한 htonl이나 htons의 경우에는 host to network로 Host system의 Byte Order에 맞게 데이터를 변환해주는 함수다. Byte Order는 CPU가 메모리에 데이터를 저장하는 방식으로 little endian, big endian이 있다.
if (bind(serv_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr) == -1) {
...
}
C
복사
•
이처럼 위에서 설정한 sockaddr_in 구조체를 bind의 매개변수로 넘겨주어야, 비로소 소켓에 IP 주소와 port 번호를 할당할 수 있다.
클라이언트 연결 요청 대기
•
bind() 함수를 통해 IP 주소와 port 번호를 할당해주었으니, 이제 클라이언트가 서버 소켓에 연결할 수 있게 요청 대기 상태로 만들어주어야 한다.
if (listen(serv_fd, 5) == -1) {
...
}
C
복사
•
첫 번째 인자로, 서버 소켓의 파일 디스크립터 번호를 전달 받는다.
•
두 번째 인자로, 연결 요청하는 대기 큐의 크기를 전달 받는다. 연결 요청 대기 큐는 클라이언트가 연결을 요청했을 때 서버가 바쁘다면, 시스템에 순서대로 클라이언트의 요청을 대기시켜 놓을 수 있는 큐이다.
•
위와 같이 listen() 함수를 호출하고 나면 클라이언트에서 연결 요청을 시도할 수 있다.
클라이언트 연결 요청 수락
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
int client_fd = accept(serv_fd, (struct sockaddr *)&client_addr, &len);
C
복사
•
accept() 함수는 첫 번째 인자로 서버 소켓의 파일 디스크립터 번호를 전달 받는다.
•
두 번째 인자로, 클라이언트의 주소정보를 전달 받는다.
•
세 번째 인자로, 클라이언트의 주소정보를 저장하고 있는 sockaddr_in 구조체의 크기를 전달 받는다.
•
이처럼 accept() 함수를 사용하여 클라이언트의 요청을 수락할 수 있는데, accept() 함수는 새로운 파일 디스크립터를 할당하여 반환한다.
연결 해제
•
위의 절차대로 bind(), listen(), accept() 함수를 순서대로 호출하면 서버 측에서 클라이언트의 연결을 요청받고 데이터를 송수신할 준비를 할 수 있다.
•
이렇게 연결된 클라이언트를 끊고 싶거나 서버를 내리고 싶다면, close() 함수를 호출하여 시스템에서 소켓에 할당된 자원을 회수할 수 있다.