210124 개발일지(48일차) - 컴퓨터 시스템 11장 웹서버 프로젝트(3) : 소켓 프로그래밍 (서버 및 클라이언트를 만드는 데 필요한 함수)

고재개발·2021년 1월 24일
0

Computer System

목록 보기
10/13

지난 번 포스팅에서 소켓 인터페이스에 대해 간략히 알아봤다.
이번에는 서버와 클라이언트를 만들 때 필요한 함수들을 자세히 뜯어보며 공부해보자.

소켓 및 소켓 디스크립터(socket descriptor)

소켓 디스크립터는 소켓의 파일 디스크립터라고 생각하면 된다.
소켓이란 유닉스 파일 디스크립터(file descriptor)를 이용하여 다른 프로그램과 정보교환을 하는 방법(혹은 도구) 이다. 일반적으로 유닉스 상에서 정보교환은 파일 디스크립터를 통해 한다. 마찬가지로 소켓을 이용한 지역 혹은 네트워크로 연결된 프로그램 간의 정보교환 역시 파일 디스크립터를 통해서 이루어진다.

서버(server)를 만드는 데 필요한 함수

서버 입장에서 생각하면서 아래 함수들을 보면, 이해에 조금은 도움이 될 것이다.

1. socket() 함수 : 소켓 디스크립터(descriptor)를 만드는 함수

소켓 디스크립터 혹은 소켓을 만들어주는 함수라고 생각하면 된다.

#include <sys/socket.h>
int socket(int domain, int type, int protocol)

1-1. int domain : AF_UNIX, AF_INET, AF_INET6등 어떤 영역에서 통신할 것인지에 대한 영역을 지정한다.
1-2. int type : SOCK_STREAM, SOCK_DGRAM, SOCK_RAW등 TCP/UDP/RAW중 어떤 소켓을 사용할 것인지를 지정한다.
1-3. int protocol : 소켓에서 사용할 프로토콜을 지정하는 것으로 IPPROTO_TCP, IPPROTO_UDP, 0 등을 사용한다. 0은 type에서 미리 정해진 경우, 그를 따른다는 의미다.
1-4. 리턴값 : 소켓 디스크립터를 반환하며, -1은 소켓 생성 실패를 의미하고 0이상 값은 각 소켓 디스크립터(고유번호라고 생각)를 의미한다.

//코드 예시 : ipv4 도메인의 TCP타입을 위한 소켓 생성
serv_socket = socket(PF_INET, SOCK_STREAM, 0);
if (serv_socket == -1)
    printf("socket error\n");

2. bind()함수 : 소켓 descriptor와 IP주소/포트번호를 결합하는 함수

클라이언트가 서버를 잘 찾아왔어도, 통신을 위해 결합하고자 하는 소켓 디스크립터를 찾아야 한다. 그래서 연결해주는 bind()함수가 필요한 것이다.

#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);

1-1. int sockfd : socket()함수에서 리턴값으로 받은 소켓 디스크립터(고유번호)를 넣어준다고 생각하면 된다.
1-2. struct sockaddr *myaddr : 서버의 ip주소를 넣어준다.(ip정보 연결)
1-3. socklen_t addrlen : 주소의 길이를 넣어준다.
1-4. 리턴값 : 성공 시 0, 실패 시 -1을 리턴한다.

//코드 예시 : 소켓과 서버 주소를 연결
if(bind(serv_socket, (struct sockaddr*) &serv_socket_addr, sizeof(serv_socket_addr)) == -1)
    printf("bind error");

3. listen()함수 : 대기상태(클라이언트의 접속 요청을 받을 수 있는)로 만드는 함수

소켓 디스크립터를 "듣기 소켓"으로 변신시켜준다.

#include <sys/socket.h>
int listen(int sockfd, int backlog);

1-1. int sockfd : bind()함수와 마찬가지로, socket()함수에서 리턴값으로 받은 소켓 디스크립터(고유번호)를 넣어준다고 생각하면 된다.
1-2. int backlog : 연결 대기열의 크기를 지정한다. 연결요청 소켓들이 대기하는 연결 대기열의 크기를 정하는 것이라 생각하면 된다. 네트워크 상태와 서비스 종류에 따라 달라진다고 한다.
1-3. 리턴값 : 성공 시 0, 실패 시 -1을 리턴한다.

