当前位置:网站首页>Introduction de l'API commune de programmation de socket et mise en œuvre de socket, select, Poll et epoll

Introduction de l'API commune de programmation de socket et mise en œuvre de socket, select, Poll et epoll

2022-07-07 18:11:00 Cheems

Préface

   Cet article vise à apprendre socket Le contenu du bloc de programmation réseau ,epollC'est la priorité., Les articles suivants sont écrits reactor Le modèle est basé sur epollAu - dessus.

   Les points de connaissance de cette colonne sont passés par L'éducation au son zéro Pour apprendre en classe , Faites un résumé et écrivez l'article ,C'est exact.c/c++linux Lecteurs intéressés par le cours ,Vous pouvez cliquer sur le lien C/C++ Introduction au cours avancé sur les serveurs de fond Services pour une vue détaillée des cours .

socketProgrammation

socketIntroduction

   Les communications inter - processus traditionnelles sont fournies par le noyau IPCLe mécanisme fonctionne, Mais seulement pour la communication Native , Pour communiquer entre eux , Doit utiliser la communication réseau ( Essentiellement avec le noyau -Le noyau fournitsocket Le mécanisme des faux fichiers permet la communication ---- En fait, en utilisant des descripteurs de fichiers ), Cela nécessite l'utilisation du noyau fourni à l'utilisateur socket APIBibliothèque de fonctions.

  UtilisersocketVa créer unsocket pair,Comme le montre la figure ci - dessous:, Un descripteur de fichier manipule deux tampons .

Insérer la description de l'image ici

UtilisersocketDeAPI Fonctions pour écrire des programmes côté serveur et côté client

Insérer la description de l'image ici

Connaissances préparatoires

Ordre des octets réseau

  Ordre des octets réseau: Concepts de grande et de petite extrémité

  • Grande extrémité: L'adresse de bas niveau stocke les données de haut niveau, Les adresses de haut niveau stockent les données de bas niveau
  • Petit bout: L'adresse de bas niveau stocke les données de bas niveau , L'adresse de haut niveau stocke les données de haut niveau

   Utilisation de grandes et de petites extrémités : Dans le réseau, il est souvent nécessaire de considérer le grand et le petit terminal est IPEt les ports. La transmission réseau est à grande échelle , L'ordinateur utilise une petite extrémité , Il faut donc convertir la taille

  En bas4 Les fonctions sont celles qui font la conversion de taille , Nom de la fonction hReprésente l'hôtehost, nReprésente le réseaunetwork, sReprésentationshort, lReprésentationlong.

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

Les fonctions ci - dessus , Si vous n'aviez pas besoin d'une fonction de conversion interne, vous ne feriez pas de conversion .

IPFonction de conversion d'adresse

  IPFonction de conversion d'adresse

int inet_pton(int af, const char *src, void *dst);
  • p-> Forme de chaîne représentant la décimale pointillée
  • to->À
  • n->ReprésentationnetworkRéseau

Description de la fonction: Décimale pointillée sous forme de chaîne IP Réseau converti en mode grand terminal IP(Remodelage4Nombre d'octets)
Description des paramètres:

  • af: AF_INET
  • src: Point décimal sous forme de chaîne IPAdresse
  • dst: L'adresse où la variable convertie est stockée
  • Par exempleinet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);

   On peut aussi calculer manuellement : Par exemple:192.168.232.145, D'abord.4 Nombre positif converti en 16Nombre décimal,
   192—>0xC0   168—>0xA8    232—>0xE8    145—>0x91
   Enfin, dans l'ordre des grands octets : 0x91E8A8C0, C'est4 Valeurs de forme des octets .
  

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

Description de la fonction: RéseauIP Convertir en chaîne décimale pointillée IP
Description des paramètres:

  • af: AF_INET
  • src: La forme du réseau IPAdresse
  • dst: Après conversionIPAdresse, Généralement un tableau de chaînes
  • size: dstLongueur

