Escribiendo en el socket con send
falla, diciéndome que necesito especificar una dirección.
Falla, porque el send()
La función solo se puede usar en conectado enchufes (como se indica aquí). Por lo general, usaría send()
para comunicación TCP (orientada a conexión) y sendto()
se puede utilizar para enviar datagramas UDP (sin conexión).
Dado que desea enviar paquetes "ping", o más correctamente, datagramas ICMP, que claramente no tienen conexión, debe usar el sendto()
función.
Parece que ninguno de los campos adicionales en sendto
en realidad se utilizan en gran medida. Entonces, ¿hay alguna razón técnica por la que deba usar sendto
? en lugar de send
¿o es solo un descuido en la API?
Respuesta corta:
Cuando no se le permite usar send()
, entonces solo queda una opción, llamada sendto()
.
Respuesta larga:
No es solo un descuido en la API. Si desea enviar un datagrama UDP utilizando un socket ordinario (por ejemplo, SOCK_DGRAM
), sendto()
necesita la información sobre la dirección y el puerto de destino, que proporcionó en la estructura sockaddr_in
, ¿Correcto? El kernel insertará esa información en el encabezado IP resultante, ya que la estructura sockaddr_in
es el único lugar donde especificó quién será el receptor. O en otras palabras:en este caso, el núcleo tiene que tomar la información de destino de su estructura ya que no proporciona un encabezado de IP adicional.
Porque sendto()
no solo se usa para UDP sino también para sockets sin procesar, tiene que ser una función más o menos "genérica" que pueda cubrir todos los diferentes casos de uso, incluso cuando algunos parámetros como el número de puerto no son relevantes/usados al final.
Por ejemplo, usando IPPROTO_RAW
(lo que automáticamente implica IP_HDRINCL
), muestra su intención de que desea crear el encabezado IP por su cuenta. Así, los dos últimos argumentos de sendto()
en realidad son información redundante, porque ya están incluidos en el búfer de datos que pasa a sendto()
como segundo argumento. Tenga en cuenta que, incluso cuando usa IP_HDRINCL
con su socket sin procesar, el kernel completará la dirección de origen y la suma de verificación de su datagrama IP si establece los campos correspondientes en 0
.
Si desea escribir su propio programa de ping, también puede cambiar el último argumento en su socket()
función de IPPROTO_RAW
a IPPROTO_ICMP
y deje que el kernel cree el encabezado IP por usted, para que tenga una cosa menos de qué preocuparse. Ahora puedes ver fácilmente cómo los dos sendto()
-parámetros *dest_addr
y addrlen
vuelve a ser significativo porque es el único lugar donde proporciona una dirección de destino.
El lenguaje y las API son muy antiguos y han crecido con el tiempo. Algunas API pueden parecer extrañas desde la perspectiva actual, pero no puede cambiar las interfaces antiguas sin romper una gran cantidad de código existente. A veces solo tienes que acostumbrarte a cosas que se definieron/diseñaron hace muchos años o décadas.
Espero que eso responda a tu pregunta.
El send()
la llamada se usa cuando los sockets están en un TCP SOCK_STREAM
estado conectado.
Desde la página del manual:
la llamada send() solo se puede usar cuando el socket está en un estado conectado (para que se conozca el destinatario deseado).
Dado que su aplicación obviamente no se conecta con ningún otro socket, no podemos esperar send()
para trabajar.