Implementação de Socket TCP em C++ para Windows

Para estabelecer comunicação de rede em sistemas Windows utilizando a linguagem C++, a biblioteca Winsock é o padrão da indústria. O código abaixo ilustra a criação de um servidor que escuta conexões e um cliente que se conecta para trocar mensagens através do protocolo TCP.

Código do Servidor (server.cpp)

O servidor é responsável por inicializar o ambiente de rede, criar um soquete passivo e aguardar solicitações de conexão. Upon accepting a connection, it reads incoming data and sends a confirmation response.

#include <iostream>
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <cstring>

#pragma comment(lib, "Ws2_32.lib")

const int PORTA_SERVICIO = 9090;

int main() {
    WSADATA dadosWinsock;
    SOCKET soqueteEscuta, soqueteCliente;
    struct sockaddr_in enderecoServidor, enderecoCliente;
    int tamanhoCliente = sizeof(enderecoCliente);
    char pacoteRecebido[2048] = { 0 };
    const char* resposta = "Ack do servidor";

    // Inicializa a DLL Winsock
    if (WSAStartup(MAKEWORD(2, 2), &dadosWinsock) != 0) {
        std::cerr << "Erro ao iniciar Winsock." << std::endl;
        return 1;
    }

    // Cria o soquete TCP
    soqueteEscuta = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (soqueteEscuta == INVALID_SOCKET) {
        std::cerr << "Falha na criacao do soquete." << std::endl;
        WSACleanup();
        return 1;
    }

    // Configura estrutura do servidor
    enderecoServidor.sin_family = AF_INET;
    enderecoServidor.sin_addr.s_addr = INADDR_ANY;
    enderecoServidor.sin_port = htons(PORTA_SERVICIO);

    // Associa o soquete ao endereco
    if (bind(soqueteEscuta, (struct sockaddr*)&enderecoServidor, sizeof(enderecoServidor)) == SOCKET_ERROR) {
        std::cerr << "Erro no bind." << std::endl;
        closesocket(soqueteEscuta);
        WSACleanup();
        return 1;
    }

    // Entra em modo de escuta
    if (listen(soqueteEscuta, SOMAXCONN) == SOCKET_ERROR) {
        std::cerr << "Erro no listen." << std::endl;
        closesocket(soqueteEscuta);
        WSACleanup();
        return 1;
    }

    std::cout << "Aguardando conexoes na porta " << PORTA_SERVICIO << std::endl;

    // Aceita conexao entrante
    soqueteCliente = accept(soqueteEscuta, (struct sockaddr*)&enderecoCliente, &tamanhoCliente);
    if (soqueteCliente == INVALID_SOCKET) {
        std::cerr << "Falha no accept." << std::endl;
        closesocket(soqueteEscuta);
        WSACleanup();
        return 1;
    }

    // Exibe IP do cliente
    char ipCliente[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &enderecoCliente.sin_addr, ipCliente, INET_ADDRSTRLEN);
    std::cout << "Cliente conectado: " << ipCliente << std::endl;

    // Recebe dados
    int bytesLidos = recv(soqueteCliente, pacoteRecebido, sizeof(pacoteRecebido), 0);
    if (bytesLidos == SOCKET_ERROR) {
        std::cerr << "Erro ao receber dados." << std::endl;
    } else if (bytesLidos > 0) {
        std::cout << "Mensagem recebida: " << pacoteRecebido << std::endl;
    }

    // Envia resposta
    send(soqueteCliente, resposta, std::strlen(resposta), 0);
    std::cout << "Resposta enviada." << std::endl;

    // Limpeza
    closesocket(soqueteCliente);
    closesocket(soqueteEscuta);
    WSACleanup();

    return 0;
}

Código do Cliente (client.cpp)

O cliente inicia a sequência conectando-se ao endereço IP do servidor. Após estabelecer o link, envia um payload de teste e aguarda o retorno antes de encerrar a sessão.

#include <iostream>
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <cstring>

#pragma comment(lib, "Ws2_32.lib")

const int PORTA_ALVO = 9090;
const char* IP_SERVIDOR = "127.0.0.1";

int main() {
    WSADATA infoWinsock;
    SOCKET soqueteRemoto;
    struct sockaddr_in enderecoRemoto;
    char bufferLeitura[2048] = { 0 };
    const char* msgEnvio = "Hello Server";

    // Setup inicial
    if (WSAStartup(MAKEWORD(2, 2), &infoWinsock) != 0) {
        std::cerr << "Inicializacao Winsock falhou." << std::endl;
        return 1;
    }

    // Cria soquete
    soqueteRemoto = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (soqueteRemoto == INVALID_SOCKET) {
        std::cerr << "Erro ao criar soquete." << std::endl;
        WSACleanup();
        return 1;
    }

    // Define endereco do servidor
    enderecoRemoto.sin_family = AF_INET;
    inet_pton(AF_INET, IP_SERVIDOR, &enderecoRemoto.sin_addr);
    enderecoRemoto.sin_port = htons(PORTA_ALVO);

    // Conecta
    if (connect(soqueteRemoto, (struct sockaddr*)&enderecoRemoto, sizeof(enderecoRemoto)) == SOCKET_ERROR) {
        std::cerr << "Conexao falhou." << std::endl;
        closesocket(soqueteRemoto);
        WSACleanup();
        return 1;
    }

    std::cout << "Conectado ao servidor." << std::endl;

    // Envio de dados
    if (send(soqueteRemoto, msgEnvio, std::strlen(msgEnvio), 0) == SOCKET_ERROR) {
        std::cerr << "Erro no envio." << std::endl;
    } else {
        std::cout << "Dados enviados." << std::endl;
    }

    // Recebe resposta
    int lido = recv(soqueteRemoto, bufferLeitura, sizeof(bufferLeitura), 0);
    if (lido > 0) {
        std::cout << "Resposta: " << bufferLeitura << std::endl;
    }

    // Finaliza
    closesocket(soqueteRemoto);
    WSACleanup();

    return 0;
}

Fluxo de Comunicação

A interação entre as duas pontas segue uma ordem específica de chamadas de sistema. Primeiramente, a biblioteca de sockets é carregada na memória. O servidor aloca um recurso de rede e o associa a uma porta local, entrando em estado de espera. Simultaneamente, o cliente resolve o endereço do servidor e solicita o estabelecimento do link. Uma vez aceito, o canal bidirecinoal permite o tráfego de pacotes até que um dos participantes execute o fechamento do soquete e libere os recursos da API Winsock.

Tags: C++ winsock TCP-IP Socket-Programming Windows-API

Publicado em 10-21 03:14