Points de connaissance ESP32 WIFI

1. Le client TCP se connecte au serveur
(1).Le processus de base consiste à connecter le wifi au sta, à créer un nouveau socket et à se connecter au serveur tcp.
(2).Les fonctions API associées
créent un socket

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

domaine : famille d'adresses, c'est-à-dire type d'adresse IP, couramment utilisés sont AF_INET et AF_INET6 ; type : méthode de transmission de données/type de socket, couramment utilisés sont SOCK_STREAM (socket au format de flux/socket orienté connexion) et SOCK_DGRAM ; protocol : le type de protocole , les plus communs sont IPPROTO_TCP et IPPTOTO_UDP, représentant respectivement le protocole de transmission TCP et le protocole de transmission UDP ; la valeur de retour est une socket.

connecter

int connect(int s,const struct sockaddr *name,socklen_t namelen)

s : socket ; sockaddr : l'adresse de l'hôte et le numéro de port auquel socket s souhaite se connecter ; namelen : la longueur du tampon de noms.

envoyer

ssize_t send(int s,const void *dataptr,size_t size,int flags)

s : le descripteur de socket de l'expéditeur ; dataptr : le buffer des données à envoyer ; size : le nombre d'octets des données à envoyer ; flags : généralement mis à 0.

reprendre

ssize_t recv(int s,void *mem,size_t len,int flags)

s : descripteur de socket du récepteur ; mem : tampon de données de réception ; taille : longueur maximale à recevoir ; drapeaux : généralement définis sur 0.

fermer la connexion

int shutdown(int s,int how)

s : descripteur de socket ; how : indicateur, utilisé pour décrire les opérations interdites.

fermer la prise

close(int s)

s : descripteur de socket.

mode de la prise de contrôle

int ioctlsocket(int s,long cmd,void *argp)

s : descripteur de socket ; cmd : commande d'opération pour le socket s ; argp : pointeur vers les paramètres de la commande cmd ; lorsque cmd est FIONBIO, cela signifie non bloquant, et l'argp correspondant est 1 lorsqu'il est non bloquant, et lorsque c'est 0 c'est bloquant.

Définir les options de socket

int setsockopt(int s,int level,int optname,const void *opval,socklen_t optlen)

s : mot de description de socket ; niveau : niveau de définition d'option ; prend en charge SOL_SOCKET, IPPROTO_TCP, IPPROTO_IP et IPPROTO_IPV6 ; optname : option à définir ; optval : pointeur, pointant vers le tampon pour stocker la nouvelle valeur de l'option à définir ; optlen : optval Longueur du tampon ; lorsque l'optname correspondant est SO_RCVTIMEO, cela signifie recevoir un délai d'expiration, optval est le délai d'expiration et optlen est la longueur

Le code principal est défini pour bloquer après que le nombre de fois d'envoi de boucle est un multiple de 5 dans le code, puis défini sur non bloquant jusqu'à ce que les données soient reçues.

static void tcp_client(void) 
{
    
    
    char rx_buffer[128];
    char host_ip[] = HOST_IP_ADDR;
    int addr_family = 0;
    int ip_protocol = 0;

    struct timeval timeout={
    
    
        .tv_sec = 0,
        .tv_usec = 20,
    }; 
    u_long non_blocking=1;
    int sendcnt=0;
    while (1) {
    
    
        struct sockaddr_in dest_addr;
        dest_addr.sin_addr.s_addr = inet_addr(host_ip);
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(PORT);
        addr_family = AF_INET;
        ip_protocol = IPPROTO_IP;

        int sock =  socket(addr_family, SOCK_STREAM, ip_protocol);
        //创建socket
        if (sock < 0) {
    
    
            ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT);

        int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
        //建立连接
        if (err != 0) {
    
    
            ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Successfully connected");

        ioctlsocket(sock,FIONBIO,&non_blocking);
        //设置为非阻塞
        setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
        //超时接收时间
        while (1) {
    
    
            int err = send(sock, payload, strlen(payload), 0);
            //发送
            if (err < 0) {
    
    
                ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                break;
            }
            sendcnt++;
            if((sendcnt%5)==0)
            {
    
    
                non_blocking=0;
                ioctlsocket(sock,FIONBIO,&non_blocking);
            }
            else
            {
    
    
                non_blocking=1;
                ioctlsocket(sock,FIONBIO,&non_blocking);
            }

            int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);   
            //接收
        #if 1
            if (len >= 0) {
    
    
                rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
                ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
                ESP_LOGI(TAG, "%s", rx_buffer);
            }
        #else
           // Error occurred during receiving
            if (len < 0) {
    
    
                ESP_LOGE(TAG, "recv failed: errno %d", errno);
                break;
            }
            // Data received
            else {
    
    
                rx_buffer[len] = 0; 
                ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
                ESP_LOGI(TAG, "%s", rx_buffer);
            }
        #endif
            vTaskDelay(2000 / portTICK_PERIOD_MS);
        }

        if (sock != -1) {
    
    
            ESP_LOGI(TAG, "Shutting down socket and restarting...");
            shutdown(sock, 0);
            close(sock);
        }
    }
    vTaskDelete(NULL);
}

