Pergunta

Estou um pouco confuso sobre a programação de soquetes em C.

Você cria um soquete, vincula-o a uma interface e a um endereço IP e faz com que ele escute.Encontrei alguns recursos da web sobre isso e entendi bem.Em particular, encontrei um artigo Programação de rede em sistemas Unix ser muito informativo.

O que me confunde é o tempo de chegada dos dados no soquete.

Como você pode saber quando os pacotes chegam e qual é o tamanho do pacote, você mesmo tem que fazer todo o trabalho pesado?

Minha suposição básica aqui é que os pacotes podem ter comprimento variável; portanto, quando os dados binários começarem a aparecer no soquete, como começar a construir pacotes a partir deles?

Foi útil?

Solução

A resposta curta é que você mesmo terá que fazer todo o trabalho pesado.Você pode ser notificado de que há dados disponíveis para leitura, mas não saberá quantos bytes estão disponíveis.Na maioria dos protocolos IP que usam pacotes de comprimento variável, haverá um cabeçalho com comprimento fixo conhecido anexado ao pacote.Este cabeçalho conterá o comprimento do pacote.Você lê o cabeçalho, obtém o comprimento do pacote e depois lê o pacote.Você repete esse padrão (ler o cabeçalho e depois ler o pacote) até que a comunicação seja concluída.

Ao ler dados de um soquete, você solicita um certo número de bytes.A chamada de leitura pode ser bloqueada até que o número solicitado de bytes seja lido, mas pode retornar menos bytes do que o solicitado.Quando isso acontece, basta tentar novamente a leitura, solicitando os bytes restantes.

Aqui está uma função C típica para ler um determinado número de bytes de um soquete:

/* buffer points to memory block that is bigger than the number of bytes to be read */
/* socket is open socket that is connected to a sender */
/* bytesToRead is the number of bytes expected from the sender */
/* bytesRead is a pointer to a integer variable that will hold the number of bytes */
/*           actually received from the sender. */
/* The function returns either the number of bytes read, */
/*                             0 if the socket was closed by the sender, and */
/*                            -1 if an error occurred while reading from the socket */
int readBytes(int socket, char *buffer, int bytesToRead, int *bytesRead)
{
    *bytesRead = 0;
    while(*bytesRead < bytesToRead)
    {
        int ret = read(socket, buffer + *bytesRead, bytesToRead - *bytesRead);
        if(ret <= 0)
        {
           /* either connection was closed or an error occurred */
           return ret;
        }
        else
        {
           *bytesRead += ret;
        }
    }
    return *bytesRead;
}

Outras dicas

Portanto, a resposta à sua pergunta depende bastante se você está usando UDP ou TCP como transporte.

Para UDP, a vida fica muito mais simples, pois você pode chamar recv/recvfrom/recvmsg com o tamanho de pacote necessário (de qualquer maneira, você provavelmente enviaria pacotes de comprimento fixo da origem) e presumir que, se os dados estiverem disponíveis , ele está presente em vários tamanhos de pacotes.(I.E.Você chama recv* com o tamanho do pacote do lado de envio e está pronto.)

Para o TCP, a vida fica um pouco mais interessante - para o propósito desta explicação, assumirei que você já sabe como usar socket(), bind(), listen() e accept() - sendo este último como você obtém o descritor de arquivo (FD) da sua conexão recém-criada.

Existem duas maneiras de fazer a E/S para um soquete - bloqueio, no qual você chama read(fd, buf, N) e a leitura fica lá e espera até que você leia N bytes em buf - ou sem bloqueio, no qual você deve verificar (usando select() ou poll()) se o FD é legível e ENTÃO fazer sua leitura().

Ao lidar com conexões baseadas em TCP, o sistema operacional não presta atenção aos tamanhos dos pacotes, pois é considerado um fluxo contínuo de dados, e não pedaços separados do tamanho de pacotes.

Se o seu aplicativo usa "pacotes" (estruturas de dados compactadas ou descompactadas que você está transmitindo), você deve ser capaz de chamar read() com o argumento de tamanho adequado e ler uma estrutura de dados inteira do soquete de cada vez.A única ressalva com a qual você precisa lidar é lembrar de ordenar adequadamente os bytes de todos os dados que você está enviando, caso o sistema de origem e de destino tenham endian de bytes diferentes.Isso se aplica tanto ao UDP quanto ao TCP.

No que diz respeito à programação do soquete *NIX, eu recomendo fortemente o W."Programação de rede Unix de Richard Stevens, vol.1" (UNPv1) e "Programação Avançada em Ambiente Unix" (APUE).O primeiro é um livro sobre programação baseada em rede, independentemente do transporte, e o último é um bom livro de programação geral, pois se aplica à programação baseada em *NIX.Além disso, procure por "TCP/IP Illustrated", Volumes 1 e 2.

Quando você faz uma leitura no soquete, você informa quantos bytes máximos devem ser lidos, mas se não tiver tantos, ele fornece quantos bytes possui.Cabe a você projetar o protocolo para saber se possui um pacote parcial ou não.Por exemplo, no passado, ao enviar dados binários de comprimento variável, eu colocava um int no início que dizia quantos bytes esperar.Eu faria uma leitura solicitando um número de bytes maior que o maior pacote possível em meu protocolo e, em seguida, compararia o primeiro int com quantos bytes recebi e o processaria ou tentaria mais leituras até que eu ' consegui o pacote completo, dependendo.

Os soquetes operam em um nível mais alto que os pacotes brutos - é como um arquivo do qual você pode ler/escrever.Além disso, quando você tenta ler de um soquete, o sistema operacional bloqueará (colocará em espera) seu processo até que ele tenha dados para atender à solicitação.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top