As respostas a essas perguntas variam dependendo de você estar usando um soquete de fluxo ( SOCK_STREAM
) ou um soquete de datagrama ( SOCK_DGRAM
) - no TCP / IP, o primeiro corresponde ao TCP e o segundo ao UDP.
Como você sabe qual o tamanho da passagem do buffer recv()
?
SOCK_STREAM
: Realmente não importa muito. Se o seu protocolo é transacional / interativo, basta escolher um tamanho que possa conter a maior mensagem / comando individual que você esperaria razoavelmente (3000 provavelmente é bom). Se o seu protocolo estiver transferindo dados em massa, buffers maiores podem ser mais eficientes - uma boa regra geral é a mesma que o kernel recebe o tamanho do buffer do soquete (geralmente algo em torno de 256kB).
SOCK_DGRAM
: Use um buffer grande o suficiente para armazenar o maior pacote que o protocolo em nível de aplicativo já envia. Se você estiver usando o UDP, em geral, seu protocolo no nível do aplicativo não deve enviar pacotes maiores que 1400 bytes, porque eles certamente precisarão ser fragmentados e remontados.
O que acontece se recv
um pacote for maior que o buffer?
SOCK_STREAM
: A questão realmente não faz sentido, porque os soquetes de fluxo não têm um conceito de pacotes - são apenas um fluxo contínuo de bytes. Se houver mais bytes disponíveis para leitura do que o seu buffer tem espaço, eles serão colocados na fila pelo sistema operacional e disponíveis para sua próxima chamada recv
.
SOCK_DGRAM
: Os bytes em excesso são descartados.
Como posso saber se recebi a mensagem inteira?
SOCK_STREAM
: Você precisa criar uma maneira de determinar o final da mensagem em seu protocolo no nível do aplicativo. Geralmente, esse é um prefixo de comprimento (iniciando cada mensagem com o comprimento da mensagem) ou um delimitador de final de mensagem (que pode ser apenas uma nova linha em um protocolo baseado em texto, por exemplo). Uma terceira opção menos usada é exigir um tamanho fixo para cada mensagem. Combinações dessas opções também são possíveis - por exemplo, um cabeçalho de tamanho fixo que inclui um valor de comprimento.
SOCK_DGRAM
: Uma única recv
chamada sempre retorna um único datagrama.
Existe uma maneira de fazer com que um buffer não tenha uma quantidade fixa de espaço, para que eu possa continuar adicionando a ele sem medo de ficar sem espaço?
Não. No entanto, você pode tentar redimensionar o buffer usando realloc()
(se ele foi originalmente alocado com malloc()
ou calloc()
, ou seja).
recv
no buffer após a mensagem parcial. Você não deve usarstrstr()
o buffer bruto preenchido porrecv()
- não há garantia de que ele contenha um terminador nulo; portanto, pode causarstrstr()
uma falha.recv()
escrever mais bytes do que o espaço restante.Para protocolos de streaming como o TCP, você pode definir seu buffer para qualquer tamanho. Dito isto, valores comuns que são potências de 2, como 4096 ou 8192, são recomendados.
Se houver mais dados do que seu buffer, ele simplesmente será salvo no kernel para sua próxima chamada
recv
.Sim, você pode continuar aumentando seu buffer. Você pode fazer uma recv no meio do buffer, começando no deslocamento
idx
, você faria:fonte
Se você possui um
SOCK_STREAM
soquete,recv
apenas obtém "até os primeiros 3000 bytes" do fluxo. Não há uma orientação clara sobre o tamanho do buffer: a única vez que você sabe o tamanho de um fluxo é quando tudo está pronto ;-).Se você possui um
SOCK_DGRAM
soquete e o datagrama é maior que o buffer,recv
preenche o buffer com a primeira parte do datagrama, retorna -1 e define errno como EMSGSIZE. Infelizmente, se o protocolo for UDP, isso significa que o restante do datagrama está perdido - parte do motivo pelo qual o UDP é chamado de protocolo não confiável (eu sei que existem protocolos de datagrama confiáveis, mas eles não são muito populares) nomeie um na família TCP / IP, apesar de conhecer bem este último ;-).Para aumentar um buffer dinamicamente, aloque-o inicialmente com
malloc
e userealloc
conforme necessário. Mas isso não irá ajudá-lo arecv
partir de uma fonte UDP, infelizmente.fonte
Para o
SOCK_STREAM
soquete, o tamanho do buffer realmente não importa, porque você está apenas puxando alguns dos bytes em espera e pode recuperar mais em uma próxima chamada. Basta escolher o tamanho do buffer que você puder pagar.Para o
SOCK_DGRAM
soquete, você receberá a parte apropriada da mensagem em espera e o restante será descartado. Você pode obter o tamanho do datagrama em espera com o seguinte ioctl:Como alternativa, você pode usar
MSG_PEEK
eMSG_TRUNC
sinalizadores darecv()
chamada para obter o tamanho do datagrama em espera.Você precisa
MSG_PEEK
espiar (não receber) a mensagem em espera - recv retorna o tamanho real, não truncado; e vocêMSG_TRUNC
não precisa sobrecarregar seu buffer atual.Então você pode apenas
malloc(size)
o buffer e orecv()
datagrama reais .fonte
malloc()
um buffer de 64 KBMSG_TRUNC
for desnecessário?SOCK_DGRAM
não é apenas UDP.Não há resposta absoluta para sua pergunta, porque a tecnologia sempre é obrigada a ser específica da implementação. Suponho que você esteja se comunicando no UDP porque o tamanho do buffer de entrada não traz problemas à comunicação TCP.
De acordo com a RFC 768 , o tamanho do pacote (inclusive cabeçalho) para UDP pode variar de 8 a 65.515 bytes. Portanto, o tamanho à prova de falhas para o buffer de entrada é de 65 507 bytes (~ 64 KB)
No entanto, nem todos os pacotes grandes podem ser roteados corretamente pelos dispositivos de rede; consulte a discussão existente para obter mais informações:
Qual é o tamanho ideal de um pacote UDP para o máximo rendimento?
Qual é o maior tamanho de pacote UDP seguro da Internet
fonte
16kb é quase certo; se você estiver usando ethernet gigabit, cada pacote poderá ter 9 KB de tamanho.
fonte