Ecrire sur le socket avec
send
échoue, me disant que je dois spécifier une adresse.
Cela échoue, car le send()
la fonction ne peut être utilisée que sur connecté prises (comme indiqué ici). Habituellement, vous utiliseriez send()
pour la communication TCP (orientée connexion) et sendto()
peut être utilisé pour envoyer des datagrammes UDP (sans connexion).
Puisque vous voulez envoyer des paquets "ping", ou plus exactement des datagrammes ICMP, qui sont clairement sans connexion, vous devez utiliser le sendto()
fonction.
Il semble qu'aucun des champs supplémentaires dans
sendto
sont en fait largement utilisés. Donc, y a-t-il une raison technique pour laquelle je dois utilisersendto
au lieu desend
ou est-ce juste un oubli dans l'API ?
Réponse courte :
Lorsque vous n'êtes pas autorisé à utiliser send()
, alors il ne reste qu'une seule option, appelée sendto()
.
Réponse longue :
Ce n'est pas seulement un oubli dans l'API. Si vous souhaitez envoyer un datagramme UDP en utilisant un socket ordinaire (par exemple SOCK_DGRAM
), sendto()
a besoin des informations sur l'adresse et le port de destination, que vous avez fournies dans la structure sockaddr_in
, droit? Le noyau insérera ces informations dans l'en-tête IP résultant, puisque la structure sockaddr_in
est le seul endroit où vous avez spécifié qui sera le destinataire. Ou en d'autres termes :dans ce cas, le noyau doit prendre les informations de destination de votre structure car vous ne fournissez pas d'en-tête IP supplémentaire.
Parce que sendto()
n'est pas seulement utilisé pour UDP mais aussi pour les sockets bruts, il doit s'agir d'une fonction plus ou moins "générique" qui peut couvrir tous les différents cas d'utilisation, même lorsque certains paramètres comme le numéro de port ne sont finalement pas pertinents/utilisés.
Par exemple, en utilisant IPPROTO_RAW
(ce qui implique automatiquement IP_HDRINCL
), vous montrez votre intention de créer vous-même l'en-tête IP. Ainsi les deux derniers arguments de sendto()
sont en fait des informations redondantes, car elles sont déjà incluses dans le tampon de données que vous transmettez à sendto()
comme deuxième argument. Notez que, même lorsque vous utilisez IP_HDRINCL
avec votre socket brut, le noyau remplira l'adresse source et la somme de contrôle de votre datagramme IP si vous définissez les champs correspondants à 0
.
Si vous souhaitez écrire votre propre programme ping, vous pouvez également modifier le dernier argument de votre socket()
fonction de IPPROTO_RAW
à IPPROTO_ICMP
et laissez le noyau créer l'en-tête IP pour vous, vous avez donc une chose de moins à vous soucier. Maintenant, vous pouvez facilement voir comment les deux sendto()
-paramètres *dest_addr
et addrlen
redeviennent significatifs car c'est le seul endroit où vous fournissez une adresse de destination.
Le langage et les API sont très anciens et ont évolué au fil du temps. Certaines API peuvent sembler étranges du point de vue d'aujourd'hui, mais vous ne pouvez pas modifier les anciennes interfaces sans casser une énorme quantité de code existant. Parfois, il suffit de s'habituer à des choses qui ont été définies/conçues il y a plusieurs années ou décennies.
J'espère que cela répond à votre question.
Le send()
call est utilisé lorsque les sockets sont dans un TCP SOCK_STREAM
état connecté.
À partir de la page de manuel :
l'appel send() ne peut être utilisé que lorsque le socket est dans un état connecté (afin que le destinataire prévu soit connu).
Étant donné que votre application ne se connecte évidemment à aucune autre socket, nous ne pouvons pas nous attendre à send()
travailler.