TCP/IP 프로토콜 스택은 4계층으로 이루어진다.
APPLICATION 계층 : 데이터 전송 경로 확인 과정이나, 데이터 수신의 응답 등 프로그램 성격에 따른 클라이언트와 서버 간의 데이터 송수신 규칙.
TCP / UDP 계층 : 호스트 간 데이터 송수신 방식의 규약
IP 계층 : 목적지로 데이터를 전송하기 위해 어떤 경로를 거칠것인지에 대한 규약.
LINK 계층 : 데이터의 물리적 연결에 대한 규약.
socket()
->bind()
->listen()
->accept()
->read() / write()
->close()
소켓생성, 소켓 주소할당, 연결요청 대기상태, 연결 허용, 데이터 송수신, 연결종료의 flow
bind()
로 소켓에 주소까지 할당했다면, listen()
을 통해 연결요청 대기상태에 들어간다.
항상 listen()
호출된 서버에 connect()
를 진행해야 오류가 발생하지 않는다.
#include <sys/socket.h>
int listen(int sock, int backlog);
// 성공 시 0, 실패 시 -1
sock
: 서버 소켓. 연결 요청 대기상태에 두고자 하는 소켓이 온다.backlog
: 연결요청 대기 큐 크기정보. 클라이언트 연결요청을 최대 몇개까지 대기시킬것인지를 의미.#include <sys/socket.h>
int accept(int sock, struct sockaddr * addr, socklen_t * addrlen);
// 성공 시 생성된 소켓의 파일 디스크립터, 실패 시 -1
sock
: 서버 소켓 FDaddr
: 연결요청한 클라이언트 주소정보를 담을 변수의 주소 값 전달.addrlen
: addr
에 전달된 주소의 변수 크기를 바이트 단위로 전달.accept()
는 연결 요청 대기 큐에 존재하는 클라이언트 연결 요청을 수락하고, 데이터 입출력에 사용할 소켓을 생성하여 그 소켓의 FD 값을 반환.
연결 요청한 클라이언트 소켓에 연결까지 이뤄진다.
socket()
->connect()
->read()/write()
->close()
소켓생성, 연결요청, 데이터 송수신, 연결종료로 구성
클라이언트의 연결 요청 : connect()
를 이용
#include <sys/socket.h>
int connect(int sock, struct sockaddr * servaddr, socklen_t addrlen);
// 성공시 0, 실패 시 -1
sock
: 클라이언트 소켓 FDservaddr
: 연결요청할 서버의 주소정보를 담은 변수 주소값 전달addrlen
: 두 번째 매개변수에 전달된 주소의 변수 크기를 바이트 단위로 전달connect()
는
socket()
->bind()
->listen()
->accept()
->read() / write()
->close(client) -> close(server)
소켓생성, 소켓 주소할당, 연결요청 대기상태, 연결 허용, 데이터 송수신, 연결종료의 flow
앞서 작성한 기본 서버의 흐름이다.
여기서 클라이언트와 연결된(accept()
에 의해 생성된) 소켓을 종료하고 나서 다시 accept()
를 진행하여 반복이 가능한 서버를 구현할 수 있다.
당연히 싱글스레드이니, 한 번에 한 클라이언트와만 소통이 가능하다. 이를 멀티스레드를 통해 다수의 클라이언트에 서비스 제공하는 것이 가능하다.
이와같이 반복문을 통해 서버와 클라이언트 간의 반복적인 데이터 송수신을 구현할 수 있다.
이때, close 함수를 통해 함수가 종료되면 상대 소켓으로는 EOF가 보내진다. (연결의 끝 의미)
"read, write 함수가 호출될 때마다 문자열 단위로 실제 입출력이 이뤄진다" 는 생각을 주의할 것.
-> TCP는 데이터의 경계가 없다. 1write -> 1 read 로 모든 데이터가 알맞게 송수신되지 못할 수 있음에 주의.