본문 바로가기
💻 programming/c++

[c++] TCP/IP 서버 클라이언트 설명 및 예제 코드 (소켓 프로그래밍)

by 연구원-A 2023. 3. 20.
반응형

TCP(전송 제어 프로토콜)는 두 컴퓨터 간의 안정적인 통신을 설정하는 데 사용됩니다. 

TCP 서버와 클라이언트 코드를 통해 두 컴퓨터 간의 데이터를 전송할 수 있습니다.

이 문서에서는 C++로 작성된 TCP 서버와 클라이언트의 기본 예제 코드를 제공합니다.

 

TCP 서버 코드

먼저 TCP 서버 코드는 소켓을 생성하고, (1. 소켓 생성)

포트 번호에 바인딩하고, (2. 바인딩)

들어오는 연결을 수신 대기하고, (3. 수신 대기)

들어오는 연결을 수락합니다. (4. 연결 수락)

소켓은 두 컴퓨터 간의 양방향 통신을 허용하는 엔드포인트를 의미합니다.

소켓은 통신 도메인, 소켓 유형 및 프로토콜을 제공하여 만들 수 있습니다. (IP 버전, 포트번호, TCP/UDP 프로토콜 등)

먼저, 통신 도메인은 소켓이 사용되는 통신 유형을 지정합니다.

IPv4의 경우 AF_INET 통신 도메인을 사용합니다.

소켓 유형에 통신 시맨틱을 지정합니다

TCP/IP의 경우 SOCK_STREAM 유형을 사용합니다.

프로토콜은 소켓과 함께 사용할 특정 프로토콜을 지정합니다. 

여기서는 운영체제가 가장 적합한 프로토콜을 선택할 수 있도록 0을 사용합니다.

아래가 TCP 서버의 샘플 코드입니다:

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cstring>

#define PORT 8080

int main() {
    int server_fd, new_socket, valread;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};
    const char* hello = "Hello from server";

    // Creating socket file descriptor
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        std::cerr << "Socket creation error" << std::endl;
        return -1;
    }

    // Attaching socket to the port 8080
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        std::cerr << "Setsockopt error" << std::endl;
        return -1;
    }
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // Bind the socket to the specified port
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0) {
        std::cerr << "Bind failed" << std::endl;
        return -1;
    }

    // Listen for incoming connections
    if (listen(server_fd, 3) < 0) {
        std::cerr << "Listen error" << std::endl;
        return -1;
    }

    // Accept incoming connections
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {
        std::cerr << "Accept error" << std::endl;
        return -1;
    }

    // Receive message from the client
    valread = read(new_socket, buffer, 1024);
    std::cout << buffer << std::endl;

    // Send message to the client
    send(new_socket, hello, strlen(hello), 0);
    std::cout << "Hello message sent" << std::endl;

    return 0;
}

1. 위 코드에서는 먼저 socket() 시스템 호출을 사용하여 소켓을 생성합니다. 

소켓 생성에 실패하면 프로그램이 종료되고 오류 메시지를 반환합니다.

2. 소켓을 생성한 후 setsockopt() 시스템 호출을 사용하여 포트 8080에 소켓을 연결합니다.

이렇게 하면 소켓이 닫힌 후에도 포트를 재사용할 수 있습니다.

3. 다음으로 bind() 시스템 호출을 사용하여 소켓을 지정된 포트에 바인딩합니다.

바인딩이 실패하면 프로그램이 종료되고 오류 메시지가 반환됩니다.

4. 그런 다음 listen() 시스템 호출을 사용하여 들어오는 연결을 수신 대기합니다.

매개변수 3은 대기열에 있을 수 있는 최대 연결 수를 지정합니다.

5. 마지막으로 accept() 시스템 호출을 사용하여 들어오는 연결을 수락합니다.

수락에 실패하면 프로그램이 종료되고 오류 메시지가 반환됩니다.

 

TCP 클라이언트 코드

TCP 클라이언트 코드는 소켓을 생성하고 IP 주소와 포트 번호를 사용하여 서버에 연결합니다.

클라이언트 코드는 서버 코드보다 간단합니다. 

socket() 시스템 함수를 호출하여 소켓을 생성한 다음 connect() 시스템 호출을 사용하여 서버에 연결합니다.

아래는 TCP 클라이언트의 샘플 코드입니다:

#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>

#define PORT 8080

int main(int argc, char const *argv[]) {
    int sock = 0, valread;
    struct sockaddr_in serv_addr;
    const char* hello = "Hello from client";
    char buffer[1024] = {0};

    // Create socket file descriptor
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        std::cerr << "Socket creation error" << std::endl;
        return -1;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // Convert IPv4 and IPv6 addresses from text to binary form
    if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) {
        std::cerr << "Invalid address/ Address not supported" << std::endl;
        return -1;
    }

    // Connect to the server
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        std::cerr << "Connection Failed" << std::endl;
        return -1;
    }

    // Send message to the server
    send(sock, hello, strlen(hello), 0);
    std::cout << "Hello message sent" << std::endl;

    // Receive message from the server
    valread = read(sock, buffer, 1024);
    std::cout << buffer << std::endl;

    return 0;
}

 

1. 위 코드에서는 먼저 socket() 시스템 호출을 사용하여 소켓을 생성합니다.

소켓 생성에 실패하면 프로그램이 종료되고 오류 메시지를 반환합니다.

2. 그런 다음 sockaddr_in 구조를 사용하여 서버 주소와 포트 번호를 지정합니다.

포트 번호는 서버에서 사용하는 포트 번호와 동일합니다.

3. 다음으로 inet_pton() 시스템 호출을 사용하여 IP 주소를 텍스트에서 바이너리 형식으로 변환합니다.

변환에 실패하면 프로그램이 종료되고 오류 메시지가 반환됩니다.

4. 마지막으로 connect() 시스템 호출을 사용하여 서버에 연결합니다.

연결에 실패하면 프로그램이 종료되고 오류 메시지를 반환합니다.

5. 서버에 연결한 후 클라이언트는 send() 시스템 호출을 사용하여 서버에 "Hello from client" 메시지를 보냅니다.

그런 다음 클라이언트는 read() 시스템 호출을 사용하여 서버로부터 메시지를 수신할 때까지 기다립니다.

6. 메시지를 수신하면 클라이언트는 콘솔에 메시지를 인쇄합니다.

이것이 C++로 TCP 서버와 클라이언트를 만드는 기본입니다. 

이 코드를 사용하면 두 컴퓨터 간에 연결을 설정하고 메시지를 안정적으로 교환할 수 있습니다.

 

본 문서는 Notion AI와 DeepL 번역기를 이용해 작성된 것입니다 (일부 번역 수정)
반응형

댓글