Valeur de retour:

  • Succès–Retour à l'exécutiondstPointeur vers
  • Échec–RetourNULL, Et la mise en placeerrno

  Par exemple: IPL'adresse est010aa8c0, Conversion au format décimal point :
  01---->1    0a---->10   a8---->168    c0---->192
   Parce que depuis le réseau IP L'adresse est en mode haut de gamme , Donc la conversion en décimale dot devrait être : 192.168.10.1

struct sockaddr

socketStructures importantes pour la programmation:struct sockaddr
Insérer la description de l'image ici

//struct sockaddrDescription de la structure:
struct sockaddr {
    
     sa_family_t sa_family;
     char     sa_data[14];
}

//struct sockaddr_inStructure:
struct sockaddr_in {
    
     sa_family_t    sin_family; /* address family: AF_INET */
     in_port_t      sin_port;   /* port in network byte order */
     struct in_addr sin_addr;   /* internet address */
};

/* Internet address. */
struct in_addr {
    
      uint32_t  s_addr;     /* address in network byte order */
};	 //Ordre des octets réseauIP--Mode grand terminal

Adoptionman 7 ip Vous pouvez voir les instructions

PrincipalAPIDescription de la fonction

socket

int socket(int domain, int type, int protocol);

Description de la fonction: Créationsocket

Description des paramètres:

  • domain: Version du Protocole
- - AF_INET IPV4
- - AF_INET6 IPV6
- - AF_UNIX AF_LOCAL La prise locale utilise 
  • type:Type de protocole
- - SOCK_STREAM Type de flux, Le Protocole par défaut estTCPAccord
- - SOCK_DGRAM   Type de rapport , Par défautUDPAccord
  • protocal:
- - Remplissage général0,  Indique que le Protocole par défaut du type correspondant est utilisé .
  • Valeur de retour:
- - Succès: Renvoie un plus grand que0Descripteur de fichier pour
- - Échec: Retour-1, Et la mise en placeerrno

  Quand on appellesocketAprès la fonction, Renvoie un descripteur de fichier, Le noyau fournit un tampon de lecture et d'écriture correspondant au descripteur de fichier, Il y a deux files d'attente en même temps, File d'attente de connexion demandée et file d'attente connectée( Pour écouter les descripteurs de fichiers ,listenFd)

Insérer la description de l'image ici

bind

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

Description de la fonction: Oui.socketDescripteurs de fichiers etIP,PORTBIND

Description des paramètres:

  • socket: AppelezsocketDescripteur de fichier retourné par la fonction
  • addr: Du serveur local IPAdresse etPORT,
struct sockaddr_in serv;
serv.sin_family = AF_INET;
serv.sin_port = htons(8888);
//serv.sin_addr.s_addr = htonl(INADDR_ANY);
//INADDR_ANY: Indique l'utilisation de toute disponibilité valide de cet ordinateurIP
inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
  • addrlen: addr Taille de la mémoire utilisée par la variable

Valeur de retour:

  • Succès: Retour0
  • Échec: Retour-1, Et la mise en placeerrno

listen

int listen(int sockfd, int backlog);

Description de la fonction: Changez la prise de la dynamique primaire à la dynamique par

Description des paramètres:

  • sockfd: AppelezsocketDescripteur de fichier retourné par la fonction
  • backlog: InlinuxDans le système, Ceci représente la file d'attente de connexion complète ( File d'attente connectée )Nombre de.InunixType de système, Ceci représente la file d'attente de connexion complète ( File d'attente connectée )+ File d'attente semi - connectée( Demande de connexion à la file d'attente )Total

Valeur de retour:

  • Succès: Retour0
  • Échec: Retour-1, Et la mise en placeerrno

accept

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);	

Description de la fonction:Obtenir une connexion, Si aucune connexion n'est actuellement disponible, l'attente sera bloquée.

