지직전기
TCP를 이용한 채팅서버 구축 #1 소켓 프로그래밍 이론(TCP/UDP) 본문

소켓 프로그래밍 강의를 듣고 공부 내용을 정리해봤습니다.
순서는 ①소켓프로그래밍 이론을 먼저 공부하고 ②Echo서버/Client를 구축해본다음 ③채팅서버를 만들고 최종적으로 ④IOCP모델을 기반으로 성능 개량까지 진행해볼 예정입니다.
개발 및 테스트 환경
Windows 10
Visual Studio 2022 Community
Wireshark(패킷분석도구)
PC 2대와 인터넷 공유기(유선 인터넷 연결설정)
소켓(Socket)이란?
소켓은 프로그램이 네트워크에서 데이터를 송수신할 수 있도록 네트워크 환경에 연결할 수 있게 만들어진 연결부입니다.
소켓은 OS 커널에 구현되어 있는 프로토콜 요소에 대한 추상화된 인터페이스로,
장치 파일의 일종으로 이해할 수 있으며 일반 파일에 대한 개념이 대부분 적용됩니다.
소켓 API의 종류와 실행 흐름
TCP 기반 채팅서버를 구축하는데 사용하는 소켓 API는 아래와 같습니다.
- socket() : 소켓을 생성합니다. 소켓의 종류를 스트림(TCP)타입 / 데이터그램(UDP)타입 중에 선택할 수있습니다.
- bind() : 소켓에 특정 포트번호를 사용 할 수 있도록 결합합니다. 중복된 포트를 사용할 경우 bind되지 않습니다.
- listen() : 결합이 성공적으로 끝나면 해당 포트번호로 클라이언트 연결요청이 있는지 대기합니다.
- connect() : IP주소와 Port 번호로 식별되는 대상으로 연결 요청을 보냅니다.
- accept() : 클라이언트에서 커넥트 요청이 들어와 연결이 성공하면 데이터 송수신을 위한 소켓을 생성합니다.
- send() / recv() : 연결이 성공하면 데이터를 주고받을 수 있습니다.
- shutdown() : 소켓의 송수신을 종료합니다. 자원해제는 close 되기 전까지 유효합니다.
- close() : 최종적으로 모든 연결을 닫아 종료합니다.

클라이언트 소켓(Client Socket)은 처음 소켓을 생성(socket)한 다음, 서버 측에 연결(connect)을 요청합니다. 그리고 서버 소켓에서 연결이 받아들여지면 데이터를 송수신(send/recv)하고, 모든 처리가 완료되면 소켓(Socket)을 닫습니다(close).
서버 소켓(Server Socket)은 클라이언트와 마찬가지로, 첫 번째 단계는 소켓을 생성(socket)하는 것입니다. 그리고 서버 소켓이 해야 할 두 번째 작업은, 서버가 사용할 IP 주소와 포트 번호를 생성한 소켓에 결합(bind)시키는 것입니다. 그런 다음 클라이언트로부터 연결 요청이 수신되는지 주시(listen)하고, 요청이 수신되면 요청을 받아들여(accept) 데이터 통신을 위한 소켓을 생성합니다. 일단 새로운 소켓을 통해 연결이 수립(ESTABLISHED)되면, 클라이언트와 마찬가지로 데이터를 송수신(send/recv)할 수 있습니다. 마지막으로 데이터 송수신이 완료되면, 소켓(Socket)을 닫습니다(close).
TCP 상태다이어그램
TCP 상태 다이어그램은 TCP 연결이 설정되고 종료되는 과정을 보여주는 도구로 TCP 연결의 각 상태와 그 상태 간의 전환을 나타냅니다.

