개요
TCP/IP 소켓 통신은 네트워크 프로그래밍의 기본이다. 컴퓨터 간 데이터를 안정적으로 교환하기 위한 방법으로, 인터넷과 대부분의 현대 네트워크 애플리케이션의 근간을 이룬다. 본 글에서는 C언어를 사용한 TCP/IP 소켓 프로그래밍의 기본 개념과 구현 방법을 소개한다. 클라이언트-서버 모델을 중심으로 실제 작동하는 코드 예제를 통해 소켓 통신의 핵심 원리를 설명한다.
설명
소켓 통신의 기본 개념
소켓은 네트워크 통신을 위한 엔드포인트로, IP 주소와 포트 번호의 조합으로 식별된다. TCP/IP 프로토콜에서 소켓은 두 컴퓨터 간의 양방향 통신 채널을 제공한다. 소켓 통신은 다음 주요 단계로 이루어진다:
- 소켓 생성
- 서버에서는 포트에 바인딩 및 연결 요청 대기
- 클라이언트에서는 서버에 연결 요청
- 데이터 송수신
- 연결 종료
TCP/IP 프로토콜의 특징
TCP(Transmission Control Protocol)는 다음과 같은 특성을 가진다:
- 연결 지향적: 데이터 전송 전에 연결을 설정
- 신뢰성: 패킷 손실 시 재전송 메커니즘 제공
- 순서 보장: 데이터가 전송된 순서대로 도착
- 흐름 제어: 수신자의 처리 능력에 맞춰 전송 속도 조절
- 혼잡 제어: 네트워크 상황에 따라 전송 속도 조절
이러한 특성으로 인해 TCP는 파일 전송, 웹 브라우징, 이메일 등 데이터 무결성이 중요한 애플리케이션에 적합하다.
예제코드
다음은 간단한 TCP/IP 클라이언트-서버 예제다. 서버는 클라이언트의 연결을 기다리고, 클라이언트는 서버에 연결한 후 메시지를 주고받는다.
서버 코드
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
char *hello = "서버로부터의 응답 메시지입니다.";
// 소켓 생성
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("소켓 생성 실패");
exit(EXIT_FAILURE);
}
// 소켓 옵션 설정 (포트 재사용)
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt 실패");
exit(EXIT_FAILURE);
}
// 주소 설정
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// 소켓을 주소에 바인딩
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("바인드 실패");
exit(EXIT_FAILURE);
}
// 연결 대기 상태로 전환
if (listen(server_fd, 3) < 0) {
perror("리슨 실패");
exit(EXIT_FAILURE);
}
printf("서버가 포트 %d에서 대기 중...\\n", PORT);
// 클라이언트 연결 수락
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("연결 수락 실패");
exit(EXIT_FAILURE);
}
printf("클라이언트 연결됨\\n");
// 클라이언트로부터 메시지 수신
read(new_socket, buffer, BUFFER_SIZE);
printf("클라이언트로부터 받은 메시지: %s\\n", buffer);
// 클라이언트에게 응답 메시지 전송
send(new_socket, hello, strlen(hello), 0);
printf("응답 메시지 전송 완료\\n");
// 소켓 닫기
close(new_socket);
close(server_fd);
return 0;
}
클라이언트 코드
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char *hello = "클라이언트로부터의 메시지입니다.";
char buffer[BUFFER_SIZE] = {0};
// 소켓 생성
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("소켓 생성 실패");
exit(EXIT_FAILURE);
}
// 서버 주소 설정
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// IP 주소를 바이너리 형태로 변환
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
perror("유효하지 않은 주소/주소가 지원되지 않음");
exit(EXIT_FAILURE);
}
// 서버에 연결
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("연결 실패");
exit(EXIT_FAILURE);
}
printf("서버에 연결됨\\n");
// 서버에 메시지 전송
send(sock, hello, strlen(hello), 0);
printf("메시지 전송 완료\\n");
// 서버로부터 응답 수신
read(sock, buffer, BUFFER_SIZE);
printf("서버로부터 받은 응답: %s\\n", buffer);
// 소켓 닫기
close(sock);
return 0;
}
코드 실행 방법
- 먼저 서버 코드를 컴파일하고 실행한다:
- gcc server.c -o server ./server
- 다른 터미널에서 클라이언트 코드를 컴파일하고 실행한다:
- gcc client.c -o client ./client
- 두 프로그램이 성공적으로 메시지를 주고받는 것을 확인할 수 있다.
결론
C언어를 사용한 TCP/IP 소켓 프로그래밍은 네트워크 애플리케이션 개발의 기본이다. 본 글에서는 소켓 통신의 기본 개념과 TCP 프로토콜의 특성, 그리고 간단한 클라이언트-서버 예제를 통해 소켓 프로그래밍의 핵심을 살펴보았다. 이러한 기초를 바탕으로 더 복잡한 네트워크 애플리케이션을 개발할 수 있다.
실제 프로덕션 환경에서는 오류 처리, 다중 클라이언트 지원, 비동기 입출력 등 추가적인 고려사항이 필요하다. 또한 보안 측면에서 SSL/TLS와 같은 암호화 프로토콜을 적용하는 것도 중요하다. 이러한 고급 주제들은 기본 소켓 프로그래밍 지식을 확장하여 구현할 수 있다.
참고문헌
- Stevens, W. R., Fenner, B., & Rudoff, A. M. (2003). UNIX Network Programming, Volume 1: The Sockets Networking API. Addison-Wesley Professional.
- Beej's Guide to Network Programming. Retrieved from https://beej.us/guide/bgnet/
- Donahoo, M. J., & Calvert, K. L. (2009). TCP/IP Sockets in C: Practical Guide for Programmers. Morgan Kaufmann.
- POSIX Socket API Documentation.
- Linux Manual Pages for socket(2), bind(2), listen(2), accept(2), connect(2).
'Programming > C' 카테고리의 다른 글
open() 과 fopen()의 차이점 (0) | 2025.03.20 |
---|---|
popen 함수 (0) | 2025.03.19 |
openssl을 활용한 SSL 통신 (0) | 2025.03.17 |
unistd.h의 getopt() 함수를 활용한 argument 핸들링 (0) | 2025.03.15 |
if-else 구조 vs lookup table구조 (0) | 2025.03.13 |