이론 : TCP/IP
네트워크 이론과 겹치는 내용이므로 짧게 다루고 넘어감.
c
사용자
-----------------
응용 계층 -> 응용, 표현, 세션 (html)
전송 계층 -> 전송 (tcp / udp)
인터넷 계층 -> 네트워크 (ip)
네트워크 접속 계층 -> 데이터 링크, 물리 (link)
-----------------
전선
서버 구현하기
서버는 다음과 같은 과정을 거친다.
mermaid
flowchart LR
socket["socket()"] -->
bind["bind()"] -->
listen["listen()"] -->
accept["accept()"] -->
data["read() / write()"] -->
close["close()"]
- socket()과 bind()에 관한 부분은 앞에서 이미 설명
- listen(), accept() 에 관해 설명.
listen()
c
#include <sys/socket.h>
int listen(int sock, int backlog);
sock: 소켓 = 파일 디스크립터backlog: “연결요청 대기 큐”의 크기.
연결요청 대기 큐
- 클라이언트의 연결요청도 인터넷을 통해서 흘러 들어오는 일종의 데이터 전송
- 이를 받아드리려면 당연히 소켓이 하나 있어야 함.
- 소켓에서 연결요청이 여러 개가 있을 때를 대비한 큐가 바로 연결요청 대기 큐.
accept()
c
#include <sys/socket.h>
int accept(int sock, struct sockaddr *addr, socklen_t addrlen);
sock: 소켓addr: 클라이언트의 주소정보를 담을 변수의 주소값addrlen:addr에 전달된 주소의 변수 크기
클라이언트 구현하기
mermaid
flowchart LR
socket["socket()"] -->
connect["connect()"] -->
data["read() / write()"] -->
close["close()"]
- “소켓의 생성”과 “연결의 요청”이 전부
- 연결의 요청 =
connect()
connect()
c
#include <sys/socket.h>
int accept(int sock, struct sockaddr *servaddr, socklen_t addrlen);
- sock : 소켓 = 파일 디스크립터
- servaddr : 연결요청할 서버의 주소정보를 담은 변수의 주소값
- addrlen :
servaddr에 전달된 주소의 변수 크기
서버와 클라이언트의 관계
mermaid
sequenceDiagram
client->>server: 연결요청
server-->>client: "데이터 송수신"
server-->>client: "데이터 송수신"
client-)server: "close()"
server-)client: "close()"
- 사전준비 : server의 listen()
- 연결요청 : client의 connect() → server의 accept()
- 데이터 송수신 : read() / write()
- 연결종료 : close()
iterative 기반 서버
mermaid
flowchart
socket["socket()"] -->
bind["bind()"] -->
listen["listen()"] -->
accept["accept()"] -->
data["read() / write()"] -->
close1["close() - client"] -->
close2["close() - server"]
close1 --> accept
- 여러 클라이언트의 요청을 받을 수 있도록 바꾼 모습
- 그래서 iterative 서버라는 것.
- 계속 요청을 넣으면 계속 수락.
- 반복문을 삽입해서 accept 함수를 반복.
문제점
echo_client.c 와 echo_server.c 에 적힌 입/출력에 관한 부분이다.
c
/* echo_client.c */
// write()를 통해 sock에 message를 쓴다. (echo 서버로 보내짐.)
write(sock, message, strlen(message));
----------------------------------------------------------------
/* echo_server.c */
// read()를 통해 message를 읽고, write()를 통해 message를 작성
// 메시지가 안 오면(=클라이언트가 q를 누르면) str_len == 0으로 인식하고 종료
while ((str_len = read(clnt_sock, message, BUF_SIZE)) != 0)
write(clnt_sock, message, str_len);
---------------------------------------------------------------
/* echo_client.c */
// read()를 통해 서버가 적은 sock의 문자열을 읽는다.
str_len = read(sock, message, BUF_SIZE - 1);
// 해당 문자열 출력
message[str_len] = 0;
printf("message from server : %s", message);
문제가 생길 수 있는 상황은 다음과 같다.
client의 write()에 적힌 문자열이 아주 길다고 가정해보자server에서 문자열의 일부만read().server에서 그 부분만 다시write()해서 보냄.client의 read()는 처음 보낸 문자열의 일부만 출력.