Mit send
in den Socket schreiben schlägt fehl und sagt mir, dass ich eine Adresse angeben muss.
Es schlägt fehl, weil die send()
Funktion kann nur auf verbunden verwendet werden Steckdosen (wie hier angegeben). Normalerweise würden Sie send()
verwenden für TCP-Kommunikation (verbindungsorientiert) und sendto()
kann verwendet werden, um UDP-Datagramme (verbindungslos) zu senden.
Da Sie eindeutig verbindungslose "Ping"-Pakete oder besser ICMP-Datagramme versenden möchten, müssen Sie den sendto()
verwenden Funktion.
Es scheint keines der zusätzlichen Felder in sendto
zu geben sind eigentlich weitgehend gewöhnt. Gibt es also einen technischen Grund, warum ich sendto
verwenden muss? statt send
oder ist es nur ein Versehen in der API?
Kurze Antwort:
Wenn Sie send()
nicht verwenden dürfen , dann bleibt nur noch eine Option namens sendto()
übrig .
Lange Antwort:
Es ist nicht nur ein Versehen in der API. Wenn Sie ein UDP-Datagramm über einen gewöhnlichen Socket senden möchten (z. B. SOCK_DGRAM
), sendto()
benötigt die Informationen über die Zieladresse und den Port, die Sie in der Struktur sockaddr_in
angegeben haben , Rechts? Der Kernel fügt diese Informationen in den resultierenden IP-Header ein, da die Struktur sockaddr_in
ist die einzige Ort, an dem Sie angegeben haben, wer der Empfänger sein wird. Oder anders gesagt:In diesem Fall muss der Kernel die Zielinformationen aus Ihrer Struktur nehmen, da Sie keinen zusätzlichen IP-Header bereitstellen.
Weil sendto()
nicht nur für UDP, sondern auch für Raw-Sockets verwendet wird, muss es eine mehr oder weniger "generische" Funktion sein, die alle verschiedenen Anwendungsfälle abdecken kann, auch wenn einige Parameter wie die Portnummer am Ende nicht relevant/verwendet werden.
Zum Beispiel durch Verwendung von IPPROTO_RAW
(was automatisch IP_HDRINCL
impliziert ) zeigen Sie Ihre Absicht, den IP-Header selbst erstellen zu wollen. Also die letzten beiden Argumente von sendto()
sind eigentlich redundante Informationen, da sie bereits in dem Datenpuffer enthalten sind, den Sie an sendto()
übergeben als zweites Argument. Beachten Sie dies, auch wenn Sie IP_HDRINCL
verwenden Mit Ihrem Raw-Socket füllt der Kernel die Quelladresse und die Prüfsumme Ihres IP-Datagramms aus, wenn Sie die entsprechenden Felder auf 0
setzen .
Wenn Sie Ihr eigenes Ping-Programm schreiben möchten, können Sie auch das letzte Argument in Ihrem socket()
ändern Funktion von IPPROTO_RAW
bis IPPROTO_ICMP
und lassen Sie den Kernel den IP-Header für Sie erstellen, damit Sie sich um eine Sache weniger Sorgen machen müssen. Jetzt können Sie leicht sehen, wie die beiden sendto()
-Parameter *dest_addr
und addrlen
wieder an Bedeutung gewinnen, weil es der einzige Ort ist, an dem Sie eine Zieladresse angeben.
Die Sprache und die APIs sind sehr alt und mit der Zeit gewachsen. Einige APIs können aus heutiger Sicht seltsam aussehen, aber Sie können die alten Schnittstellen nicht ändern, ohne eine große Menge vorhandenen Codes zu beschädigen. Manchmal muss man sich einfach an Dinge gewöhnen, die vor vielen Jahren oder Jahrzehnten definiert/entworfen wurden.
Ich hoffe, das beantwortet Ihre Frage.
Der send()
Aufruf wird verwendet, wenn sich die Sockets in einem TCP SOCK_STREAM
befinden verbundener Zustand.
Aus der Manpage:
Der send()-Aufruf darf nur verwendet werden, wenn sich der Socket in einem verbundenen Zustand befindet (so dass der beabsichtigte Empfänger bekannt ist).
Da sich Ihre Anwendung offensichtlich mit keinem anderen Socket verbindet, können wir send()
nicht erwarten zu arbeiten.