Configurações e Princípios de Sockets TCP/UDP para Comunicação de Rede

Buffers de E/S em Sockets TCP

Em sockets TCP, os dados são transmitidos como um fluxo contínuo sem delimitações fixas. Isso permite que múltiplas operações de escrita sejam consolidadas em uma única leitura, e uma única escrita possa ser dividida em várias leituras. A transmissão é gerenciada por buffers de E/S individuais, que são criados automaticamente ao inicializar o socket. Quando a função write é chamada, os dados não são enviaods imediatamente, mas copiados para o buffer de saída. O protocloo TCP cuida do envio desses dados para o buffer de entrada do receptor, de onde a função read pode recuperá-los.

Características importantes dos buffers: cada socket TCP mantém seus próprios buffers de E/S; ao fechar um socket, quaisquer dados residuais no buffer de saída ainda são transmitidos, mas dados no buffer de entrada são perdidos. Para gerenciar situações de sobrecarga, TCP utiliza janelas deslizantes, pausando a transmissão se o buffer de entrada do receptor estiver cheio.

A função write retorna imediatamente após copiar os dados para o buffer de saída, sem esperar pela confirmação de recebimento pelo host remoto. A transmissão completa é realizada em segundo plano pelo protocolo TCP.

Conexão TCP e Sequenciamento

TCP utiliza números de sequência (SEQ) e confirmação (ACK) em unidades de bytes para garantir a entrega ordenada dos dados. O valor de ACK indica o próximo byte esperado pelo receptor. Por exemplo, se SEQ for 1200 e 100 bytes forem enviados, o próximo ACK será 1301.

O processo de desconexão segue uma sequência de quatro etapas: o lado que inicia envia um FIN, o outro responde com ACK, em seguida envia seu próprio FIN, e finalmente recebe o ACK conclusivo. Em cenários onde o ACK não acompanha dados, como no primeiro ACK do receptor, o número de sequência permanece inalterado.

Comunicação Baseada em UDP

O UDP não requer estabelecimento de conexão prévia para a transmissão de dados. Tanto o servidor quanto o cliente utilizam um único socket, ao contrário do TCP, onde o servidor precisa criar um socket para cada cliente conectado. No UDP, as funções sendto e recvfrom devem ser chamadas em pares correspondentes para cada operação de envio e recebimento.

Funções UDP Essenciais

A função sendto é usada para enviar dados a um destino específico, enquanto recvfrom recebe dados de qualqure fonte, registrando o endereço do remetente. Ambas requerem parâmetros como o socket, buffer de dados, tamanho, flags e estruturas de endereço.

Conexão de Sockets UDP

Embora sockets UDP sejam considerados não conectados, é possível utilizar a função connect para registrar permanentemente o endereço de destino no socket. Isso evita a necessidade de registrar o endereço repetidamente em cada chamada sendto, otimizando o envio de múltiplos pacotes para o mesmo destino.


int socket_udp = socket(PF_INET, SOCK_DGRAM, 0);
struct sockaddr_in endereco_alvo;
memset(&endereco_alvo, 0, sizeof(endereco_alvo));
endereco_alvo.sin_family = AF_INET;
endereco_alvo.sin_addr.s_addr = inet_addr(argv[1]);
endereco_alvo.sin_port = htons(atoi(argv[2]));
connect(socket_udp, (struct sockaddr*)&endereco_alvo, sizeof(endereco_alvo));
// Após conectar, usar write/read ou sendto/recvfrom
char mensagem[] = "Dados de teste";
write(socket_udp, mensagem, strlen(mensagem));
close(socket_udp);

Desconexão Parcial (Half-close)

Um socket é composto por dois fluxos independentes: um de entrada e outro de saída. A função shutdown permite fechar seletivamente um desses fluxos, em vez de desativar todo o socket com close.


int shutdown(int socket_descritor, int modo);
// Modos: SHUT_RD (fecha entrada), SHUT_WR (fecha saída), SHUT_RDWR (fecha ambos)

Ao fechar o fluxo de entrada, não são enviadas mensagens de controle, mas se dados chegarem após o fechamento, um pacote RST é enviado como resposta. Dados já presentes no buffer de entrada ainda podem ser lidos. Fechar o fluxo de saída envia um pacote FIN ao peer remoto. Em contraste, a função close envia um pacote RST imediatamente, interrompendo abruptamente a comunicação.

Resolução de Nomes com DNS

O DNS permite mapear nomes de domínio para endereços IP, facilitando a localização de hosts em redes. A função gethostbyname retorna informações de host, como nome oficial e endereços IP, a partir de um nome de domínio. A estrutura hostent armazena esses dados, incluindo aliases e lista de endereços IP.


struct hostent *host_info = gethostbyname("exemplo.com");
if (host_info) {
    printf("Nome oficial: %s\n", host_info->h_name);
    printf("Primeiro IP: %s\n", inet_ntoa(*(struct in_addr*)host_info->h_addr_list[0]));
}

Para a operação inversa, gethostbyaddr obtém informações de host a partir de um endereço IP, útil para identificação de domínios.

Opções e Configurações de Socket

Sockets oferecem diversas opções configuráveis em diferentes camadas de protocolo, como SOL_SOCKET (camada de socket), IPPROTO_IP (camada IP) e IPPROTO_TCP (camada TCP). Essas opções podem ser lidas ou modificadas usando as funções getsockopt e setsockopt.


int obter_opcao(int socket_fd, int nivel, int nome_opcao, void *valor, socklen_t *tamanho);
int definir_opcao(int socket_fd, int nivel, int nome_opcao, const void *valor, socklen_t tamanho);

Ajustando Buffers TCP

As opções SO_SNDBUF e SO_RCVBUF controlam os tamanhos dos buffers de saída e entrada, respectivamente. Modificar esses valores pode impactar a performance da transmissão.


int socket_fd = socket(PF_INET, SOCK_STREAM, 0);
int tamanho_buffer = 4096;
setsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF, &tamanho_buffer, sizeof(tamanho_buffer));

Reutilização de Endereços com SO_REUSEADDR

Durante a reinicialização de servidores, portas em estado TIME_WAIT podem impedir novas associações. Definir SO_REUSEADDR como 1 permite reutilizar essas portas imediatamente, evitando erros de vinculação.

Desativando o Algoritmo de Nagle com TCP_NODELAY

O algoritmo de Nagle otimiza a eficiência de rede atrasando o envio de pacotes pequenos para consolidá-los em pacotes maiores. Embora reduza o congestionamento, pode introduzir latência. Definir TCP_NODELAY como 1 desativa esse comportamento, enviando dados imediatamente.

Tags: TCP UDP DNS SocketAPI NetworkBuffers

Publicado em 6-5 07:21 por Thomas