Programmation des sockets sous Linux: les bases de la programmation réseau introduisent certaines fonctions de framework dans la programmation des sockets. Il peut garantir que les données du réseau peuvent atteindre l'utilisateur normalement. Ce blog explique principalement l'interaction des données de communication du réseau, c'est-à-dire la transmission et la réception des données du réseau et le modèle IO.
Fonction IO utilisée dans la communication réseau
Les fonctions d'E / S utilisées dans la communication réseau sont: recv () / send (), readv () / writev (), recvmsg () / sendmsg (), read () / write (), recvfrom () / sendto (). Parmi eux, la lecture / écriture est l'interface de fonctionnement commune de tous les fichiers, qui ne sera pas expliquée ici. Recvfrom / sendto a été expliqué dans l'article précédent, et il n'y a pas trop d'introduction ici.
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
#include <sys/uio.h>
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
Le processus de réception et d'envoi est le même, la différence entre eux est principalement la quantité de données envoyées à la fois et la possibilité pour eux de choisir les informations de l'autre partie (informations de structure d'adresse, telles que IP, port, etc.). Les différences sont les suivantes:
- Lecture / écriture et lecture / écriture peuvent être utilisés pour tous les descripteurs de fichiers, recvfrom / sendto, recv / send, recvmsg / sendmsg ne peuvent utiliser que des descripteurs de socket.
- readv / writev et recvmsg / sendmsg peuvent utiliser plusieurs tampons, lecture / écriture, recv / send, recvfrom / sendto ne peuvent utiliser qu'un seul tampon
- recvfrom / sendto, recvmsg / sendmsg peut choisir l'adresse IP de l'autre partie
- recvmsg / sendmsg possède des informations de contrôle facultatives et peut effectuer des opérations avancées
Les valeurs des drapeaux dans ces fonctions sont les suivantes, soit une valeur unique, soit une valeur composée générée au niveau du bit ou
- MSG_DONTWAIT: opération d'E / S non bloquante, retournez immédiatement, n'attendez pas. Pour une seule opération, la valeur d'indicateur du fichier n'est pas modifiée.
- MSG_ERRQUEUE: message d'erreur reçu de la file d'attente des erreurs de socket
- MSG_OOB: recevoir des données hors bande
- MSG_PEEK: afficher les données sans effacer les données dans le tampon
- MSG_TRUNC: retourne toutes les données, si le tampon est trop petit entraînera une perte de données
- MSG_WAITALL: attendez tous les messages, c'est-à-dire que les données du tampon atteindront la longueur spécifiée avant de revenir.
La valeur de retour de l'opération de réception est le nombre d'octets reçus avec succès et -1 est renvoyé pour indiquer qu'une erreur s'est produite. Lorsque l'autre partie ferme la connexion de manière normale, la valeur de retour est 0.
Fonction de réception et processus d'interaction avec le noyau: lorsque les données du tampon du noyau sont plus petites que le tampon spécifié, lorsque MSG_WAITALL n'est pas défini, toutes les données du tampon seront copiées dans le tampon utilisateur et la longueur des données sera retournée. Lorsqu'il y a plus de données dans le tampon de réception du noyau que spécifié par l'utilisateur, les données dans le tampon de réception de la longueur len spécifiée par l'utilisateur seront copiées à l'adresse spécifiée par l'utilisateur, et le reste des données doit être copié lorsque la fonction de réception est appelée la prochaine fois. Après avoir copié les données spécifiées par l'utilisateur, le noyau détruira les données copiées et fera des ajustements.
Lors de l'envoi de données, -1 est renvoyé pour indiquer une erreur. Lorsque l'autre partie ferme la connexion normalement, la valeur renvoyée est 0 et le reste est le nombre d'octets envoyés avec succès. Étant donné que les données du buf du tampon utilisateur ne sont pas nécessairement envoyées lorsqu'elles sont envoyées via la fonction d'envoi, vous devez vérifier la valeur de retour de la fonction send () pour l'opération suivante (si vous devez renvoyer les données précédentes). Lorsque le tampon d'envoi est plein, l'appel de la fonction d'envoi renvoie le type d'erreur ENOBUFS. Le type d'erreur EPIPE sera renvoyé lorsque le socket est fermé
La différence entre readv et writev et recvmsg et sendmsg
Ces fonctions peuvent être utilisées pour recevoir plusieurs données de tampon. Parmi eux, readv et writev spécifient directement le nombre de vecteurs tampon dans les paramètres, où vector est un tableau de vecteurs vectoriels. La structure des données est définie comme suit:
struct iovec {
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes to transfer */
};
En cours d'utilisation, la longueur de l'iov_base d'iovc doit être spécifiée et la valeur est stockée dans iov_len.
Les paramètres dans recvmsg et sendmsg sont plus compliqués, et struct msghdr est défini comme suit:
struct msghdr {
void *msg_name; /* optional address */
socklen_t msg_namelen; /* size of address */
struct iovec *msg_iov; /* scatter/gather array */
size_t msg_iovlen; /* # elements in msg_iov */
void *msg_control; /* ancillary data, see below */
size_t msg_controllen; /* ancillary data buffer len */
int msg_flags; /* flags (unused) */
};
Msg_name représente l'adresse source, qui est un pointeur vers une structure sockaddr, msg_iov et readv ont la même structure. msg_iovlen indique le nombre de tampons msg_iov.
Voici une brève introduction à son utilisation. S'il existe une distinction plus détaillée entre les messages de bienvenue, quelles sont les occasions d'utilisation de readv / writev et recvmsg / sendmsg?
Modèle IO
Les méthodes d'E / S incluent les E / S bloquantes, les E / S non bloquantes, le multiplexage d'E / S, la commande de signal, les E / S asynchrones, etc. Le blocage des E / S est le type d'E / S le plus courant. Lors de l'utilisation de ce modèle pour la réception de données, le programme attendra que les données n'arrivent pas. Au lieu de bloquer les E / S, le noyau ne bloquera pas toutes les demandes et reviendra immédiatement, que les données arrivent ou non.
Multiplexage IO
En utilisant le modèle de multiplexage IO, vous pouvez ajouter un délai d'attente lorsque vous attendez. Lorsque le délai d'attente n'arrive pas, cela correspond à la situation de blocage. Lorsque l'heure du supermarché arrive et qu'aucune donnée n'est reçue, elle revient et n'attend plus. Le processus est le suivant:
Les fonctions incluent les fonctions de sélection et d'interrogation.
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
Parmi eux, readfds, writefds et exceptfds sont les ensembles de descripteurs de fichiers qui nous intéressent s'ils sont lisibles, inscriptibles et anormaux. Ceux qui ne se soucient pas peuvent être définis sur NULL. Sous Linux, fd_set est un grand tableau d'octets, où chaque descripteur de fichier n'occupe qu'un seul bit (le descripteur 0 occupe le 0e bit ...). Vous pouvez utiliser FD_CLR pour effacer le bit correspondant du descripteur de fichier fd, utiliser FD_SET pour définir le bit correspondant du descripteur de fichier fd et utiliser FD_ZERO pour effacer tous les bits de l'ensemble de descripteurs de fichier. FD_ISSET est utilisé pour détecter le descripteur de fichier fd Indique s'il faut définir l'ensemble de descripteurs de fichiers . Le paramètre nfds est la valeur du plus grand descripteur de fichier dans ces trois ensembles de description de fichier plus un. Ce nombre indique en fait au noyau que nous sommes préoccupés par la plage de descripteurs. Vous pouvez utiliser FD_SETSIZE (1024), mais cela gaspillera des ressources. Le dernier paramètre est le paramètre de temporisation.
Il existe trois types de valeurs de retour de la fonction de sélection: return -1, indiquant qu'une erreur s'est produite et qu'un signal a été capturé lors de l'écoute; return 0, indiquant un délai d'attente; renvoie un nombre positif, indiquant qu'un fichier est prêt, puis utilisez FD_ISSET pour Déterminez quel fichier est prêt. Lorsque la fonction de sélection revient, le bit correspondant au descripteur de fichier fd qui ne remplit pas la condition est effacé, il doit donc être ajouté à nouveau lorsqu'il est réutilisé .
Il y a aussi une fonction poll dans io réutilisation qui peut atteindre la même fonction. Parmi eux, au lieu de construire un ensemble de descripteurs pour chaque condition, poll construit un tableau de structures pollfd. Chaque membre spécifie un numéro de descripteur et nous Décrivez les conditions d'intérêt. Le délai d'expiration est en millisecondes. La valeur de retour est la même que celle de select. Le deuxième paramètre est le nombre de pollfd.
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
Étant donné que les fonctions de sélection et d'interrogation seront affectées par le signal, vous pouvez utiliser les deux fonctions pselect et ppoll pour protéger l'influence du signal afin d'éviter les interférences de signal pendant l'exécution de la fonction.
Modèle d'E / S asynchrone
Nous sommes trop passifs dans les premiers modes io, et la réutilisation d'io nécessite également une période de temps (essentiellement une interrogation pour voir). Les E / S asynchrones peuvent être utilisées ici. Une fois que la condition qui nous tient à cœur est remplie, elle est notifiée par un signal. Il n'est pas nécessaire d'attendre trop. Le principe est le suivant:
sous Linux, le signal io asynchrone est une combinaison de SIGIO et SIGURG, où SIGIO est un signal d'E / S asynchrone général, et SIGURG est uniquement utilisé pour informer le processus que les données hors bande sur la connexion réseau sont arrivées.
Afin de recevoir le signal d'E / S asynchrone, vous devez effectuer les 3 étapes suivantes:
- Signal d'appel ou sigaction pour établir un gestionnaire de signal pour le signal SIGIO.
- Utilisez la commande F_SETOWN pour utiliser fcntl pour définir l'ID de processus ou l'ID de groupe de processus pour recevoir le signal.
- Utilisez la commande F_SETFL pour appeler fcntl pour définir l'indicateur d'état du fichier O_ASYNC L'
étape 3 ne peut être effectuée que sur le terminal ou le descripteur de réseau. L'io asynchrone a de grandes limites: ils ne peuvent pas être utilisés sur tous les types de fichiers et un seul signal peut être utilisé. Si vous souhaitez effectuer des E / S asynchrones sur plusieurs descripteurs, vous ne savez pas à quel descripteur ce signal correspond lorsque le processus reçoit le signal.