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.