[Network] 네트워크 프로그래밍과 소켓



네트워크 프로그래밍과 소켓


네트워크 프로그래밍은 둘 이상의 컴퓨터에서 데이터를 주고 받는 프로그램을 구현하는 것을 말하며, 이 때 소켓 (socket) 을 기반으로 프로그래밍을 하기 때문에 소켓 프로그래밍이라고도 한다.



1. 소켓 (socket) 이란?


두 장치 간에 어떤 데이터를 송수신 하려면 어떤 연결이 필요한데, 이 연결의 매개체가 되는 것을 소켓이라고 한다. 즉 이 소켓을 통해 다수의 장치는 데이터를 주고 받을 수 있게 되며, 소켓을 닫아버리면 비로소 장치 간의 연결이 끊어지게 되는 것이다. 예를 들어, 서버와 클라이언트에서 연결을 하기 위해서 소켓을 열어 데이터를 주고 받고, 데이터 송수신이 끝나고 소켓을 닫아 연결을 끊어버리는 것이다. 쉽게 말해 소켓은 두 장치 간의 연결을 위한 도구이다. 소켓은 전화기를 예를 들어 설명할 수 있다.


1. 소켓 생성


전화기를 하나 사오는 것으로 비유할 수 있다. socket()을 이용하여 장치 간의 연결을 위한 소켓을 하나 생성한다. 주의할 점은 데이터를 송신하는 소켓과 수신하는 소켓의 구현에는 조금 차이가 있다. 

#include <sys/socket.h>

int socket(int domain, int type, int protocol);


2. 소켓의 주소 할당 및 연결


전화기는 사자마자 바로 전화를 걸고 받을 수 있는게 아니다. 해당 전화기에 전화 번호가 부여되는 것 처럼, 소켓을 사용하려면 소켓에 주소 정보를 할당하여 통신하게끔 한다. 이 때 소켓의 주소 정보는 IP와 PORT 번호로 구성 된다. bind()를 이용하여 주소를 할당해준다. 이 때 성공하면 0을, 실패하면 -1을 반환한다.

#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
 

3. 연결 요청이 가능한 상태의 소켓


전화기에 전화 번호를 부여하면 이 전화기는 이제 외부의 어디에서 걸려오든 간에 전화를 받을 수 있게 된다. 소켓 또한 주소가 할당되면 통신 할 상태가 되는데, 위에서 얘기했듯이 데이터를 주는 소켓과 데이터를 받는 소켓에는 차이가 있기 때문에 데이터 송신 소켓은 데이터를 보내기 위한 용도의 소켓일 뿐, 해당 소켓이 데이터를 받지는 않으므로 데이터 송신 소켓은 연결 요청을 위한 상태를 체크할 필요가 없다. 연결 요청 상태 체크는 데이터 수신 소켓에서만 체크해주면 된다. listen()을 이용하여 연결 요청 상태를 체크하며, 성공하면 0을, 실패하면 -1을 반환한다.

#include <sys/socket.h>

int listen(int sockfd, int blocklog);


4. 연결 요청의 수락


누군가 전화를 걸면 '따르릉' 소리와 함께 전화가 울릴 것이다. 그럼 우린 수화기를 들어 걸려 온 전화를 받을 것이다. 소켓 또한 accept()를 이용하여 걸려온 연결 요청을 받아 해당 요청을 수락한다. 요청을 수락하면 양방향 데이터 송신이 가능하게 된다. 마치 전화기에서 서로 대화를 하는 것과 같이 말이다. 다만 조금 다른 점은, accept이란 시스템 콜에 대해 서버가 소켓을 새로 생성하여 그 소켓을 이용하여 클라이언트의 송신 소켓과 새로 생성한 소켓 간에 데이터 송수신을 하게 된다. 그렇다면 원래 있던 수신 소켓은 그 동안 무엇을 하는가? 다른 장치로부터 연결 요청이 또 들어올 수 있으니까 그 요청을 기다렸다가 받는 역할을 하게 된다. accept는 성공 시 파일 디스크럽터를, 실패 시 -1을 반환한다.

#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
 

5. 전화를 거는 소켓


위에선 전화를 받는 상황을 보았는데, 이번엔 전화를 거는 상황이다. 소켓을 생성하면 connect()를 이용하여 연결 요청을 보낸다. 아래 connect에서의 매개변수 중 struct sockaddr 에서는 서버의 주소를 넣게 된다. 

#include <sys/socket.h>

int connect(int sockfd, struct sockaddr *serv_addr, socklen_t addrlen);






2. 프로토콜(protocol) 이란?


프로토콜은 '약속'을 의미하며, 컴퓨터 상호간에 데이터 송수신에 필요한 통신규약을 의미한다. 위에서 봤던 socket() 안의 매개 변수의 의미를 하나씩 보자.

domain : 소켓이 사용할 프로토콜 체계(protocol family) 정보를 전달한다.
type : 소켓의 데이터 전송방식에 대한 정보를 전달한다.
protocol : 두 컴퓨터간 통신에 사용되는 프로토콜 정보를 전달한다.

#include <sys/socket.h>

int socket(int domain, int type, int protocol);


1. 프로토콜 체계 (protocol family)


프로토콜의 종류를 의미한다. 대표적인 프로토콜 체계는 아래와 같으며, 그 중 PF_INET과 PF_INET6를 주로 사용하게 된다.

PF_INET : IPv4 인터넷 프로토콜 체계
PF_INET6 : IPv6 인터넷 프로토콜 체계
PF_LOCAL : 로컬 통신을 위한 UNIX 프로토콜 체계
PF_PACKET : Low level 소켓을 위한 프로토콜 체계
PF_IPX : IPX 노벨 프로토콜 체계


2. 소켓의 타입 (socket type)


데이터 전송 방식을 의미하며 소켓이 생성될 때 소켓의 타입도 결정되어야 한다. PF_INET (IPv4) 의 소켓 타입에는 연결 지향형 소켓 타입 (TCP)비연결 지향형 소켓 타입 (UDP) 가 대표적인다.

1) 연결 지향형 소켓 타입 (SOCK_STREAM, TCP)

- 중간에 데이터가 소멸되지 않는다.
- 전송 순서대로 데이터가 수신된다.
- 데이터의 경계가 존재하지 않는다.
- 소켓 대 소켓의 연결은 반드시 1 : 1 구조
- 전화를 생각해보면 이해하기 쉽다.

2) 비연결 지향형 소켓 타입 (SOCK_DGRAM, UDP)

- 전송 순서 상관 없이 속도가 빠른 속도의 전송을 지향한다.
- 데이터 손실 및 파손의 우려가 있다. 속도를 중시하기 때문에 전송 순서를 바꾸다가 손실될 가능성이 있다.
- 데이터의 경계가 존재한다. 데이터의 순서가 바뀌기 때문에 경계를 정하지 않으면 제대로 보낼 수가 없다.
- 한 번에 보낼 수 있는 데이터의 크기가 제한된다.
- 택배와 비슷하다.


댓글