2. Explication détaillée des paramètres de setsockopt
Si vous souhaitez appeler closesocket à partir d'un socket qui est déjà dans l'état établi (généralement distingué par le numéro de port et l'identifiant) ​​et continuer à réutiliser le socket (généralement, il ne sera pas fermé immédiatement, il passera par le processus de TIME_WAIT) :

(1) Si vous souhaitez continuer à réutiliser le socket après avoir appelé closesocket depuis le socket qui est déjà dans l'état établi (généralement distingué par le numéro de port et l'identifiant) ​​(généralement il ne sera pas fermé immédiatement, il passera par le processus de TIME_WAIT): BOOL bReuseaddr
  = TRUE
  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*) bReuseaddr, sizeof(BOOL));

(2) Si vous souhaitez fermer de force le socket connecté après avoir appelé closesocket, veuillez ne pas faire l'expérience de la
  procédure d'attente :
  BOOL bDontLinger=FALSE
  setsockopt(s, SOL_SOCKET, SO_DONTLINGER, (const char*) bDontLinger, sizeof(BOOL) ) ;

(3). Dans le processus de SEND() send() et recv(), parfois en raison des conditions du réseau et d'autres raisons, la limite de temps d'envoi et de réception est définie : int nNetTimeout=1000//1 seconde // limite de temps
  d'
  envoi
  setsockopt( socket, SOL_S0CKET, SO_SNDTIMEO, (char *) nNetTimeout, sizeof(int)); //
  recevoir
  setsockopt(socket, SOL_S0CKET, SO_RCVTIMEO, (char *) nNetTimeout, sizeof(int));

(4). Lors de l'envoi (), ce qui est renvoyé est l'octet réellement envoyé (synchrone) ou l'octet envoyé au tampon de socket (asynchrone) ; l'état par défaut du système est de 8 688 octets (environ 8,5 k) envoyés et reçus une fois ; dans le processus réel La quantité de données d'envoi et de réception est relativement importante, vous pouvez définir le tampon de socket pour éviter que send (), send (), recv () envoie et reçoive en continu : //receive buffer int nRecvBuf=
  32
  * 1024//set to 32K
  setsockopt(s, SOL_SOCKET, SO_RCVBUF , (const char*) nRecvBuf, sizeof(int)); //
  Send buffer
  int nSendBuf=32 * 1024//set to 32K
  setsockopt(s, SOL_SOCKET, SO_SNDBUF, (const char*) nSendBuf, sizeof(int)) ;

(5) Si vous espérez que la copie du tampon système vers le tampon socket n'affectera pas les performances du programme lors de l'envoi de données :
  int nZero=0;
  setsockopt(socket, SOL_S0CKET, SO_SNDBUF, (char *) nZero , sizeof (nZéro));

(6). Comme ci-dessus, complétez la fonction ci-dessus dans recv() (par défaut, copiez le contenu du tampon de socket dans le tampon système) :
  int nZero=0 ;
  setsockopt(socket, SOL_S0CKET, SO_RCVBUF, (char * ) nZero, sizeof(int));

(7) Habituellement, lors de l'envoi de datagrammes UDP, on espère que les données envoyées par la socket ont des caractéristiques de diffusion :
  BOOL bBroadcast=TRUE
  setsockopt(s, SOL_SOCKET, SO_BROADCAST, (const char*) bBroadcast, sizeof(BOOL));

(8) Dans le processus de connexion du client au serveur, si le socket en mode non bloquant est dans le processus connect (), vous pouvez définir le délai connect () jusqu'à ce que accpet () soit appelé (ce paramètre de fonction ne démarre que dans le processus non bloquant Effet significatif, n'a aucun effet sur le blocage des appels de fonction)
  BOOL bConditionalAccept=TRUE
  setsockopt(s, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, (const char *)bconditionaccept, sizeof(BOOL));

Je suppose que tu aimes

Origine blog.csdn.net/qizhi321123/article/details/128862965
conseillé
Classement