(4-1 그림)
위 그림은 서버-클라이언트의 예로 웹 서버와 웹 클라이언트가 동작하는 모습니다. HTTP은 TCP에 기반한 프로토콜이기 때문에 웹 서버-클라이언트는 대표적인 TCP 서버-클라이언트 응용 프로그램이라고 할 수 있다.
TCP 서버-클라이언트의 핵심 동작을 개념적으로 정리하면 다음과 같다. (괄호 안에는 사용할 소켓 함수 표시)
listen
)connect
)하여 데이터를 보낸다.(send
)accept
) 클라이언트가 보낸 데이터를 받아(recv
) 처리한다.send
)recv
) 처리한다.closesocket
)(4-2 그림)
이와 같은 방식은 웹 서버-클라이언트, 텔넷 서버-클라이언트, FTP 서버-클라이언트 등에도 동일하게 적용된다.
(4-3 그림)
위 그림은 TCP 서버와 TCP 클라이언트가 연결되어 통신을 수행하는 과정을 보여준다.
TCP 서버-클라이언트가 통신하는 상황을 일반적인 형태로 나타내면 아래 그림과 같다. 서버쪽 소켓과 클라이언트쪽 소켓이 일대일로 대응하는 것을 알 수 잇다. 'TCP 클라이언트 #n'처럼 클라이언트 1개가 소켓 2개 이상 사용해 서버에 접속할 수도 있다.
IPv4 기반으로 동작하는 간단한 TCP 서버-클라이언트를 작성하고 테스트해 볼 것이다.
테스트에서 사용할 서버와 클라이언트 예제의 동작을 미리 살펴보면 아래와 같다.
recv
) 이를 문자열로 간주해 무조건 화면에 출력한다.(printf
) 그리고 받은 데이터를 변경 없이 다시 클라이언트에 보낸다.(send
) 받은 데이터를 그대로 다시 보낸다는 뜻으로 에코 서버(echo erver)라고 부르겠다.fget
) 문자열을 서버에 보낸다.(send
) 서버가 받은 데이터를 그대로 돌려보내면, 클라이언트는 이를 받아서(recv
) 화면에 출력한다.(printf
) 에코 서버와 통신한다는 의미로 에코 클라이언트(echo client)라고 부르겠다.(4-5 그림)
소스 코드의 경우 해당 링크를 참조하면 된다.
해당 코드의 경우 실습의 편의를 위해 서버와 클라이언트를 같은 컴퓨터에서 실행하는 것을 가정하고 있다. 서로 다른 컴퓨터에서 실행하려면 TCPClient.cpp
파일의 6행을 서버의 IP 주소로 변경해야 한다. 서버는 포트 변호를 9000을 사용하고 있기 때문에 다른 포트 번호를 사용하려면 TCPServer.cpp
파일의 6행과 TCPClient.cpp
파일의 7행을 변경하면 된다.
테스트는 아래 순서에 따라 실행해보면 된다.
TCP 서버(TCPServer.exe
)를 실행한다. 초기에는 아무것도 출력되지 않는다.
(4-6 그림)
명령 프롬프트를 실행 후 netstat -a -n
명령을 실행한다.
netstat
: TCP/IP 네트워크 연결과 통계 정보를 표시하는 유틸리티로 윈도우 운영체제에서 기본으로 제공-a
: 모든 연결과 연결 대기 포트를 표시하는 옵션-n
: 주소와 포트 번호를 숫자 형식으로 표시하는 옵션(4-7 그림)
TCP 포트 번호 9000의 상태가 LISTENING(연결 대기 중)
임을 볼 수 있다.
TCP 클라이언트(TCPClient.exe
)를 실행한다.
(4-8 그림)
다시 netstat -a -n
명령을 실행한다. 예제 프로그램과 관련된 정보는 총 3개인데, 각각 포트 번호 9000, 9000, 49186을 사용하고 있다. 첫 번째 행은 LISTENING(연결 대기 중)
상태이고, 나머지 두 생은 ESTABLISHED(연결됨)
상태임을 알 수 있다.
(4-9 그림)
최종적으로 아래 그림과 같은 상태가 되는 것이다.
(4-10 그림)
클라이언트에서 글자를 입력하고 Enter
키를 누르면 입력 데이터가 서버에 전송된다. 클라이언트는 송신 바이트 수를 표시하고, 서버는 클라이언트의 IP 주소와 포트 번호, 클라이언트로부터 받은 데이터를 화면에 출력한다. 서버는 받은 데이터를 클라이언트에 되돌려주기 때문에, 클라이언트는 자신이 보낸 데이터를 다시 받게 된다. 클라이언트 화면에는 수신 바이트 수와 받은 데이터가 표시된다.
(4-11 그림)
글자를 입력하지 않고 그냥 Enter
키를 누르면 클라이언트는 종료하고, 서버 화면에 다음과 같이 표시된다.
(4-12 그림)
다시 netstat -a -n
명령을 실행해 네트워크 연결 상태를 확인한다. LISTENING
상태인 포트(9000)와 TIME_WAIT
상태인 포트(49186)가 존재함을 알 수 있다. 서버는 계속 실행중이므로 연결 대기 중인 포트 번호(9000)은 유지되고 있지만, 클라이언트와 통신하던 소켓을 사라졌다. 또한 클라이언트는 종료했음에도 사용하던 포트 번호(49186)가 사라지지 않고 TIME_WAIT
상태로 남아있는 것이다.
(4-13 그림)
최종적으로 아래 그림과 같은 상태가 되는 것이다.
Ctrl + c
를 눌러 강제 종료한다. 클라이언트가 강제 종료되었음을 암시하는 오류 메세지가 서버에 표시된다.netstat -a -n
명령을 실행해 네트워크 연결 상태를 확인해보자. 이번에는 LISTENING
상태인(9000)만 존재하고 TIME_WAIT
상태인 포트는 존재하지 않는다.참고 자료
김성우 저, "TCP/IP 윈도우 소켓 프로그래밍", 한빛아카데미, 2018