Paramètre de fonction:

  • sockfd: AppelezsocketDescripteur de fichier retourné par la fonction
  • addr: Paramètres sortants, Enregistrer l'adresse du client
  • addrlen: Paramètres entrants et sortants, addrTaille de la mémoire occupée par la variable

Valeur de retour:

  • Succès: Renvoie un nouveau descripteur de fichier,Pour communiquer avec les clients
  • Échec: Retour-1, Et la mise en placeerrnoValeur.

  acceptLa fonction est une fonction de blocage, S'il n'y a pas de nouvelle demande de connexion, Est toujours bloqué.
  Obtenir une nouvelle connexion de la file d'attente connectée, .Et obtenir un nouveau descripteur de fichier, Ce descripteur de fichier est utilisé pour communiquer avec le client. (Le noyau est responsable d'amener les connexions dans la file d'attente de demande dans la file d'attente connectée)

connect

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

Description de la fonction: Connexion au serveur

Paramètre de fonction:

  • sockfd: AppelezsocketDescripteur de fichier retourné par la fonction
  • addr: Adresse du serveur
  • addrlen: addr Taille de la mémoire de la variable

Valeur de retour:

  • Succès: Retour0
  • Échec: Retour-1, Et la mise en placeerrnoValeur

Lire et envoyer des données

  Ensuite, vous pouvez utiliserwriteEtread La fonction lit et écrit .En plus d'utiliserread/writeEn dehors de la fonction, Peut également être utilisérecvEtsendFonctions.

ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);	
//Correspondant àrecvEtsendCes deux fonctionsflagsRemplissez directement0C'est bon

  Attention!: Si le tampon d'écriture est plein, writeÇa bloque aussi, readEn lisant l'opération, L'absence de données dans le tampon de lecture peut causer un blocage .

Modèle de serveur hautement simultané-select

selectIntroduction

  MulticanalIOTechnique: select, Écouter plusieurs descripteurs de fichiers en même temps, Laisser les opérations surveillées au noyau pour traitement

