⚙️ 실습 환경
- Windows 11
- Visual Studio 2022 + C++
- Wireshark
✅ TCP
TCP(Transmission Control Protocol)는 컴퓨터 네트워크에서 데이터를 안정적으로 전송하기 위한 프로토콜입니다. 안정적으로, 순서대로, 에러없이 데이터를 교환할 수 있습니다.
TCP는 3way-hankshake 연결 과정이 필요합니다. C++ 언어를 통해서 TCP 연결이 어떻게 이루어지는지 소켓 프로그램을 직접 짜보면서 그 과정을 살펴보았습니다. 두 개의 Visual Studio 프로그램을 통해서 각각의 프로그램이 루프백 주소를 통해서 연결하는 과정을 살펴보았습니다.
- TCP Server
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") #define MAX_BUFFER_SIZE 1024 int main() { WSADATA wsaData; SOCKET serverSocket, clientSocket; struct sockaddr_in serverAddr, clientAddr; char buffer[MAX_BUFFER_SIZE]; int clientAddrSize; // Winsock 초기화 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("Failed to initialize winsock.\n"); return -1; } // 소켓 생성 if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { printf("Failed to create socket.\n"); return -1; } // 서버 설정 serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); serverAddr.sin_port = htons(7777); // 소켓 바인딩 if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { printf("Failed to bind socket.\n"); return -1; } // 클라이언트의 연결 대기 if (listen(serverSocket, 1) == SOCKET_ERROR) { printf("Failed to listen on socket.\n"); return -1; } printf("서버가 실행 중입니다...\n"); // 클라이언트 연결 수락 clientAddrSize = sizeof(clientAddr); if ((clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrSize)) == INVALID_SOCKET) { printf("Failed to accept client connection.\n"); return -1; } printf("클라이언트가 연결되었습니다.\n"); while (1) { // 클라이언트로부터 메시지 수신 memset(buffer, 0, MAX_BUFFER_SIZE); int bytesRead = recv(clientSocket, buffer, MAX_BUFFER_SIZE, 0); if (bytesRead == SOCKET_ERROR || bytesRead == 0) { break; } printf("수신한 메시지: %s\n", buffer); // 클라이언트에게 응답 전송 char response[MAX_BUFFER_SIZE]; snprintf(response, MAX_BUFFER_SIZE, "서버가 메시지를 수신했습니다: %s", buffer); send(clientSocket, response, strlen(response), 0); } // 연결 종료 closesocket(clientSocket); closesocket(serverSocket); WSACleanup(); return 0; }
- TCP Client
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <winsock2.h> #include <ws2tcpip.h> #pragma comment(lib, "ws2_32.lib") #define MAX_BUFFER_SIZE 1024 int main() { WSADATA wsaData; SOCKET clientSocket; struct sockaddr_in serverAddr; struct in_addr ipAddr; char buffer[MAX_BUFFER_SIZE]; char message[MAX_BUFFER_SIZE]; // Winsock 초기화 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("Failed to initialize winsock.\n"); return -1; } // 소켓 생성 if ((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { printf("Failed to create socket.\n"); return -1; } // 서버 정보 설정 serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(7777); if (inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr) <= 0) { printf("Invalid address/ Address not supported.\n"); return -1; } // 서버에 연결 if (connect(clientSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) { printf("Failed to connect to server.\n"); return -1; } while (1) { // 사용자로부터 메시지 입력 printf("메시지를 입력하세요 (종료하려면 q 또는 Q): "); fgets(message, MAX_BUFFER_SIZE, stdin); message[strcspn(message, "\n")] = '\0'; if (strcmp(message, "q") == 0 || strcmp(message, "Q") == 0) { break; } // 서버로 메시지 전송 send(clientSocket, message, strlen(message), 0); // 서버로부터 응답 수신 memset(buffer, 0, MAX_BUFFER_SIZE); int bytesRead = recv(clientSocket, buffer, MAX_BUFFER_SIZE, 0); if (bytesRead == SOCKET_ERROR || bytesRead == 0) { break; } printf("서버로부터 받은 응답: %s\n", buffer); } // 연결 종료 closesocket(clientSocket); WSACleanup(); return 0; }
- 동작 및 연결 확인
👉 TCP는 연결지향형 프로토콜로 3way-handshake 과정을 통해서 클라이언트가 연결되었는지를 먼저 확인합니다.
✅ UDP
사용자 데이터그램 프로토콜(User Datagram Protocol, UDP)은 유니버설 데이터그램 프로토콜(Universal Datagram Protocol)이라고 부르기도 합니다.
UDP는 인터넷 프로토콜 스택에서 사용되는 간단한 전송 계층 프로토콜입니다. 주로 소량의 데이터를 빠르게 전송하는데 사용되며, 신뢰성이나 오류 검사 및 수정과 같은 기능은 제공하지 않습니다. 이로 인해 UDP는 TCP보다 더 빠르고 경량이며, 일부 애플리케이션에서는 신뢰성이나 순서를 보장하지 않아도 되는 경우에 많이 사용됩니다.
- UDP Server
#include <iostream> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") int main() { // Winsock 초기화 WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cout << "Failed to initialize winsock." << std::endl; return 1; } // UDP 소켓 생성 SOCKET serverSocket = socket(AF_INET, SOCK_DGRAM, 0); if (serverSocket == INVALID_SOCKET) { std::cout << "Failed to create socket." << std::endl; WSACleanup(); return 1; } // 서버 정보 설정 sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(12345); // 서버 포트 번호 serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 모든 인터페이스에서 수신 대기 // 소켓과 서버 정보를 바인딩 if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { std::cout << "Failed to bind socket." << std::endl; closesocket(serverSocket); WSACleanup(); return 1; } printf("서버를 실행중...."); // 데이터 수신 char buffer[1024]; sockaddr_in clientAddr; int clientAddrSize = sizeof(clientAddr); int recvBytes = recvfrom(serverSocket, buffer, sizeof(buffer), 0, (sockaddr*)&clientAddr, &clientAddrSize); if (recvBytes == SOCKET_ERROR) { std::cout << "Failed to receive data." << std::endl; } else { buffer[recvBytes] = '\0'; std::cout << "Received data from client: " << buffer << std::endl; } // Winsock 정리 closesocket(serverSocket); WSACleanup(); return 0; }
- UDP Client
#include <iostream> #include <string> #include <winsock2.h> #include <ws2tcpip.h> #pragma comment(lib, "ws2_32.lib") int main() { // Winsock 초기화 WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cout << "Failed to initialize winsock." << std::endl; return 1; } // UDP 소켓 생성 SOCKET clientSocket = socket(AF_INET, SOCK_DGRAM, 0); if (clientSocket == INVALID_SOCKET) { std::cout << "Failed to create socket." << std::endl; WSACleanup(); return 1; } // 서버 정보 설정 sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(12345); // 서버 포트 번호 if (inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr) <= 0) { printf("Invalid address/ Address not supported.\n"); return -1; } // 데이터 전송 std::string message; while (true) { std::cout << "Enter a message (q to quit): "; std::getline(std::cin, message); if (message == "q") break; if (sendto(clientSocket, message.c_str(), (int)message.size(), 0, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { std::cout << "Failed to send data." << std::endl; break; } } // Winsock 정리 closesocket(clientSocket); WSACleanup(); return 0; }
- 동작 및 연결 확인
👉 UDP는 서로 간의 연견을 확인하지 않고 데이터를 보내는 것을 확인할 수 있습니다.
Uploaded by N2T