Goomba

[통신] 소켓 통신 개념 및 TCP/IP

  • etc

개념

소켓이란 단어의 뜻 그래도 본다면 어떠한 것을 연결하는 하나의 '창구'라고 생각합니다.
여기서는 데이터가 오가는 하나의 '창구' 개념이 되겠네요.

그리고 소켓은 기본적으로 '하나'의 창구이기 떄문에 두 곳에서 데이터를 송수신하려면
개별적으로 소켓을 하나씩 가지고 있어야 합니다.

데이터를 요청하는 클라이언트(Client)와 응답하는 서버(Server)의 형태로 나눌 수 있는데,
대부분 비슷하다고 합니다.

클라이언트

socket()  →  connect()  →  send()/recv()

서버

socket()  →  bind()  →  listen()  →  accept()  →  recv()/send()

각 단계별 역할

각 단계의 역할을 간단하게 정리했습니다.

FunctionDescription
socket()소켓 생성 (통신용 File Descriptor)
bind()IP 주소와 포트를 소켓에 연결
listen()클라이언트 연결 요청 대기 상태로 설정
accept()연결 요청을 받아들이고 새로운 소켓 생성
connect()클라이언트가 서버에 연결 요청
send()/recv()양방향 데이터 송수신

TCP/IP 통신

TCP/IP 통신이란, 네트워크에서 데이터를 주고받기 위한 “표준 통신 프로토콜”입니다.
TCP/IP는 하나의 프로토콜이 아닌 두 개의 TCP 그리고 IP 프로토콜을 합쳐서 부르는 용어라고 합니다.

TCP

Transmission Control Protocol의 약자로 IP 위에서 동작하는 프로토콜로
데이터 전송을 보증하며 보낸 순서대로 받을 수 있습니다. 즉, 데이터를 신뢰성 있게 주고받는 역할을 합니다.

IP

Internet Protocol의 약자로 데이터를 어디로 보낼 지 주소를 지정하는 역할을 합니다.
다만 패킷 전달 여부를 보증할 수 없고 순서도 다를 수 있다고 합니다.

계층 구조

TCP/IP는 총 4 개의 계층 구조를 가지고 있습니다.

LayerDescriptionExample
응용 계층 (Application)실제 서비스HTTP, FTP 등
전송 계층 (Transport)패킷을 조립하고 전송TCP, UDP
인터넷 계층 (Internet)패킷의 경로 설정 및 전송IP
네트워크 인터페이스 계층 (Network Interface)물리적 네트워크로 실제 하드웨어 전송이더넷, wifi

응용 계층을 시작으로 네트워크 인터페이스 계층을 통해 전송하며
수신할 때는 반대로 네트워크 인터페이스 계층을 시작으로 응용 계층으로 도달한다고 합니다.

예제

간단하게 C로 작성된 TCP/IP 소켓 통신 예제입니다.

클라이언트

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8000
#define BUFFER_SIZE 1024

int main(void)
{
    int socket_fd;
    struct sockaddr_in server_addr;
    char buffer[BUFFER_SIZE];

    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_fd < 0)
    {
        perror("socket");
        exit(1);
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    if (connect(socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        perror("connect");
        close(socket_fd);
        exit(1);
    }
    printf("[Client] Connected to server.\n");

    const char *msg = "Hello from Client!";
    send(socket_fd, msg, strlen(msg), 0);
    printf("[Client] Sent: %s\n", msg);

    memset(buffer, 0, BUFFER_SIZE);
    recv(socket_fd, buffer, BUFFER_SIZE, 0);
    printf("[Client] Received: %s\n", buffer);

    close(socket_fd);
    printf("[Client] Connection closed.\n");

    return 0;
}

서버

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8000
#define BUFFER_SIZE 1024

int main(void)
{
    int listen_fd, connection_fd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len = sizeof(client_addr);
    char buffer[BUFFER_SIZE];

    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd < 0)
    {
        perror("socket");
        exit(1);
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 모든 IP에서 접속 허용
    server_addr.sin_port = htons(PORT);

    if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        perror("bind");
        close(listen_fd);
        exit(1);
    }

    if (listen(listen_fd, 5) < 0)
    {
        perror("listen");
        close(listen_fd);
        exit(1);
    }

    printf("[Server] Listening on port %d...\n", PORT);

    connection_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_len);
    if (connection_fd < 0)
    {
        perror("accept");
        close(listen_fd);
        exit(1);
    }

    printf("[Server] Client connected! FD=%d\n", connection_fd);

    memset(buffer, 0, BUFFER_SIZE);
    recv(connection_fd, buffer, BUFFER_SIZE, 0);
    printf("[Server] Received: %s\n", buffer);

    const char *msg = "Hello from Server!";
    send(connection_fd, msg, strlen(msg), 0);
    printf("[Server] Sent: %s\n", msg);

    close(connection_fd);
    close(listen_fd);
    printf("[Server] Connection closed.\n");

    return 0;
}