1. 소켓 API 개념
네트워크 프로그래밍은 컴퓨터 네트워크를 통해 데이터를 주고받는 프로그램을 작성하는 과정입니다. 소켓 API는 네트워크 상에서 데이터를 송수신하기 위해 사용하는 인터페이스로, 운영 체제와 프로그래밍 언어에서 제공하는 중요한 기능 중 하나입니다. 소켓은 네트워크 통신의 끝점을 의미하며, IP 주소와 포트 번호를 바탕으로 네트워크 상에서 특정 프로세스 간의 통신 통로를 만들어 줍니다. 이를 통해 클라이언트와 서버가 서로 데이터를 주고받을 수 있습니다.
① 소켓 개념
소켓은 네트워크 프로그래밍의 기본 단위로, 프로세스 간 통신을 가능하게 하는 논리적인 통신 채널입니다. 소켓은 크게 TCP와 UDP 두 가지 유형이 있습니다. TCP 소켓은 연결 지향적 통신을 제공하여 데이터의 신뢰성과 순서 보장을 중요시하는 서비스에 적합합니다. 반면 UDP 소켓은 비연결형 통신을 제공하며, 빠른 속도와 낮은 오버헤드가 필요한 환경에 적합합니다. 각각의 소켓은 IP 주소와 포트 번호를 조합하여 네트워크 상의 통신 상대를 식별합니다.
② 소켓 프로그래밍 기본
소켓 프로그래밍은 크게 소켓 생성, 주소 지정, 연결 수립, 데이터 송수신, 그리고 연결 종료의 단계로 진행됩니다. TCP 소켓은 연결을 수립하고 데이터 송수신을 진행하며, UDP 소켓은 연결 없이 데이터를 주고받을 수 있습니다. TCP 소켓의 기본 동작 단계는 다음과 같습니다:
- 소켓 생성:
socket() - 서버 주소 지정:
bind() - 연결 대기:
listen() - 클라이언트 연결 수락:
accept() - 데이터 송수신:
send(),recv() - 연결 종료:
close()
이 과정은 서버 측에서 주로 수행되며, 클라이언트는 socket()과 connect() 함수를 이용해 서버에 연결을 요청합니다. 소켓 프로그래밍을 통해 구현한 네트워크 애플리케이션은 채팅, 파일 전송, 웹 서비스 등 다양한 분야에 활용됩니다.
2. TCP/UDP 통신 구현
TCP와 UDP는 인터넷 프로토콜 스위트(IP Suite) 내에서 가장 기본적인 전송 계층 프로토콜입니다. TCP는 연결 지향적이며 신뢰성 있는 데이터 전송을 보장하기 위해 순서 제어, 오류 검사, 재전송 등의 기능을 제공합니다. UDP는 비연결형 프로토콜로, 헤더가 간단하고 오버헤드가 적으며 실시간 전송이 필요한 애플리케이션에 적합합니다. 각각의 특성 때문에 TCP는 웹 브라우징, 이메일 등 신뢰성이 중요한 서비스에 사용되고, UDP는 스트리밍, 온라인 게임 등 속도가 중요한 분야에 많이 사용됩니다.
① TCP 소켓 통신
TCP 통신은 연결을 먼저 수립하는 3-way handshake 과정을 거치며, 데이터는 순서대로 안정적으로 전송됩니다. 이 때문에 TCP는 데이터 손실에 강하며, 전송 중 문제가 발생할 경우 재전송 메커니즘을 통해 복구합니다. TCP 서버는 클라이언트로부터 연결 요청을 받고, 데이터 송수신 후 연결을 종료하는 구조입니다.
TCP 서버 예제
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
char *message = "Hello, Client!";
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == 0) {
perror("소켓 생성 실패");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8080);
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("바인딩 실패");
exit(EXIT_FAILURE);
}
if (listen(server_fd, 3) < 0) {
perror("대기 실패");
exit(EXIT_FAILURE);
}
new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
if (new_socket < 0) {
perror("연결 실패");
exit(EXIT_FAILURE);
}
send(new_socket, message, strlen(message), 0);
printf("메시지 전송 완료\n");
close(new_socket);
close(server_fd);
return 0;
}
TCP 클라이언트 예제
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main() {
int sock = 0;
struct sockaddr_in server_address;
char buffer[1024] = {0};
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("소켓 생성 실패");
exit(EXIT_FAILURE);
}
server_address.sin_family = AF_INET;
server_address.sin_port = htons(8080);
server_address.sin_addr.s_addr = INADDR_ANY;
if (connect(sock, (struct sockaddr *)&server_address, sizeof(server_address)) < 0) {
perror("서버 연결 실패");
exit(EXIT_FAILURE);
}
read(sock, buffer, 1024);
printf("서버 메시지: %s\n", buffer);
close(sock);
return 0;
}
② UDP 소켓 통신
UDP는 비연결형 프로토콜로, 데이터를 목적지에 바로 전송하며 별도의 연결 수립 과정이 없습니다. 따라서 속도가 빠르고 오버헤드가 적으나, 데이터가 손실되거나 순서가 바뀔 수 있습니다. 실시간 데이터 전송, 스트리밍 서비스, 온라인 게임 등에서 주로 사용됩니다.
UDP 서버 예제
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main() {
int sockfd;
struct sockaddr_in server_addr, client_addr;
char buffer[1024];
socklen_t addr_len = sizeof(client_addr);
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("소켓 생성 실패");
exit(EXIT_FAILURE);
}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8080);
if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("바인딩 실패");
exit(EXIT_FAILURE);
}
recvfrom(sockfd, (char *)buffer, 1024, MSG_WAITALL, (struct sockaddr *) &client_addr, &addr_len);
printf("클라이언트로부터 받은 메시지: %s\n", buffer);
close(sockfd);
return 0;
}
UDP 클라이언트 예제
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main() {
int sockfd;
struct sockaddr_in server_addr;
char *message = "Hello, Server!";
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("소켓 생성 실패");
exit(EXIT_FAILURE);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
server_addr.sin_addr.s_addr = INADDR_ANY;
sendto(sockfd, (const char *)message, strlen(message), MSG_CONFIRM, (const struct sockaddr *) &server_addr, sizeof(server_addr));
printf("서버에 메시지 전송 완료\n");
close(sockfd);
return 0;
}
'Programming' 카테고리의 다른 글
| JavaScript 소개 및 역사 (48) | 2025.08.28 |
|---|---|
| C 자료구조와 알고리즘 구현 (51) | 2025.08.27 |
| C 저수준 입출력 (Low-Level I/O) (51) | 2025.08.25 |
| C 시스템 콜과 POSIX API (46) | 2025.08.24 |
| C 컴파일 과정 (42) | 2025.08.23 |