4-2. echo 서버와 클라이언트 구현 [TCP/IP][C][LINUX]
위 그림은 Iterative 서버의 함수호출 순서, TCP 서버-클라이언트 구현을 나타낸 것입니다.
Iterative 서버란 반복문을 삽입해 accept 함수를 반복 호출해서 연결요청을 하는 모든 클라이언트에게 서비스를 제공하는 개념입니다. 클라이언트가 connect()로 연결요청을 하면 서버의 listen()에서 할당된 연결요청 큐에 추가됩니다. 서버는 들어온 연결요청을 하나하나 accept() 하고 서비스를 제공하고 close()로써 마무리하는 과정을 반복합니다. 이러면 한 번에 한 클라이언트에게만 서비스를 제공할 수 있게 되는데, 이는 프로세스와 쓰레드를 통해 다중 클라이언트에게 서비스를 제공하는 서버를 만들 수 있게 됩니다.
echo란 입력한 문자열을 그대로 출력해주는 것(명령어 등)을 뜻합니다. 프로그램의 기본 동작 방식은 다음과 같습니다.
- 서버는 한 순간에 하나의 클라이언트와 연결되어 echo 서비스 제공 (연결지향)
- 서버는 총 5개의 클라이언트에게 순차적 서비스 제공 및 종료
- 클라이언트는 프로그램 사용자로부터 문자열 데이터를 입력받아서 서버에 전송
- 서버는 전송받은 문자열 데이터를 클라이언트에게 재전송
- 서버와 클라이언트간 문자열 echo는 클라이언트가 Q를 입력할 때까지 반복
먼저, 앞서 다루지 않은 두 함수를 먼저 다루겠습니다.
#include<sys/socket.h>
int listen(int sock, int backlog);
// success: 0 fail: -1
// sock : 서버 소켓의 파일 디스크럽터 backlog : 연결요청 대기 큐의 크기
int accept(int sock, struct sockaddr * addr, socklen_t * addrlen)
// success: 파일 디스크럽터 fail: -1
// sock : 서버 소켓의 파일 디스크럽터
// addr : 연결 요청을 한 클라이언트의 주소정보를 담을 변수의 주소 값
// addrlen : addr의 변수 크기
구현된 echo 서버와 클라이언트의 코드는 각각 다음과 같습니다.
https://github.com/kjmin622/TCP-IP-Socket-Programing/blob/master/src/echo_server.c
https://github.com/kjmin622/TCP-IP-Socket-Programing/blob/master/src/echo_client.c
이 코드에는 문제점이 있는데, TCP는 데이터의 경계가 존재하지 않아, 둘 이상의 write 함수 호출로 전달된 문자열 정보가 묶여서 한번에 서버로 전송될 수 있습니다. 따라서 보낸 만큼 받는 과정이 추가로 필요합니다.
write를 할 때 입력한 문자열의 길이를 미리 받아둔 후, 받아둔 문자열의 길이 만큼 read를 받으면 됩니다.
https://github.com/kjmin622/TCP-IP-Socket-Programing/blob/master/src/echo_client2.c
다만 대부분의 상황에선 수신할 데이터의 크기를 미리 파악할 수 없습니다. 따라서 데이터의 끝을 파악할 수 있는 약속을 별도로 정의하고 약속하는데, 이런 것을 어플리케이션 프로토콜이라 합니다.