Programming/C

C언어로 하는 TCP/IP 소켓 통신

moxie2ks 2025. 3. 18. 18:01
728x90
반응형

개요

TCP/IP 소켓 통신은 네트워크 프로그래밍의 기본이다. 컴퓨터 간 데이터를 안정적으로 교환하기 위한 방법으로, 인터넷과 대부분의 현대 네트워크 애플리케이션의 근간을 이룬다. 본 글에서는 C언어를 사용한 TCP/IP 소켓 프로그래밍의 기본 개념과 구현 방법을 소개한다. 클라이언트-서버 모델을 중심으로 실제 작동하는 코드 예제를 통해 소켓 통신의 핵심 원리를 설명한다.

설명

소켓 통신의 기본 개념

소켓은 네트워크 통신을 위한 엔드포인트로, IP 주소와 포트 번호의 조합으로 식별된다. TCP/IP 프로토콜에서 소켓은 두 컴퓨터 간의 양방향 통신 채널을 제공한다. 소켓 통신은 다음 주요 단계로 이루어진다:

  1. 소켓 생성
  2. 서버에서는 포트에 바인딩 및 연결 요청 대기
  3. 클라이언트에서는 서버에 연결 요청
  4. 데이터 송수신
  5. 연결 종료

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;
}

코드 실행 방법

  1. 먼저 서버 코드를 컴파일하고 실행한다:
  2. gcc server.c -o server ./server
  3. 다른 터미널에서 클라이언트 코드를 컴파일하고 실행한다:
  4. gcc client.c -o client ./client
  5. 두 프로그램이 성공적으로 메시지를 주고받는 것을 확인할 수 있다.

결론

C언어를 사용한 TCP/IP 소켓 프로그래밍은 네트워크 애플리케이션 개발의 기본이다. 본 글에서는 소켓 통신의 기본 개념과 TCP 프로토콜의 특성, 그리고 간단한 클라이언트-서버 예제를 통해 소켓 프로그래밍의 핵심을 살펴보았다. 이러한 기초를 바탕으로 더 복잡한 네트워크 애플리케이션을 개발할 수 있다.

실제 프로덕션 환경에서는 오류 처리, 다중 클라이언트 지원, 비동기 입출력 등 추가적인 고려사항이 필요하다. 또한 보안 측면에서 SSL/TLS와 같은 암호화 프로토콜을 적용하는 것도 중요하다. 이러한 고급 주제들은 기본 소켓 프로그래밍 지식을 확장하여 구현할 수 있다.

참고문헌

  1. Stevens, W. R., Fenner, B., & Rudoff, A. M. (2003). UNIX Network Programming, Volume 1: The Sockets Networking API. Addison-Wesley Professional.
  2. Beej's Guide to Network Programming. Retrieved from https://beej.us/guide/bgnet/
  3. Donahoo, M. J., & Calvert, K. L. (2009). TCP/IP Sockets in C: Practical Guide for Programmers. Morgan Kaufmann.
  4. POSIX Socket API Documentation.
  5. Linux Manual Pages for socket(2), bind(2), listen(2), accept(2), connect(2).
728x90
반응형

'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