Puntos de conocimiento WIFI ESP32

1. El cliente TCP se conecta al servidor
(1) El proceso básico es conectar el wifi a la estación, crear un nuevo socket y conectarse al servidor tcp.
(2). Las funciones API relacionadas
crean socket

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

dominio: familia de direcciones, es decir, tipo de dirección IP, comúnmente utilizados son AF_INET y AF_INET6; tipo: método de transmisión de datos/tipo de socket, comúnmente utilizados son SOCK_STREAM (zócalo de formato de flujo/zócalo orientado a la conexión) y SOCK_DGRAM; protocolo: el tipo de protocolo , los comunes son IPPROTO_TCP e IPPTOTO_UDP, que representan respectivamente el protocolo de transmisión TCP y el protocolo de transmisión UDP; el valor de retorno es un socket.

conectar

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

s: socket; sockaddr: la dirección del host y el número de puerto al que desea conectarse el socket s; namelen: la longitud del búfer de nombres.

enviar

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

s: el descriptor de socket del remitente; dataptr: el búfer de los datos que se enviarán; tamaño: el número de bytes de los datos que se enviarán; banderas: generalmente establecido en 0.

tomar el control

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

s: descriptor de socket del receptor; mem: búfer de datos de recepción; tamaño: la longitud máxima que se recibirá; banderas: generalmente establecido en 0.

conexión cercana

int shutdown(int s,int how)

s: descriptor de socket; how: indicador, utilizado para describir qué operaciones están prohibidas.

cerrar enchufe

close(int s)

s: descriptor de socket.

modo de la toma de control

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

s: descriptor de socket; cmd: comando de operación para socket s; argp: puntero a los parámetros del comando cmd; cuando cmd es FIONBIO, significa que no bloquea, y el argp correspondiente es 1 cuando no bloquea, y cuando es 0 está bloqueando.

Establecer opciones de enchufe

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

s: palabra de descripción del socket; level: nivel de definición de la opción; admite SOL_SOCKET, IPPROTO_TCP, IPPROTO_IP e IPPROTO_IPV6; optname: opción a configurar; optval: puntero, que apunta al búfer para almacenar el nuevo valor de la opción a configurar; optlen: optval Longitud del búfer; cuando el optname correspondiente es SO_RCVTIMEO, significa recibir el tiempo de espera, optval es el tiempo de espera y optlen es la longitud

El código central se configura para bloquear después de que el número de veces de envío del bucle sea un múltiplo de 5 en el código, y luego se configura para que no bloquee hasta que se reciben los datos.

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. Explicación detallada de los parámetros de setsockopt
Si desea llamar a closesocket desde un socket que ya está en el estado establecido (generalmente se distingue por número de puerto e identificador) y continuar reutilizando el socket (generalmente no se cerrará de inmediato, pasará por el proceso de TIME_WAIT):

(1) Si desea continuar reutilizando el socket después de llamar a closesocket desde el socket que ya está en el estado establecido (generalmente se distingue por número de puerto e identificador) (generalmente no se cerrará de inmediato, pasará por el proceso de TIME_WAIT): BOOL bReuseaddr
  = TRUE
  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*) bReuseaddr, sizeof(BOOL));

(2) Si desea cerrar a la fuerza el socket conectado después de llamar a closesocket, no experimente el
  procedimiento de tiempo de espera:
  BOOL bDontLinger=FALSE
  setsockopt(s, SOL_SOCKET, SO_DONTLINGER, (const char*) bDontLinger, sizeof(BOOL) ) ;

(3) En el proceso de SEND () send() y recv(), a veces debido a las condiciones de la red y otras razones, se establece el límite de tiempo de envío y recepción: int nNetTimeout=1000//1 segundo // límite de tiempo
  de
  envío
  setsockopt( socket, SOL_S0CKET, SO_SNDTIMEO, (char *) nNetTimeout, sizeof(int));
  // límite de tiempo de recepción
  setsockopt(socket, SOL_S0CKET, SO_RCVTIMEO, (char *) nNetTimeout, sizeof(int));

(4) Al enviar (), lo que se devuelve es el byte realmente enviado (sincrónico) o el byte enviado al búfer del socket (asincrónico), el estado predeterminado del sistema es 8688 bytes (alrededor de 8.5k) enviados y recibidos una vez ; en el proceso real La cantidad de envío y recepción de datos es relativamente grande, puede configurar el búfer de socket para evitar enviar () enviar (), recv () para enviar y recibir continuamente: //recibir búfer int nRecvBuf=32
  *
  1024 //establecer en 32K
  setsockopt(s, SOL_SOCKET, SO_RCVBUF, (const char*) nRecvBuf, sizeof(int));
  //Enviar búfer
  int nSendBuf=32 * 1024//establecer en 32K
  setsockopt(s, SOL_SOCKET, SO_SNDBUF, ( const char*) nSendBuf, sizeof(int)) ;

(5) Si espera que la copia del búfer del sistema al búfer del socket no afecte el rendimiento del programa al enviar datos:
  int nZero=0;
  setsockopt(socket, SOL_S0CKET, SO_SNDBUF, (char *) nZero, sizeof (nCero));

(6) Igual que arriba, complete la función anterior en recv() (por defecto, copie el contenido del búfer del socket al búfer del sistema):
  int nZero=0;
  setsockopt(socket, SOL_S0CKET, SO_RCVBUF, (char *) nCero, tamaño de(int));

(7) Normalmente, al enviar datagramas UDP, se espera que los datos enviados por el socket tengan características de transmisión:
  BOOL bBroadcast=TRUE
  setsockopt(s, SOL_SOCKET, SO_BROADCAST, (const char*) bBroadcast, sizeof(BOOL));

(8) En el proceso de conexión del cliente al servidor, si el socket de modo sin bloqueo está en el proceso de conexión (), puede configurar el retraso de conexión () hasta que se llame a accpet () (la configuración de esta función solo comienza en el proceso sin bloqueo Efecto significativo, no tiene efecto en el bloqueo de llamadas a funciones)
  BOOL bConditionalAccept=TRUE
  setsockopt(s, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, (const char *)bconditionaccept, sizeof(BOOL));

Supongo que te gusta

Origin blog.csdn.net/qizhi321123/article/details/128862965
Recomendado
Clasificación