por - ¿Qué tan grande debe ser mi buffer recv cuando llamo a recv en la biblioteca de socket?
send c (6)
Tengo algunas preguntas sobre la biblioteca de socket en C. Aquí hay un fragmento de código al que me referiré en mis preguntas.
char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
- ¿Cómo decido qué tan grande hacer recv_buffer? Estoy usando 3000, pero es arbitrario.
- ¿Qué pasa si
recv()
recibe un paquete más grande que mi buffer? - ¿Cómo puedo saber si he recibido el mensaje completo sin llamar nuevamente a recv y que espere para siempre cuando no se reciba nada?
- ¿Hay alguna manera en que pueda hacer que un buffer no tenga una cantidad fija de espacio, de modo que pueda seguir agregando sin temor a quedarme sin espacio? tal vez usando
strcat
para concatenar la última respuestarecv()
al buffer?
Sé que hay muchas preguntas en una, pero agradecería cualquier respuesta.
16kb es aproximadamente correcto; si está usando gigabit ethernet, cada paquete podría tener un tamaño de 9 kb.
Las respuestas a estas preguntas varían dependiendo de si está utilizando un socket de flujo ( SOCK_STREAM
) o un socket de datagrama ( SOCK_DGRAM
), dentro de TCP / IP, el primero corresponde a TCP y el segundo a UDP.
¿Cómo sabes qué tan grande hacer pasar el buffer a recv()
?
SOCK_STREAM
: Realmente no importa demasiado. Si su protocolo es transaccional / interactivo, simplemente elija un tamaño que pueda contener el mensaje / comando individual más grande que razonablemente podría esperar (3000 es probable que esté bien). Si su protocolo está transfiriendo datos masivos, entonces los buffers más grandes pueden ser más eficientes. Una buena regla general es la misma que la del kernel para recibir el tamaño del buffer del socket (a menudo algo alrededor de 256kB).SOCK_DGRAM
: utilice un búfer lo suficientemente grande como para contener el paquete más grande que su protocolo de nivel de aplicación haya enviado. Si está utilizando UDP, entonces, en general, su protocolo de nivel de aplicación no debería enviar paquetes de más de 1400 bytes, ya que seguramente tendrán que ser fragmentados y reensamblados.
¿Qué pasa si recv
obtiene un paquete más grande que el buffer?
SOCK_STREAM
: la pregunta realmente no tiene sentido puesto que, porque los sockets de flujo no tienen un concepto de paquetes, son solo un flujo continuo de bytes. Si hay más bytes disponibles para leer de los que su búfer tiene espacio, el sistema operativo los pondrá en cola y estarán disponibles para su próxima llamada arecv
.SOCK_DGRAM
: los bytes en exceso se descartan.
¿Cómo puedo saber si he recibido el mensaje completo?
SOCK_STREAM
: debe crear una forma de determinar el final del mensaje en su protocolo de nivel de aplicación. Comúnmente, esto es un prefijo de longitud (comenzando cada mensaje con la longitud del mensaje) o un delimitador de fin de mensaje (que podría ser una nueva línea en un protocolo basado en texto, por ejemplo). Una tercera opción, menos utilizada, es ordenar un tamaño fijo para cada mensaje. Las combinaciones de estas opciones también son posibles, por ejemplo, un encabezado de tamaño fijo que incluye un valor de longitud.SOCK_DGRAM
: Una única llamadarecv
siempre devuelve un solo datagrama.
¿Hay alguna manera de hacer que un buffer no tenga una cantidad fija de espacio, de modo que pueda seguir agregando sin temor a quedarme sin espacio?
No. Sin embargo, puede intentar cambiar el tamaño del búfer utilizando realloc()
(si se asignó originalmente con malloc()
o calloc()
, eso es).
No hay una respuesta absoluta a su pregunta porque la tecnología siempre está vinculada a la implementación específica. Supongo que se está comunicando en UDP porque el tamaño del búfer entrante no trae problemas a la comunicación TCP.
De acuerdo con RFC 768 , el tamaño del paquete (encabezado incluido) para UDP puede variar de 8 a 65 515 bytes. Entonces, el tamaño a prueba de fallas para el buffer entrante será de 65 507bytes (~ 64KB)
Sin embargo, no todos los paquetes grandes pueden ser enrutados correctamente por dispositivos de red; consulte la discusión existente para obtener más información:
¿Cuál es el tamaño óptimo de un paquete UDP para un rendimiento máximo?
¿Cuál es el tamaño de paquete Safe UDP más grande en Internet?
Para el socket SOCK_STREAM
, el tamaño del búfer en realidad no importa, porque solo está extrayendo algunos de los bytes en espera y puede recuperar más en una próxima llamada. Solo elija el tamaño de búfer que pueda pagar.
Para el socket SOCK_DGRAM
, obtendrá la parte de ajuste del mensaje de espera y el resto se descartará. Puede obtener el tamaño del datagrama en espera con el siguiente ioctl:
#include <sys/ioctl.h>
int size;
ioctl(sockfd, FIONREAD, &size);
Alternativamente, puede utilizar los MSG_PEEK
y MSG_TRUNC
de la llamada recv()
para obtener el tamaño del datagrama en espera.
ssize_t size = recv(sockfd, buf, len, MSG_PEEK | MSG_TRUNC);
Entonces puedes simplemente malloc()
el buffer y recuperar el datagrama.
Para protocolos de transmisión como TCP, puede configurar su búfer a cualquier tamaño. Dicho esto, se recomiendan los valores comunes que son potencias de 2 como 4096 u 8192.
Si hay más datos, entonces cuál es su buffer, simplemente se guardará en el kernel para su próxima llamada a recv
.
Sí, puedes seguir creciendo tu buffer. Puede hacer una recv en el medio del búfer que comienza en offset idx
, usted haría:
recv(socket, recv_buffer + idx, recv_buffer_size - idx, 0);
Si tiene un socket SOCK_STREAM
, recv
solo obtiene "hasta los primeros 3000 bytes" de la transmisión. No hay una guía clara sobre qué tan grande es hacer el buffer: la única vez que sabes qué tan grande es una secuencia, es cuando todo está hecho ;-).
Si tiene un socket SOCK_DGRAM
y el datagrama es más grande que el búfer, recv
llena el búfer con la primera parte del datagrama, devuelve -1 y establece errno en EMSGSIZE. Desafortunadamente, si el protocolo es UDP, esto significa que el resto del datagrama se pierde, parte de por qué el UDP se llama un protocolo no confiable (sé que hay protocolos de datagramas confiables pero no son muy populares, no pude nombre uno en la familia TCP / IP, a pesar de conocer bastante bien este último ;-).
Para hacer crecer un búfer dinámicamente, asigne inicialmente con malloc
y use realloc
según sea necesario. Pero eso no te ayudará con el recv
de una fuente UDP, ¡ay!