Scrittura al socket con
send
fallisce, dicendomi che devo specificare un indirizzo.
Fallisce, perché send()
la funzione può essere utilizzata solo su connesso prese (come indicato qui). Di solito useresti send()
per la comunicazione TCP (orientata alla connessione) e sendto()
può essere utilizzato per inviare datagrammi UDP (senza connessione).
Poiché si desidera inviare pacchetti "ping", o più correttamente datagrammi ICMP, che sono chiaramente senza connessione, è necessario utilizzare il sendto()
funzione.
Sembra nessuno dei campi extra in
sendto
sono effettivamente utilizzati in misura maggiore. Quindi, c'è un motivo tecnico per cui devo usaresendto
invece disend
o è solo una svista nell'API?
Risposta breve:
Quando non sei autorizzato a usare send()
, rimane solo un'opzione, chiamata sendto()
.
Risposta lunga:
Non è solo una svista nell'API. Se vuoi inviare un datagramma UDP usando un normale socket (es. SOCK_DGRAM
), sendto()
ha bisogno delle informazioni sull'indirizzo di destinazione e sulla porta, che hai fornito nella struct sockaddr_in
, Giusto? Il kernel inserirà tali informazioni nell'intestazione IP risultante, poiché la struct sockaddr_in
è l'unico luogo in cui hai specificato chi sarà il destinatario. O in altre parole:in questo caso il kernel deve prendere le informazioni di destinazione dalla tua struttura poiché non fornisci un'intestazione IP aggiuntiva.
Perché sendto()
non è utilizzato solo per UDP ma anche per i raw socket, deve essere una funzione più o meno "generica" in grado di coprire tutti i diversi casi d'uso, anche quando alcuni parametri come il numero di porta non sono rilevanti/utilizzati alla fine.
Ad esempio, utilizzando IPPROTO_RAW
(che implica automaticamente IP_HDRINCL
), mostri la tua intenzione di voler creare l'intestazione IP da solo. Così gli ultimi due argomenti di sendto()
sono in realtà informazioni ridondanti, perché sono già incluse nel buffer di dati che passi a sendto()
come secondo argomento. Nota che, anche quando usi IP_HDRINCL
con il tuo raw socket, il kernel compilerà l'indirizzo sorgente e il checksum del tuo datagramma IP se imposti i campi corrispondenti a 0
.
Se vuoi scrivere il tuo programma ping, puoi anche cambiare l'ultimo argomento nel tuo socket()
funzione da IPPROTO_RAW
a IPPROTO_ICMP
e lascia che il kernel crei l'intestazione IP per te, così hai una cosa in meno di cui preoccuparti. Ora puoi facilmente vedere come i due sendto()
-parametri *dest_addr
e addrlen
diventa di nuovo significativo perché è l'unico posto in cui fornisci un indirizzo di destinazione.
Il linguaggio e le API sono molto vecchi e sono cresciuti nel tempo. Alcune API possono sembrare strane dal punto di vista odierno, ma non è possibile modificare le vecchie interfacce senza interrompere un'enorme quantità di codice esistente. A volte devi solo abituarti a cose che sono state definite/progettate molti anni o decenni fa.
Spero di aver risposto alla tua domanda.
Il send()
chiamata è usata quando i socket sono in un SOCK_STREAM
TCP stato connesso.
Dalla pagina man:
la chiamata send() può essere utilizzata solo quando il socket è in uno stato connesso (in modo che il destinatario previsto sia noto).
Poiché la tua applicazione ovviamente non si connette a nessun altro socket, non possiamo aspettarci send()
lavorare.