TCP 소켓 프로그래밍

1. TCP란?

TCP(Transmission Control Protocol)는 연결 지향적이고 신뢰성 있는 프로토콜이다.

 

특징:

  - 연결 지향성 : 데이터 전송 전 연결 설정 필요

  - 신뢰성 : 데이터 손실, 중복, 순서 오류 방지

  - 흐름 제어 : 수신자의 처리 속도에 맞춰 전송 속도 조절

  - 혼잡 제어 : 네트워크 상황에 따라 전송 속도 조절

  - 전이중 통신(Full-duplex) : 양방향 동시 통신 가능

 

1.1. 연결 지향 통신

TCP의 연결 지향 통신은 신뢰성을 보장하기 위해 여러 방법으로 데이터를 체크한다.

 

  - 3-way handshake: 연결 설정 시 SYN, SYN-ACK, ACK 패킷 교환

  - 데이터 전송: 세그먼트 단위로 데이터 전송, 각 세그먼트에 대해 ACK 응답

  - 4-way handshake: 연결 종료 시 FIN, ACK, FIN, ACK 패킷 교환

 

이런 특성으로 인해 TCP는 신뢰성 있는 데이터 전송을 보장하지만, UDP에 비해 오버헤드가 크다.

 

 

2. TCP 구현 순서

TCP 서버 구현 순서 :

 

1) 소켓 생성

2) 소켓 바인딩

3) 연결 대기(Listen)

4) 클라이언트 연결 수락(Accept)

5) 데이터 송수신

6) 연결 종료

 

 

TCP 클라이언트 구현 순서 :

 

1) 소켓 생성

2) 서버에 연결

3) 데이터 송수신

4) 연결 종료

 

3. TCP 통신의 장단점

장점 :

 

1) 신뢰성이 높음

  - 데이터 전송 순서가 보장됨

  - 패킷 손실 시 재전송 기능

  - 흐름 제어와 혼잡 제어를 통한 안정적인 통신

 

2) 연결 지향적

  - 통신 전 연결 설정 (3-way handshaking)

  - 양방향 통신 가능

  - 데이터 무결성 보장

 

3) 에러 체크

  - 체크섬을 통한 데이터 오류 검출

  - 손상된 패킷 재전송

 

단점:

 

1) 속도가 상대적으로 느림

  - 연결 설정에 시간 소요

  - 패킷 손실 시 재전송으로 인한 지연

  - 오버헤드가 큼

 

2) 리소스 사용량이 많음

  - 연결 상태 유지를 위한 메모리 사용

  - 재전송을 위한 버퍼 필요

 

3) 실시간성 부족

  - 신뢰성을 위한 각종 제어로 인한 지연

  - 실시간 스트리밍에는 부적합할 수 있음

 

 

TCP 소켓 프로그래밍은 신뢰성 있는 데이터 전송이 필요한 경우에 적합하다.

웹 서버, 파일 전송 등의 애플리케이션과 게임의 중요 데이터(게임 상태, 플레이어 정보 등)에 TCP통신을 사용한다.

 

 

4. 간단한 송수신 서버

TCP 통신을 하는 송수신 서버이다.

 

서버

#include <iostream>
#include <string>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#define PORT 7777
using namespace std;

void HandleError(const char* cause) {
    int errCode = ::WSAGetLastError();
    cout << cause << " ErrorCode : " << errCode << endl;
}

int main() {
    WSADATA wsaData;
    if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
        return 1;

    // 소켓 생성
    SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
    if (listenSocket == INVALID_SOCKET) {
        HandleError("Socket");
        return 1;
    }

    SOCKADDR_IN serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(PORT);

    // 바인드
    if (::bind(listenSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        HandleError("Bind");
        return 1;
    }

    // 리스닝
    if (::listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) {
        HandleError("Listen");
        return 1;
    }

    // 연결 수락
    SOCKADDR_IN clientAddr;
    int addrLen = sizeof(clientAddr);

    SOCKET clientSocket = ::accept(listenSocket, (SOCKADDR*)&clientAddr, &addrLen);
    if (clientSocket == INVALID_SOCKET) {
        HandleError("Accept");
        return 1;
    }

    cout << "Client Connected" << endl;

    // 데이터 송수신
    char recvBuffer[100] = { 0 };
    int recvLen = ::recv(clientSocket, recvBuffer, sizeof(recvBuffer), 0);
    if (recvLen <= 0) {
        HandleError("Recv");
        return 1;
    }
    cout << "Received Data : " << recvBuffer << endl;

    char sendBuffer[100] = "Hello Client!";
    ::send(clientSocket, sendBuffer, strlen(sendBuffer), 0);

    ::closesocket(clientSocket);
    ::closesocket(listenSocket);
    ::WSACleanup();
    system("pause");  // 콘솔 창이 바로 닫히지 않도록
}



클라이언트

 

#include <iostream>
#include <winsock2.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#define PORT 7777
#define SERVERIP "127.0.0.1"
using namespace std;

void HandleError(const char* cause) {
    int errCode = ::WSAGetLastError();
    cout << cause << " ErrorCode : " << errCode << endl;
}

int main() {
    WSADATA wsaData;
    if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
        return 1;

    // 소켓 생성
    SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
    if (clientSocket == INVALID_SOCKET) {
        HandleError("Socket");
        return 1;
    }

    // 서버 주소 설정
    SOCKADDR_IN serverAddr;
    serverAddr.sin_family = AF_INET;
    ::inet_pton(AF_INET, SERVERIP, &serverAddr.sin_addr); // 서버의 주소 지정
    serverAddr.sin_port = htons(PORT);

    // 서버 연결
    if (::connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        HandleError("Connect");
        return 1;
    }

    cout << "Server Connected" << endl;

    // 송신
    char sendBuffer[100] = "Hello Server!";
    ::send(clientSocket, sendBuffer, strlen(sendBuffer), 0);

    // 수신
    char recvBuffer[100] = { 0 };
    int recvLen = ::recv(clientSocket, recvBuffer, sizeof(recvBuffer), 0);
    if (recvLen <= 0) {
        HandleError("Recv");
        return 1;
    }
    cout << "Received Data : " << recvBuffer << endl;

    ::closesocket(clientSocket);
    ::WSACleanup();
    system("pause");

}

서버와 클라이언트가 연결되면 메시지를 한번 주고받는다.

'학습 > C++ 소켓 프로그래밍' 카테고리의 다른 글

소켓 옵션  (0) 2024.11.08
UDP 소켓 프로그래밍  (0) 2024.10.31
소켓 프로그래밍  (0) 2024.10.29
소켓 프로그래밍 기초  (0) 2024.10.29
네트워크 프로그래밍 기초  (0) 2024.10.22