- CLOSED
- 초기 상태입니다. 소켓이 열리지 않은 상태입니다.
- LISTEN
- 서버 소켓이 클라이언트의 연결 요청을 기다리는 상태입니다.
- 서버가 socket()을 호출한 후 bind() 및 listen()을 호출하면 이 상태로 들어갑니다.
- SYN_SENT
- 클라이언트가 서버에 연결 요청(SYN 패킷)을 보낸 후 응답을 기다리는 상태입니다.
- 클라이언트가 connect()를 호출하면 이 상태로 들어갑니다.
- SYN_RECEIVED
- 서버가 클라이언트의 SYN 패킷을 받고, 이를 승인하는 ACK 패킷과 함께 자신의 SYN 패킷을 클라이언트에게 보낸 상태입니다.
- 서버는 LISTEN 상태에서 클라이언트의 SYN 패킷을 받으면 이 상태로 전환됩니다.
- ESTABLISHED
- 연결이 설정된 상태로, 양쪽에서 데이터를 주고받을 수 있습니다.
- 클라이언트와 서버 모두 3-way handshake가 완료되면 이 상태로 들어갑니다.
- FIN_WAIT_1
- 연결을 종료하기 위해 FIN 패킷을 보낸 후 상대방의 ACK를 기다리는 상태입니다.
- 연결을 종료하려는 측에서 close()를 호출하면 이 상태로 들어갑니다.
- FIN_WAIT_2
- FIN 패킷을 보낸 후 상대방의 FIN 패킷을 기다리는 상태입니다.
- FIN_WAIT_1 상태에서 상대방의 ACK를 받으면 이 상태로 전환됩니다.
- CLOSE_WAIT
- 상대방으로부터 FIN 패킷을 받고 이를 승인하는 ACK 패킷을 보낸 상태입니다. 이후 연결을 완전히 종료하기 위해 close()를 호출할 때까지 기다립니다.
- 상대방이 연결을 종료하려고 할 때, ESTABLISHED 상태에서 FIN 패킷을 받으면 이 상태로 전환됩니다.
- CLOSING
- 양쪽 모두 FIN 패킷을 보낸 상태로, 서로의 FIN 패킷에 대한 ACK를 기다립니다.
- 양쪽 모두 거의 동시에 연결을 종료하려고 하면 이 상태가 됩니다.
- LAST_ACK
- FIN 패킷을 보내고 상대방의 ACK 패킷을 기다리는 상태입니다.
- CLOSE_WAIT 상태에서 close()를 호출하면 이 상태로 들어갑니다.
- TIME_WAIT
- 모든 패킷이 성공적으로 전달되고 일정 시간(TCP의 경우 2배의 최대 세그먼트 생명주기(MSL)) 동안 대기하는 상태입니다.
- FIN_WAIT_2 상태에서 FIN 패킷을 받고 ACK 패킷을 보낸 후 이 상태로 전환됩니다.
TCP 연결(3-way Handshake)
- SYN_SENT (클라이언트) -> SYN_RECEIVED (서버): 클라이언트가 SYN 패킷을 보내고 서버가 이를 받음.
- SYN_RECEIVED (서버) -> SYN_SENT (클라이언트): 서버가 SYN+ACK 패킷을 보내고 클라이언트가 이를 받음.
- ESTABLISHED: 클라이언트가 ACK 패킷을 보내고 서버가 이를 받음.
TCP 연결 종료 (4-way Handshake)
- ESTABLISHED -> FIN_WAIT_1 (클라이언트/서버): 연결 종료를 위해 FIN 패킷을 보냄.
- FIN_WAIT_1 -> FIN_WAIT_2 (클라이언트/서버): 상대방의 ACK 패킷을 받음.
- FIN_WAIT_2 -> TIME_WAIT (클라이언트/서버): 상대방의 FIN 패킷을 받고 ACK를 보냄.
- TIME_WAIT -> CLOSED: 일정 시간(MSL의 2배) 후 연결 종료
UDP 프로토콜

UDP는 앞서 말한 데이터그램 타입으로 비연결형 전송방식입니다. TCP와는 달리 데이터 수신여부를 확인하지않아 신뢰성이 낮지만(흐름, 오류제어 x) 속도가 빠르고 네트워크 부하가 적다는 장점이 있습니다. 또한 1:1(유니캐스트), 1:N(멀티캐스트), N:M(브로드캐스트) 통신 방식을 구현하기 좋습니다.

UDP는 비연결형 전송방식이기 때문에 TCP와 다르게 accept하는 과정이 없습니다.
서버와 클라이언트 양측에서 소켓을 생성하고 서버에서 소켓을 바인딩하고나면
서로 메세지를 주고 받을 수 있습니다.
데이터 신뢰성은 떨어지지만 그만큼 전송속도가 빠르기에 실시간 응답이 중요한 채팅이나
라이브 스트리밍 등에 적용할 수 있습니다.
TCP / UDP 프로토콜 차이점

'STUDY > C, C++' 카테고리의 다른 글
| [Socket] send()와 recv()는 1:1 매핑일까? feat 네이글알고리즘 (3) | 2024.09.07 |
|---|---|
| C++ 이용해 전화번호부 만들기 (0) | 2024.09.06 |
| C++을 이용해 계산기 프로그램 만들기 (0) | 2024.09.06 |