네트워크 프로그래밍에서 가장 기본적인 개념 중 하나는 TCP 소켓 프로그래밍입니다. TCP는 신뢰성 있는 데이터 전송을 보장하는 연결 지향 프로토콜로, 서버와 클라이언트 간의 안정적인 통신을 가능하게 합니다. 이번 글에서는 TCP 소켓의 개념을 설명하고, C 언어를 이용해 간단한 서버와 클라이언트를 구현하는 방법을 소개합니다.
1. TCP 소켓이란?
TCP(Transmission Control Protocol)는 데이터의 순서와 무결성을 보장하는 프로토콜입니다. 이를 구현하기 위해 운영체제는 소켓(Socket)이라는 개념을 제공합니다. 소켓은 네트워크를 통해 데이터를 송수신하는 엔드포인트 역할을 하며, 두 개의 소켓이 연결되면 데이터를 주고받을 수 있습니다.
2. TCP 서버와 클라이언트의 동작 방식
TCP 기반 통신에서는 한쪽이 서버(Server), 다른 한쪽이 클라이언트(Client) 역할을 합니다. 각각의 동작 과정은 다음과 같습니다.
서버의 동작 과정
- 소켓 생성: 통신을 위한 소켓을 생성합니다.
- 주소 바인딩: 특정 IP와 포트에 소켓을 연결합니다.
- 연결 대기: 클라이언트의 연결 요청을 기다립니다.
- 클라이언트 연결 수락: 클라이언트의 요청을 수락합니다.
- 데이터 송수신: 클라이언트와 데이터를 주고받습니다.
- 연결 종료: 통신이 끝나면 소켓을 닫습니다.
클라이언트의 동작 과정
- 소켓 생성: 서버와 통신할 소켓을 생성합니다.
- 서버에 연결 요청: 서버의 IP와 포트로 연결을 시도합니다.
- 데이터 송수신: 서버와 데이터를 주고받습니다.
- 연결 종료: 통신이 끝나면 소켓을 닫습니다.
3. TCP 서버 구현
서버는 클라이언트의 요청을 받아 메시지를 수신한 후, 응답을 보내는 역할을 합니다.
3.1 코드 분석
아래 서버 코드에서 사용된 주요 API와 기능을 설명하겠습니다.
- socket(): 새로운 소켓을 생성합니다.
- bind(): 소켓을 특정 IP와 포트에 바인딩합니다.
- listen(): 클라이언트의 연결 요청을 대기합니다.
- accept(): 클라이언트의 연결을 수락하고 새로운 소켓을 생성합니다.
- recv(): 클라이언트로부터 데이터를 수신합니다.
- send(): 클라이언트에게 데이터를 전송합니다.
- close(): 소켓을 닫고 연결을 종료합니다.
3.2 서버 코드
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
char buffer[BUFFER_SIZE];
// 1. 소켓 생성
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
// 2. 주소 설정 및 바인딩
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 3. 클라이언트 연결 대기
if (listen(server_fd, 5) < 0) {
perror("Listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}
printf("Server listening on port %d\n", PORT);
// 4. 클라이언트 연결 수락
client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd < 0) {
perror("Accept failed");
close(server_fd);
exit(EXIT_FAILURE);
}
// 5. 데이터 수신 및 응답
recv(client_fd, buffer, BUFFER_SIZE, 0);
printf("Received: %s\n", buffer);
send(client_fd, "Hello, Client!", 14, 0);
// 6. 소켓 종료
close(client_fd);
close(server_fd);
return 0;
}
4. TCP 클라이언트 구현
클라이언트는 서버에 접속하고 메시지를 보내는 역할을 합니다.
4.1 코드 분석
- socket(): 새로운 소켓을 생성합니다.
- connect(): 서버의 IP와 포트로 연결을 시도합니다.
- send(): 서버로 데이터를 전송합니다.
- recv(): 서버로부터 데이터를 수신합니다.
- close(): 소켓을 닫고 연결을 종료합니다.
4.2 클라이언트 코드
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int sock;
struct sockaddr_in server_addr;
char buffer[BUFFER_SIZE] = "Hello, Server!";
// 1. 소켓 생성
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
// 2. 서버 주소 설정
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);
// 3. 서버에 연결 요청
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Connection failed");
close(sock);
exit(EXIT_FAILURE);
}
// 4. 데이터 송신 및 수신
send(sock, buffer, strlen(buffer), 0);
recv(sock, buffer, BUFFER_SIZE, 0);
printf("Received: %s\n", buffer);
// 5. 소켓 종료
close(sock);
return 0;
}
5. 실행 결과
위의 서버와 클라이언트를 실행하면 다음과 같은 결과를 확인할 수 있습니다.
- 서버 측 출력:
Server listening on port 8080
Received: Hello, Server!
- 클라이언트 측 출력:
Received: Hello, Client!
6. 정리
이 글에서는 TCP 소켓 프로그래밍의 기본 개념과 C 언어로 서버와 클라이언트를 구현하는 방법을 살펴보았습니다. TCP를 이용하면 신뢰성 있는 데이터 전송이 가능하며, 다양한 네트워크 애플리케이션의 기반이 됩니다. 이를 바탕으로 더 확장된 네트워크 프로그래밍을 수행할 수 있습니다.
'리눅스 시스템 및 네트워크 프로그래밍 > 네트워크 프로그래밍' 카테고리의 다른 글
논블로킹 소켓과 멀티플렉싱: select, poll, epoll 비교 (0) | 2025.02.02 |
---|---|
UDP 소켓 프로그래밍 기초: 서버와 클라이언트 구현하기 (0) | 2025.02.02 |
댓글