리눅스 커널의 관점에서 웹 소켓은 네트워크 상의 두 프로그램 간 양방향 통신을 위한 엔드포인트다.
UNIX 관점에서 본다면 소켓은 식별자를 가진 열린 파일이다.
소켓 인터페이스는 네트워크 애플리케이션을 설계하기 위해 UNIX 입출력 함수와 연결하는데 쓰이는 함수의 집합이다.
클라이언트에서 TCP/IP로 넘어가기 전에 시스템 콜을 하는 영역으로,
클라이언트에서 연결을 요청하면 클라이언트 소켓 주소에 들어가는 포트는 커널에서 자동으로 배정한다.
여기서 처음 TCP 서버에서 socket을 미리 만드는데,
이 소켓은 클라이언트로부터 정보를 받는 소켓이고,
클라이언트와 통신할때는 새로운 소켓을 만든다.
생각보다 중요한 개념이니 잘 알아두고 넘어가자
소켓 생성 socket()
클라이언트/서버가 소켓 판별자(socket descriptor)를 만들기 위해 사용하는 함수이다.
clientfd = socket(AF_INET , SOCK_STREAM , 0);
AF_INET : 네트워크 주소체계를 알려준다. AF_INET는 32비트 주소를 나타낸다.(IPv4)
type : 소켓이 어떤 타입(TCP/UDP 등)인지 알려준다. SOCK_STREAM은 TCP 프로토콜을 사용한 통신 소켓이라는 뜻이다.
protocol : 프로토콜 정보. 앞의 두개 인자를 이용해 프로토콜을 특정할 수 없을때 따로 명시해준다. 그 외는 0으로 처리한다.
소켓 주소 할당
socket()함수로 socket descriptor를 할당받고 소켓 유형을 지정한 후 , IP주소와 포트 번호를 할당하기 위해 위의 소켓 구조체를 정의해야 한다.
generic 소켓 주소 구조체 sockaddr
/* Generic socket address structure (for connect, bind, and accept) */
struct sockaddr
{
uint16_t sa_family; /* Protocol family */
char sa_data[14]; /* Address data */
};
이 소켓의 종류가 무엇인지 담고있는 , 말 그대로 일반적인 형태의 구조체이다.
IPv4는 sockaddr_in 이라는 전용 구조체가 따로 있다.
IPv4 인터넷 소켓 주소 구조체 sockaddr_in
/* IP socket address structure */
struct sockaddr_in {
uint16_t sin_family; /* Protocol family (always AF_INET) */
uint16_t sin_port; /* Port number in network byte order */
struct in_addr sin_addr; /* IP address in network byte order */
unsigned char sin_zero[8]; /* Pad to sizeof(struct sockaddr) */
};
아래는 예제의 일부분이다.
....
struct sockaddr_in serv_addr;
char* hello = "Hello from client";
char buffer[1024] = { 0 };
if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// Convert IPv4 and IPv6 addresses from text to binary
....
다음 선언으로 소켓을 생성하고
다음 선언으로 familt 와 포트 번호를 설정한다.
bind()
소켓에 주소를 할당하는 함수이다.
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
// Returns 0 if OK, -1 on error
bind 함수는 커널에게 addr에 있는 서버의 소켓 주소를 소켓 식별자 socketfd와 연결 해달라고 한 후 , 성공시 0 , 에러시 -1을 리턴한다.
listen()
연결 요청을 대기하는 함수이다. 서버는 커널에게 해당 디스크립터가 클라이언트가 아닌 서버에 의해 사용 될 것이라고 명시해 주기 위해서 listen()을 호출한다.
#include <sys/socket.h>
int listen(int sockfd, int backlog);
// Returns: 0 if OK, −1 on error
accept()
열결 요청을 수락하는 함수
#include <sys/socket.h>
int accept(int listenfd, struct sockaddr *addr, int *addrlen);
// Returns: nonnegative connected descriptor if OK, -1 on error
듣기 식별자와 연결 식별자
listen.desc 와 connected desc는 소켓이 서버와 연결되었는지 아닌지 여부를 나타내며 , 소켓의 상태를 구분한다.
listen desc.는 클라이언트의 접속을 기다리는 상태의 네트워크 소켓이다.
클라이언트 요청에 대한 끝점으로 한번 생성되며 서버가 살아있는 동안 계속 존재한다.
connected desc. 는 이미 서버와 연결되어 데이터를 주고받는 상태의 네트워크 소켓.
클라이언트와 서버 사이에 연결된 연결의 끝점으로 , 서버가 연결 요청을 수락할 때 마다 생성되며,
서버가 클라이언트에 서비스 하는 동안에만 존재한다.
이 두 디스크립터를 구분함으로써 많은 클라이언트 연결을 동시에 처리 할 수 있는 동시성 서버를 만들 수 있다.
bind() , listen() , accept() 세 가지 함수를 순차적으로 호출함으로써 서버 측에서 데이터 송수신 준비를 마칠 수 있다.
최종적으로 클라이언트 측에서 connect() 함수를 통해 연결한 뒤 , write() 함수로 실제 데이터를 출력한다.
데이터 송수신이 완료 되면 , close()시스템 콜을 통해 소켓의 자원을 모두 제거해 준다.
서버에서는 socket()함수로 소켓 디스크립터를 할당받은 뒤 bind() , listen() , accept() 함수를 순차적으로 호출해 클라이언트와 연결 할 수 있었다.
클라이언트에서는 대기중인 서버에 연결 요청(connect)을 통해 클라이언트 - 서버 간의 연결을 생성 할 수 있다.
connect()
서버에 연결 요청을 보낸다.
#include <sys/socket.h>
int connect(int clientfd, const struct sockaddr *addr,
socklen_t addrlen);
// Returns: 0 if OK, −1 on error
++ TCP의 3way handshake
클라이언트에서 connect()를 호출하면 listen() - 대기중이던 서버에서 연결이 요청되고
서버는 accept()함수를 불러 서버와 클라이언트 간의 연결이 생성된다.
close()
통신을 끝낸다.
#include <sys/socket.h>
int close(int socket_fd);
// Returns : 0 if OK , -1 on error
shutdown()
#include <sys/socket.h>
int shutdown(int socket_fd , int how_to);
// Returns : 0 if OK , -1 on error
close와 같이 소켓을 종료하지만 두번째 매개변수 how_to의 값에 따라 readbuffer와 writebuffer를 차단할 지 선택할 수 있다.
++ 버퍼는 보내는 버퍼/받는 버퍼로 생각하면 편하다.
메세지를 보낼 경우 버퍼에 쌓아두고 순차적으로 데이터를 보낸다는 것이다.
SHUT_RD : recv buffer 만 차단한다.
위처럼 호출 된 경우 더이상 해당 소켓으로 부터 통신을 수신할 수 없다.
SHUT_WR : send buffer 만 차단한다.
위처럼 호출 된 경우 더이상 해당 소켓(s)에게 송신을 할 수 없다.
SHUT_RDWR : 두 버퍼 모두 차단한다.
위처럼 호출 한 경우 더이상 해당 소켓과 송수신을 할 수 없다.
파일 서술자 라고도 불리며 특정한 파일에 접근하기 위한 추상적인 키이다.
이 용어는 POSIX 운영체제에 쓰인다.(POSIX = 서로다른 UNIX os의 공통 API를 정리한 인터페이스 규격)
POSIX에서 파일 디스크립터는 정수, C언어 int형을 의미한다.
Datagram socket
Stream Socket
1. 프로토콜 : TCP(transmission control protocol)
2. 특징: