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 búfer 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 esperaría (3000 probablemente esté bien). Si su protocolo está transfiriendo datos masivos, los búferes más grandes pueden ser más eficientes: una buena regla general es casi la misma que el tamaño del búfer de recepción del núcleo del zócalo (a menudo alrededor de 256kB).
SOCK_DGRAM
: Utilice un búfer lo suficientemente grande como para contener el paquete más grande que envíe su protocolo de nivel de aplicación. 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 deberán fragmentarse y volverse a ensamblar.
¿Qué sucede si recv
un paquete es más grande que el búfer?
SOCK_STREAM
: La pregunta realmente no tiene sentido, ya que 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 tiene espacio para el búfer, el sistema operativo los pondrá en cola y estarán disponibles para su próxima llamada recv
.
SOCK_DGRAM
: Los bytes sobrantes se descartan.
¿Cómo puedo saber si he recibido el mensaje completo?
SOCK_STREAM
: Debe crear alguna forma de determinar el final del mensaje en su protocolo de nivel de aplicación. Por lo general, se trata de un prefijo de longitud (que comienza 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. También son posibles combinaciones de estas opciones, por ejemplo, un encabezado de tamaño fijo que incluye un valor de longitud.
SOCK_DGRAM
: Una sola recv
llamada siempre devuelve un solo datagrama.
¿Hay alguna manera de hacer que un búfer no tenga una cantidad fija de espacio, de modo que pueda seguir agregándolo sin temor a quedarse 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).
recv
en el búfer después del mensaje parcial. No debe usarstrstr()
en el búfer sin procesar llenorecv()
; no hay garantía de que contenga un terminador nulo, por lo que podría provocarstrstr()
un bloqueo.recv()
que escriba más bytes de los que quedan.Para protocolos de transmisión como TCP, puede configurar su búfer en cualquier tamaño. Dicho esto, se recomiendan valores comunes que son potencias de 2, como 4096 u 8192.
Si hay más datos, entonces cuál es su búfer, simplemente se guardará en el núcleo para su próxima llamada
recv
.Sí, puedes seguir haciendo crecer tu búfer. Puede hacer una recepción en el medio del búfer comenzando en el desplazamiento
idx
, haría:fuente
Si tienes un
SOCK_STREAM
enchufe,recv
solo obtiene "hasta los primeros 3000 bytes" de la transmisión. No hay una guía clara sobre qué tan grande hacer el búfer: la única vez que sabes qué tan grande es una transmisión, es cuando todo está hecho ;-).Si tiene un
SOCK_DGRAM
socket 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é UDP se llama no confiable protocolo (sé que hay protocolos de datagramas confiables pero no son muy populares; no pude nombrar uno en la familia TCP / IP, a pesar de conocer a este último bastante bien ;-).Para hacer crecer un búfer de forma dinámica, asígnelo inicialmente
malloc
y úselorealloc
según sea necesario. Pero eso no lo ayudarárecv
desde una fuente UDP, por desgracia.fuente
Para el
SOCK_STREAM
zócalo, el tamaño del búfer realmente no importa, porque solo está extrayendo algunos de los bytes en espera y puede recuperar más en una próxima llamada. Simplemente elija el tamaño de búfer que pueda pagar.Para el
SOCK_DGRAM
zócalo, obtendrá la parte adecuada del mensaje de espera y el resto se descartará. Puede obtener el tamaño del datagrama de espera con el siguiente ioctl:Alternativamente, puede usar los indicadores
MSG_PEEK
yMSG_TRUNC
de larecv()
llamada para obtener el tamaño del datagrama en espera.Es necesario
MSG_PEEK
que peek (no recibir) el mensaje en espera - regresa recv lo real, y no truncada tamaño; y no necesitaMSG_TRUNC
desbordar su búfer actual.Entonces puede simplemente
malloc(size)
el búfer real y elrecv()
datagrama.fuente
malloc()
un búfer de 64 KB, ¿MSG_TRUNC
es innecesario?SOCK_DGRAM
no es solo UDP.No hay una respuesta absoluta a su pregunta, porque la tecnología siempre está destinada a ser específica de la implementación. 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 (incluido el encabezado) para UDP puede variar de 8 a 65 515 bytes. Por lo tanto, el tamaño a prueba de fallas para el búfer entrante es 65 507 bytes (~ 64 KB)
Sin embargo, no todos los paquetes grandes pueden ser enrutados adecuadamente 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 UDP seguro más grande en Internet?
fuente
16kb es lo correcto; Si está utilizando Gigabit Ethernet, cada paquete puede tener un tamaño de 9 kb.
fuente