//코드 예시 : 소켓을 듣기 소켓으로 바꿔주고, 연결 대기열 10개를 생성
if(listen(serv_socket, 10) == -1)
    printf("listen error");

4. accept()함수 : 소켓(디스크립터)에 클라이언트를 연결하는 함수

이제 서버와 클라이언트를 연결하는 단계다.

#include <sys/socket.h>
int accept(int listenfd, struct sockaddr *client_addr, socklen_t client_addrlen);

1-1. int listenfd : listen()함수에서 듣기 소켓으로 바뀐 listenfd를 넣어준다.
1-2. struct sockaddr *cliend_addr : 클라이언트의 주소 정보를 담고있는 구조체를 넣어준다.
1-3. socklen_t client_addrlen : 앞의 인자 값(클라이언트 주소 정보 구조체)의 길이를 넣어준다.
1-4. 리턴값 : 성공 시 연결 식별자(connected descriptor = connfd), 실패 시 -1을 리턴한다.

※ 듣기 식별자는 한 번 생성되면, 서버가 살아있는 동안 계속 존재한다. 반면, 연결 식별자는 서버-클라이언트가 연결될 때만 생성되며, 클라이언트에 서비스하는 동안만 존재한다.

클라이언트(client)를 만드는 데 필요한 함수

클라이언트 입장에서 생각하면서 아래 함수들을 보면, 이해에 조금은 도움이 될 것이다.

1. socket() 함수 : 소켓 디스크립터(descriptor)를 만드는 함수

서버에서와 같이 소켓(혹은 소켓 디스크립터)를 만든다.

#include <sys/socket.h>
int socket(int domain, int type, int protocol);

1-1. int domain : AF_UNIX, AF_INET, AF_INET6등 어떤 영역에서 통신할 것인지에 대한 영역을 지정한다.
1-2. int type : SOCK_STREAM, SOCK_DGRAM, SOCK_RAW등 TCP/UDP/RAW중 어떤 소켓을 사용할 것인지를 지정한다.
1-3. int protocol : 소켓에서 사용할 프로토콜을 지정하는 것으로 IPPROTO_TCP, IPPROTO_UDP, 0 등을 사용한다. 0은 type에서 미리 정해진 경우, 그를 따른다는 의미다.
1-4. 리턴값 : 소켓 디스크립터를 반환하며, -1은 소켓 생성 실패를 의미하고 0이상 값은 각 소켓 디스크립터(고유번호라고 생각)를 의미한다.

//코드 예시 : ipv4 도메인의 TCP타입을 위한 소켓 생성
serv_socket = socket(PF_INET, SOCK_STREAM, 0);
if (serv_socket == -1)
    printf("socket error\n");

2. connect()함수 : 서버에 연결을 요청하는 함수 

서버에 연결을 요청하는 함수

#include <sys/socket.h>
int connect(int sockfd, struct sockaddr* serv_addr, socklen_t addrlen);

1-1. int sockfd : 클라이언트측 socket()함수의 리턴값으로 받은 소켓 디스크립터를 여기 넣는다.
1-2. sockaddr* serv_addr : 서버의 주소 정보를 넣어준다.
1-3. socklent_t addrlen : 서버의 주소 정보 길이를 넣어준다.
1-4. 리턴값 : 성공 시 0, 실패 시 -1을 리턴한다.

3. close()함수 : 소켓을 닫고 통신을 종료하는 함수

소켓을 닫는다.

###include <unistd.h>
int close(int sofckfd);

1-1. int sockfd : 종료할 소켓 디스크립터를 인자로 넣는다.
1-2. 리턴값 : 성공 시 0, 실패 시 -1을 리턴한다.

그 외의 함수들

여기까지 서버와 클라이언트에서 사용하는 함수들에 대해 알아보았다. 이 외에 wirte/read 및 send/recieve 함수들도 있지만 간단하고, 상황에 따라 달라져서 다루지 않았다.
간략한 소켓프로그래밍에 대해서는 여기까지 알아보는 것으로 하자~
마지막으로, 많은 도움을 받은 블로그를 공유한다.
https://jhnyang.tistory.com/251?category=947031

profile
고재개발

0개의 댓글