SMALL
포스팅 계기
금주에 오랜만에 장기 휴가를 쓰게 되었다. 이에 따라 하루에 2개씩 글을 쓰는 게 내 목표다. 그중에 네트워크 프로그래밍에 관한 글을 쓰고 싶어 해당 포스팅을 하게 되었다. 네트워크 프로그래밍은 현대 소프트웨어 개발에서 필수적인 요소다. 특히, 서버와 클라이언트 간의 데이터 통신을 다루는 TCP와 UDP는 다양한 애플리케이션에서 사용되고 있다. 이번 글에서는 C++을 활용하여 TCP 및 UDP 소켓을 구현하는 방법을 소개하고, 이를 통해 네트워크 프로그래밍의 기초를 이해하는 데 도움이 될 수 있는 내용을 작성해 보겠다.
개념 설명
TCP
- TCP(Transmission Control Protocol)
- 연결 지향 프로토콜로, 신뢰성 있는 데이터 전송을 보장
- 패킷이 순서대로 도착하며, 손실된 패킷은 재전송
- HTTP, FTP, SMTP 등과 같은 애플리케이션에서 주로 사용
- TCP 연결 과정
- 클라이언트 → SYN → 서버
- 서버 → SYN-ACK → 클라이언트
- 클라이언트 → ACK → 서버
- 연결 완료
- TCP 해제 과정
- 클라이언트 → FIN → 서버
- 서버 → ACK → 클라이언트
- 서버 → FIN → 클라이언트
- 클라이언트 → ACK → 서버
- 연결 종료
UDP
- UDP(User Datagram Protocol)
- 비연결형 프로토콜로, 신뢰성보다는 속도를 중시
- 패킷이 순서대로 도착하지 않을 수 있으며, 손실된 패킷에 대한 보장이 없음
- 실시간 스트리밍, 온라인 게임, VoIP 등에 많이 활용
- UDP 패킷 전송 과정
- 클라이언트 → 데이터 패킷 → 서버
- 서버가 수신 후 처리 (패킷 손실 가능)
예제 코드
TCP 코드
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#define PORT 8080
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[1024] = {0};
server_fd = socket(AF_INET, SOCK_STREAM, 0); // SOCKET 생성
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); // SOCKET Option
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY; // 할당할 주소
address.sin_port = htons(PORT); // 포트 번호 지정
bind(server_fd, (struct sockaddr*)&address, sizeof(address)); // 바인드
listen(server_fd, 3); // 연결 대기
new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen); // SOCKET 연결
read(new_socket, buffer, 1024);
std::cout << "Received: " << buffer << std::endl;
close(new_socket);
close(server_fd);
return 0;
}
UDP 코드
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#define PORT 8080
int main() {
int sockfd;
struct sockaddr_in server_addr, client_addr;
char buffer[1024];
socklen_t len = sizeof(client_addr);
sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 소켓 생성
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // 주소
server_addr.sin_port = htons(PORT); // 포트
bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 바인드
recvfrom(sockfd, buffer, 1024, 0, (struct sockaddr*)&client_addr, &len); // 수신
std::cout << "Received: " << buffer << std::endl; // 수신 받은 버퍼
close(sockfd); // 연결 종료
return 0;
}
소켓 옵션 개념 및 종류
소켓을 생성할 때 다양한 옵션을 설정할 수 있으며, 이는 네트워크 성능 및 동작 방식에 영향을 줍니다. 주요 옵션은 아래를 참고 부탁한다.
- SO_REUSEADDR: 이미 사용 중이던 포트를 재사용할 수 있도록 설정 (TCP 서버에서 주로 사용)
- SO_RCVBUF / SO_SNDBUF: 수신 및 송신 버퍼 크기 설정
- SO_LINGER: 연결 종료 시 대기 여부 및 시간을 설정
- SO_KEEPALIVE: 일정 시간 동안 응답이 없을 경우 연결을 유지할지 여부
- TCP_NODELAY: Nagle 알고리즘을 비활성화하여 작은 패킷도 즉시 전송
실무에서 겪을 수 있는 현상
- 포트 충돌: 동일한 포트에서 여러 프로그램이 실행되면 바인딩 오류 발생 가능
- 패킷 손실 (UDP): 데이터가 손실될 가능성이 있으며, 이를 보완하는 로직 필요
- 연결 종료 처리 (TCP): 클라이언트가 갑자기 종료될 경우 예외 처리가 필요함
- 방화벽 및 네트워크 설정 문제: 특정 네트워크 환경에서 소켓 통신이 차단될 수 있음
글을 끝 마치면서
소켓 프로그래밍은 네트워크 기반 애플리케이션을 개발하는 데 있어서 중요한 기술 중 하나다. 웹 서버, 게임 서버, 스트리밍 서비스 등 다양한 분야에서 사용되며, 안정적이고 효율적인 네트워크 통신을 구현하는 데 필수적인 개념이다.
이번 글을 통해 TCP와 UDP의 차이점과 C++에서의 기본적인 구현 방법을 이해하는 데 도움이 되었기를 바라며, 앞으로 더 깊이 있는 내용과 실전에서의 응용 방법도 다룰 예정이니 많은 관심 부탁한다.
'C++ 개발이야기 > Socket 통신' 카테고리의 다른 글
고성능 네트워크 프로그래밍 기법 – 실무에서 반드시 알아야 할 최적화 기술 (0) | 2025.02.28 |
---|---|
보안과 암호화된 소켓 통신 - C++ (0) | 2025.02.27 |
비동기 소켓 프로그래밍 - C++ (0) | 2025.02.26 |
멀티스레드 기반 소켓 프로그래밍 : 서버 성능 극대화를 위한 필수 개념 - c++ (0) | 2025.02.26 |
TCP CLOSE_WAIT, FIN_WAIT_2 에러 - C++ (2) | 2024.10.19 |