Question

Je suis un peu confus au sujet de la programmation socket en C.

Vous créez un socket, le liez à une interface et à une adresse IP et le faites écouter.J'ai trouvé quelques ressources Web à ce sujet et je l'ai bien compris.J'ai notamment trouvé un article Programmation réseau sous systèmes Unix être très instructif.

Ce qui me rend confus, c'est le moment où les données arrivent sur le socket.

Comment savoir quand les paquets arrivent et quelle est leur taille ? Devez-vous faire tout le gros du travail vous-même ?

Mon hypothèse de base ici est que les paquets peuvent être de longueur variable, donc une fois que les données binaires commencent à apparaître sur le socket, comment commencer à construire des paquets à partir de cela ?

Était-ce utile?

La solution

La réponse courte est que vous devez faire tout le gros du travail vous-même.Vous pouvez être averti qu'il y a des données disponibles à lire, mais vous ne saurez pas combien d'octets sont disponibles.Dans la plupart des protocoles IP qui utilisent des paquets de longueur variable, un en-tête avec une longueur fixe connue sera ajouté au paquet.Cet en-tête contiendra la longueur du paquet.Vous lisez l'en-tête, obtenez la longueur du paquet, puis lisez le paquet.Vous répétez ce modèle (lire l'en-tête, puis lire le paquet) jusqu'à ce que la communication soit terminée.

Lors de la lecture de données depuis un socket, vous demandez un certain nombre d'octets.L'appel de lecture peut se bloquer jusqu'à ce que le nombre d'octets demandé soit lu, mais il peut renvoyer moins d'octets que ce qui a été demandé.Lorsque cela se produit, vous réessayez simplement la lecture, en demandant les octets restants.

Voici une fonction C typique pour lire un nombre défini d'octets à partir d'un socket :

/* buffer points to memory block that is bigger than the number of bytes to be read */
/* socket is open socket that is connected to a sender */
/* bytesToRead is the number of bytes expected from the sender */
/* bytesRead is a pointer to a integer variable that will hold the number of bytes */
/*           actually received from the sender. */
/* The function returns either the number of bytes read, */
/*                             0 if the socket was closed by the sender, and */
/*                            -1 if an error occurred while reading from the socket */
int readBytes(int socket, char *buffer, int bytesToRead, int *bytesRead)
{
    *bytesRead = 0;
    while(*bytesRead < bytesToRead)
    {
        int ret = read(socket, buffer + *bytesRead, bytesToRead - *bytesRead);
        if(ret <= 0)
        {
           /* either connection was closed or an error occurred */
           return ret;
        }
        else
        {
           *bytesRead += ret;
        }
    }
    return *bytesRead;
}

Autres conseils

Ainsi, la réponse à votre question dépend en grande partie du fait que vous utilisez UDP ou TCP comme moyen de transport.

Pour UDP, la vie devient beaucoup plus simple, dans la mesure où vous pouvez appeler recv/recvfrom/recvmsg avec la taille de paquet dont vous avez besoin (de toute façon, vous enverriez probablement des paquets de longueur fixe depuis la source) et supposez que si les données sont disponibles , il est présent en multiples de tailles de paquet.(C'EST À DIRE.Vous appelez recv* avec la taille de votre paquet côté envoi, et vous êtes prêt.)

Pour TCP, la vie devient un peu plus intéressante - pour les besoins de cette explication, je suppose que vous savez déjà comment utiliser socket(), bind(), Listen() et accept() - cette dernière étant la façon dont vous obtenez le descripteur de fichier (FD) de votre nouvelle connexion.

Il existe deux façons d'effectuer les E/S pour un blocage de socket, dans lesquelles vous appelez read(fd, buf, N) et la lecture reste là et attend que vous ayez lu N octets dans buf - ou non bloquant, dans lequel vous devez vérifier (en utilisant select() ou poll()) si le FD est lisible, puis faire votre read().

Lorsqu'il s'agit de connexions basées sur TCP, le système d'exploitation ne prête pas attention à la taille des paquets, car il est considéré comme un flux continu de données et non comme des morceaux séparés de la taille d'un paquet.

Si votre application utilise des "paquets" (structures de données compressées ou décompressées que vous transmettez), vous devriez pouvoir appeler read() avec l'argument de taille appropriée et lire une structure de données entière hors du socket à la fois.La seule mise en garde à laquelle vous devez faire face est de vous rappeler de trier correctement les octets de toutes les données que vous envoyez, au cas où les systèmes source et de destination auraient un caractère endian d'octets différent.Cela s'applique à la fois à UDP et à TCP.

En ce qui concerne la programmation des sockets *NIX, je recommande fortement W."Programmation réseau Unix, Vol.1" (UNPv1) et "Programmation avancée dans un environnement Unix" (APUE).Le premier est un ouvrage sur la programmation basée sur le réseau, quel que soit le transport, et le second est un bon livre de programmation complet car il s'applique à la programmation basée sur *NIX.Recherchez également "TCP/IP Illustrated", volumes 1 et 2.

Lorsque vous effectuez une lecture sur le socket, vous lui indiquez le nombre maximum d'octets à lire, mais s'il n'en a pas autant, il vous en donne le nombre maximum.C'est à vous de concevoir le protocole afin que vous sachiez si vous disposez d'un paquet partiel ou non.Par exemple, dans le passé, lors de l'envoi de données binaires de longueur variable, je mettais un int au début indiquant le nombre d'octets attendus.Je ferais une lecture demandant un nombre d'octets supérieur au plus grand paquet possible dans mon protocole, puis je comparerais le premier entier au nombre d'octets que j'avais reçu, et je le traiterais ou j'essaierais plus de lectures jusqu'à ce que je ' J'ai reçu le paquet complet, ça dépend.

Les sockets fonctionnent à un niveau plus élevé que les paquets bruts - c'est comme un fichier à partir duquel vous pouvez lire/écrire.De plus, lorsque vous essayez de lire à partir d'un socket, le système d'exploitation bloquera (mettra en attente) votre processus jusqu'à ce qu'il dispose des données nécessaires pour répondre à la demande.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top