int select(int nfds, fd_set * readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

Type de donnéesfd_set::Collection de descripteurs de fichiers—— L'essence est bitmap

Description de la fonction: Le noyau délégué surveille la lecture du descripteur de fichier,Écrire ou faire une erreur

Description des paramètres:

  • nfds: Le plus grand descripteur de fichier+1
  • readfds: Lire la collection, Est un paramètre entrant et sortant
		Entrée: Indique au noyau quels descripteurs de fichiers doivent être surveillés
		Sortie: Le noyau indique à l'application quels descripteurs de fichiers ont changé
  • writefds: Écrire une collection de descripteurs de fichiers(Paramètres entrants et sortants,Ibid.)
  • execptfds: Collection de descripteurs de fichiers d'exception(Paramètres entrants et sortants,Ibid.)
  • timeout:
		NULL--Indique un blocage permanent, Jusqu'à ce qu'un incident se produise
		0   --Indique qu'il n'y a pas de blocage, Reviens tout de suite., Qu'un événement surveillé se produise ou non
		>0  --Retour à l'événement spécifié ou si un événement s'est produit
  • Valeur de retour: Nombre de descripteurs de fichiers modifiés retournés avec succès.Échec retourné-1, Et la mise en placeerrnoValeur.

select-api

Oui.fdDeset Effacer dans la collection

void FD_CLR(int fd, fd_set *set);

Description de la fonction: JugementfdDans la collection
Valeur de retour: SifdInsetEnsemble, Retour1, Sinon, retournez à0

int FD_ISSET(int fd, fd_set *set);

Oui.fdSet tosetEnsemble

void FD_SET(int fd, fd_set *set);

InitialisationsetEnsemble

void FD_ZERO(fd_set *set);

Avecselect La fonction est en fait de confier au noyau la tâche de nous aider à détecter quels descripteurs de fichiers ont des données lisibles ,Writable,Erreur survenue

int select(int nfds, fd_set * readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

selectAvantages et inconvénients

selectAvantages:

  1. selectSupport multiplateforme

selectInconvénients:

  1. Difficultés d'écriture du Code
  2. Il s'agit d'une copie aller - retour de la zone utilisateur à la zone du noyau
  3. Lorsque le client a plusieurs connexions, Mais quelques cas actifs, selectMoins efficace(Par exemple: Dans un cas extrême, 3-1023Tous les descripteurs de fichiers sont ouverts, Mais seulement1023Il y a des données envoyées, selectC'est inefficace)
  4. Soutien maximal1024Connexions client(selectSoutien maximal1024Les connexions client ne sont pas supportées par des tables de descripteurs de fichiers jusqu'à1024Limite des descripteurs de fichiers, Mais parFD_SETSIZE=1024Restreint)

FD_SETSIZE=1024 fd_setLa macro a été utilisée, Bien sûr, vous pouvez modifier le noyau, Puis recompiler le noyau, Ce n'est généralement pas recommandé

selectMise en œuvre du Code

#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <pthread.h>

#define MAX_LEN 4096

int main(int argc, char **argv) {
    
    int listenfd, connfd, n;
    struct sockaddr_in svr_addr;
    char buff[MAX_LEN];

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
    
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    memset(&svr_addr, 0, sizeof(svr_addr));
    svr_addr.sin_family = AF_INET;
    svr_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    svr_addr.sin_port = htons(8081);

    if (bind(listenfd, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) {
    
        printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    if (listen(listenfd, 10) == -1) {
    
        printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    //select
    fd_set rfds, rset, wfds, wset;
    FD_ZERO(&rfds);
    FD_ZERO(&wfds);
    FD_SET(listenfd, &rfds);
    int max_fd = listenfd;

    while (1) {
    
        rset = rfds;
        wset = wfds;
        int nready = select(max_fd + 1, &rset, &wset, NULL, NULL);

        if (FD_ISSET(listenfd, &rset)) {
     //
            struct sockaddr_in clt_addr;
            socklen_t len = sizeof(clt_addr);
            if ((connfd = accept(listenfd, (struct sockaddr *) &clt_addr, &len)) == -1) {
    
                printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
                return 0;
            }
            FD_SET(connfd, &rfds);
            if (connfd > max_fd) max_fd = connfd;
            if (--nready == 0) continue;
        }
        int i = 0;
        for (i = listenfd + 1; i <= max_fd; i++) {
    
            if (FD_ISSET(i, &rset)) {
     //
                n = recv(i, buff, MAX_LEN, 0);
                if (n > 0) {
    
                    buff[n] = '\0';
                    printf("recv msg from client: %s\n", buff);
                    FD_SET(i, &wfds);
                }
                else if (n == 0) {
     //
                    FD_CLR(i, &rfds);
                    close(i);
                }
                if (--nready == 0) break;
            }
            else if (FD_ISSET(i, &wset)) {
    
                send(i, buff, n, 0);
                FD_SET(i, &rfds);
                FD_CLR(i, &wfds);
            }
        }
    }
    close(listenfd);
    return 0;
}

Modèle de serveur hautement simultané-poll

pollIntroduction

  pollSuivez - moi.selectSimilaire, Surveillance multiplex IO, Mais...pollImpossible de traverser la plateforme.En fait...pollC'est ça.select Trois ensembles de descripteurs de fichiers sont devenus une collection .

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

Description des paramètres:

  • fds: Paramètres entrants et sortants, En fait, c'est un tableau de structures
fds.fd:  Descripteurs de fichiers à surveiller 
fds.events: 
	POLLIN---->Lire l'événement
	POLLOUT---->Écrivez l'événement
fds.revents: Événements retournés
  • nfds: Nombre de contenus réellement valides dans le tableau
  • timeout: Temps mort, En millisecondes.
-1:Blocage permanent,  Jusqu'à ce que l'événement surveillé se produise 
0:  Qu'un événement se produise ou non , Reviens tout de suite.
>0:  Jusqu'à ce que l'événement surveillé se produise ou s'arrête 

Valeur de retour:

  • Succès: Nombre d'événements de préparation retournés
  • Échec: Retour-1.Sitimeout=0, poll La fonction n'est pas bloquée , Et aucun événement ne s'est produit , Retour à-1, Eterrno=EAGAIN, Cette situation ne doit pas être considérée comme une erreur .
struct pollfd {
    
   int   fd;        /* file descriptor */    Descripteurs de fichiers surveillés 
   short events;     /* requested events */   Événements à surveiller ---Ne sera pas modifié
   short revents;    /* returned events */    Renvoie un événement modifié  --- Retour du noyau 
};

Description:

  1. QuandpollQuand la fonction revient, Dans la structurefdEteventsIl n'y a pas eu de changement, Y a - t - il eu des incidents causés par reventsPour juger, Alors...poll Oui demande et retour séparés
  2. struct pollfdDans la structurefd Membre si la valeur attribuée est -1, Etpoll Pas de surveillance
  3. Par rapport àselect, poll Aucun changement essentiel ; MaispollPeut percer1024Limites.In/proc/sys/fs/file-max Voir ce qu'un processus peut ouvrir socket Limite supérieure du descripteur , Le profil peut être modifié si nécessaire : /etc/security/limits.conf,Ajouter les informations de configuration suivantes, Le redémarrage du terminal prend effet
* soft nofile 1024
* hard nofile 100000

softEthardReprésente séparémentulimit Limites minimales et maximales que la commande peut modifier

pollMise en œuvre du Code

#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <pthread.h>
#define MAX_LEN 4096
#define POLL_SIZE 1024

int main(int argc, char **argv) {
    
    int listenfd, connfd, n;
    struct sockaddr_in svr_addr;
    char buff[MAX_LEN];

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
    
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    memset(&svr_addr, 0, sizeof(svr_addr));
    svr_addr.sin_family = AF_INET;
    svr_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    svr_addr.sin_port = htons(8081);

    if (bind(listenfd, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) {
    
        printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    if (listen(listenfd, 10) == -1) {
    
        printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    //poll
    struct pollfd fds[POLL_SIZE] = {
    0};
    fds[0].fd = listenfd;
    fds[0].events = POLLIN;

    int max_fd = listenfd;
    int i = 0;
    for (i = 1; i < POLL_SIZE; i++) {
    
        fds[i].fd = -1;
    }
    while (1) {
    
        int nready = poll(fds, max_fd + 1, -1);

        if (fds[0].revents & POLLIN) {
    
            struct sockaddr_in client = {
    };
            socklen_t len = sizeof(client);
            if ((connfd = accept(listenfd, (struct sockaddr *) &client, &len)) == -1) {
    
                printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
                return 0;
            }
            printf("accept \n");
            fds[connfd].fd = connfd;
            fds[connfd].events = POLLIN;
            if (connfd > max_fd) max_fd = connfd;
            if (--nready == 0) continue;
        }
        //int i = 0;
        for (i = listenfd + 1; i <= max_fd; i++) {
    
            if (fds[i].revents & POLLIN) {
    
                n = recv(i, buff, MAX_LEN, 0);
                if (n > 0) {
    
                    buff[n] = '\0';
                    printf("recv msg from client: %s\n", buff);
                    send(i, buff, n, 0);
                }
                else if (n == 0) {
     //
                    fds[i].fd = -1;
                    close(i);
                }
                if (--nready == 0) break;
            }
        }
    }
}

Modèle de serveur hautement simultané-epoll (Points saillants)

epollIntroduction

  Déléguer la détection des changements dans les descripteurs de fichiers au noyau pour le traitement, Le noyau changera ensuite le descripteur de fichier correspondant à ÉvénementsRetour à la demande.

  N'oublie pas,epollEst axé sur les événements, Sa structure de données sous - jacente est un arbre Rouge et noir ,Rouge et noirkey- Oui.fd,valC'est un événement., Retour à l'événement .

epollIl existe deux modes de fonctionnement,ETEtLTMode.

Déclenchement horizontalLT:

  • High Level Representative1
  • Tant qu'il y a des données dans le tampon, Je n'arrête pas de dire

Déclencheur de bordET:

  • Les changements de niveau représentent 1
  • La présence de données dans le tampon n'est notifiée qu'une seule fois, Avant que de nouvelles données arrivent (Si vous n'avez pas fini de lire les données, Les données restantes ne seront pas notifiées, Jusqu'à ce que de nouvelles données arrivent)

  epollDéclenchement horizontal par défautLT, Dans les scénarios qui exigent des performances élevées , Peut être changé en bord ET Approche non bloquante pour améliorer l'efficacité .

  Utilisation généraleLT C'est une lecture ponctuelle. , Plus de données . Et une seule fois peut lire , Les petites quantités de données utilisent des bords ET.

  ET Le mode n'est notifié qu'une seule fois , Alors, lisez en boucle pendant que vous lisez , Jusqu'à la fin, Mais après la lecture readÇa va bloquer, Le descripteur de fichier doit donc être réglé en mode non - blocage (fcntlFonctions)

  read Lorsque la fonction est lue en mode non - bloquant , En cas de retour-1, EterrnoPourEAGAIN, Indique que la ressource actuelle n'est pas disponible , Ce qui signifie que le tampon n'a pas de données ( Les données du tampon ont été lues ); Ou quandreadLorsque la longueur des données lues retournées est inférieure à la longueur des données demandées, Vous pouvez vous assurer qu'il n'y a plus de données lisibles dans le tampon , Vous pouvez également considérer que l'événement de lecture a été traité à ce stade .

epollRéacteur

  Réacteur: Un petit événement déclenche une série de réactions

  epoll L'esprit du réacteur : c++L'idée d'encapsulation de( Encapsuler les données et les opérations )

  • Descripteur,Événements, Les traitements correspondants sont encapsulés
  • Quand l'événement correspondant au descripteur se produit , Appel automatique à la méthode de traitement ( En fait, le principe est la fonction de rappel )

  epoll L'idée centrale du réacteur est : En appelantepoll_ctlEn fonction, Oui.events En montant dans l'arbre ,Utilisationepoll_data_tDeptrMembres, Mettez un descripteur de fichier, Les événements et les fonctions de rappel sont encapsulés dans une structure , Et laissezptr Pointez vers cette structure .Puis appelezepoll_waitQuand la fonction revient, Vous pouvez obtenir des détails events, Et obtenireventsDans la structureevents.data.ptrPointeur, ptr Il y a une fonction de rappel dans la structure pointée par le pointeur , Cette fonction de rappel peut éventuellement être appelée .

struct epoll_event {
    
	uint32_t     events;      /* Epoll events */
	epoll_data_t data;        /* User data variable */
};
typedef union epoll_data {
    
	void        *ptr;
	int          fd;
	uint32_t     u32;
	uint64_t     u64;
} epoll_data_t;

epoll-api

int epoll_create(int size);

Description de la fonction: Créer une racine d'arbre

Description des paramètres:

  • size: Nombre maximum de noeuds, Ce paramètre estlinux 2.6.8Ignoré, Mais vous devez passer un0Nombre de,Importance historique,Avecepoll_create1Ça marche..
  • Valeur de retour:
Succès: Renvoie un plus grand que0Descripteur de fichier pour, Représentant la racine de l'arbre entier.
Échec: Retour-1, Et la mise en placeerrnoValeur.

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

Description de la fonction: Le noeud à écouter estepollAjouter à l'arbre, Supprimer et modifier

Description des paramètres:

  • epfd: epollRacines

  • op:

EPOLL_CTL_ADD: Ajouter un noeud d'événement à l'arbre
EPOLL_CTL_DEL: Supprimer le noeud d'événement de l'arbre
EPOLL_CTL_MOD: Modifier le noeud d'événement correspondant sur l'arbre
  • fd: Descripteur de fichier correspondant au noeud d'événement
  • event: Noeud d'événement à utiliser
struct epoll_event {
    
	uint32_t     events;      /* Epoll events */
	epoll_data_t data;        /* User data variable */
};
typedef union epoll_data {
    
	void        *ptr;
	int          fd;
	uint32_t     u32;
	uint64_t     u64;
} epoll_data_t;
  • event.eventsLes plus courants sont::
EPOLLIN: Lire l'événement
EPOLLOUT: Écrivez l'événement
EPOLLERR: Événements d'erreur
EPOLLET: Mode de déclenchement du bord
  • event.fd: Descripteur de fichier correspondant à l'événement à surveiller

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

Description de la fonction: Attendre que l'événement de retour du noyau se produise

Description des paramètres:

  • epfd: epollRacines
  • events: Paramètres sortants, En fait, c'est un tableau de structures d'événements
  • maxevents: Taille du tableau
  • timeout:
	-1: Indique un blocage permanent
	0: Retour immédiat
	>0:  Indique un délai d'attente pour l'événement 

Valeur de retour:

  • Succès: Renvoie le nombre d'événements qui se sont produits
  • Échec: Sitimeout=0, Aucun événement n'est retourné ; Retour-1, ParamètreserrnoValeur

  epoll_waitDeeventsEst un paramètre sortant, Appelezepoll_ctl Quelles valeurs sont passées au noyau , Quandepoll_waitAu retour, Ce que le noyau renvoie ,C'est vrai.struct event Modifier la valeur de la variable structure pour .

epollAvantages et inconvénients

epollAvantages:

  1. Haute performance, Un million de concurrents. ,EtselectPas question.

epollInconvénients:

  1. Impossible de traverser la plateforme,linuxEn bas

epollMise en œuvre du Code

#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <pthread.h>

#define POLL_SIZE 1024
#define MAX_LEN 4096

int main(int argc, char **argv) {
    
    int listenfd, connfd, n;
    char buff[MAX_LEN];
    struct sockaddr_in svr_addr;
    memset(&svr_addr, 0, sizeof(svr_addr));
    svr_addr.sin_family = AF_INET;
    svr_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    svr_addr.sin_port = htons(8081);

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
    
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    if (bind(listenfd, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) {
    
        printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    if (listen(listenfd, 10) == -1) {
    
        printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    int epfd = epoll_create(1); //int size

    struct epoll_event events[POLL_SIZE] = {
    0};
    struct epoll_event ev;

    ev.events = EPOLLIN;
    ev.data.fd = listenfd;

    epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);

    while (1) {
    
        int nready = epoll_wait(epfd, events, POLL_SIZE, 5);
        if (nready == -1) {
    
            continue;
        }
        int i = 0;
        for (i = 0; i < nready; i++) {
    
            int actFd = events[i].data.fd;
            if (actFd == listenfd) {
    
                struct sockaddr_in cli_addr;
                socklen_t len = sizeof(cli_addr);
                if ((connfd = accept(listenfd, (struct sockaddr *) &cli_addr, &len)) == -1) {
    
                    printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
                    return 0;
                }
                printf("accept\n");
                ev.events = EPOLLIN;
                ev.data.fd = connfd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
            }
            else if (events[i].events & EPOLLIN) {
    
                n = recv(actFd, buff, MAX_LEN, 0);
                if (n > 0) {
    
                    buff[n] = '\0';
                    printf("recv msg from client: %s\n", buff);
                    send(actFd, buff, n, 0);
                }
                else if (n == 0) {
     //
                    epoll_ctl(epfd, EPOLL_CTL_DEL, actFd, NULL);
                    close(actFd);
                }
            }
        }
    }
    return 0;
}
原网站

版权声明
本文为[Cheems]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/188/202207071603369667.html