[통신] 소켓 통신 개념 및 TCP/IP
개념
소켓이란 단어의 뜻 그래도 본다면 어떠한 것을 연결하는 하나의 '창구'라고 생각합니다.
여기서는 데이터가 오가는 하나의 '창구' 개념이 되겠네요.
그리고 소켓은 기본적으로 '하나'의 창구이기 떄문에 두 곳에서 데이터를 송수신하려면
개별적으로 소켓을 하나씩 가지고 있어야 합니다.
데이터를 요청하는 클라이언트(Client)와 응답하는 서버(Server)의 형태로 나눌 수 있는데,
대부분 비슷하다고 합니다.
클라이언트
socket() → connect() → send()/recv()
서버
socket() → bind() → listen() → accept() → recv()/send()
각 단계별 역할
각 단계의 역할을 간단하게 정리했습니다.
| Function | Description |
|---|---|
| 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 개의 계층 구조를 가지고 있습니다.
| Layer | Description | Example |
|---|---|---|
| 응용 계층 (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;
}