ESP32 WIFI Knowledge Points

1. The TCP client connects to the server
(1). The basic process is to connect the wifi to the sta, create a new socket, and connect to the tcp server.
(2).Related API functions
create socket

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

domain: address family, that is, IP address type, commonly used are AF_INET and AF_INET6; type: data transmission method/socket type, commonly used are SOCK_STREAM (stream format socket/connection-oriented socket) and SOCK_DGRAM ; protocol: the protocol type, the common ones are IPPROTO_TCP and IPPTOTO_UDP, respectively representing the TCP transmission protocol and UDP transmission protocol; the return value is a socket.

connect

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

s: socket; sockaddr: the host address and port number that socket s wants to connect to; namelen: the length of the name buffer.

send

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

s: the socket descriptor of the sender; dataptr: the buffer of the data to be sent; size: the number of bytes of the data to be sent; flags: generally set to 0.

take over

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

s: Receiver socket descriptor; mem: Receive data buffer; size: The maximum length to be received; flags: Generally set to 0.

close connection

int shutdown(int s,int how)

s: socket descriptor; how: flag, used to describe which operations are prohibited.

close socket

close(int s)

s: socket descriptor.

mode of the control socket

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

s: socket descriptor; cmd: operation command for socket s; argp: pointer to the parameters of the cmd command; when cmd is FIONBIO, it means non-blocking, and the corresponding argp is non-blocking when it is 1, and it is 0 is blocking.

Set socket options

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

s: socket description word; level: option definition level; supports SOL_SOCKET, IPPROTO_TCP, IPPROTO_IP and IPPROTO_IPV6; optname: option to be set; optval: pointer, pointing to the buffer to store the new value of the option to be set; optlen: optval Buffer length; when the corresponding optname is SO_RCVTIMEO, it means receiving timeout, optval is the timeout time, and optlen is the length

The core code is set to block after the number of loop sending times is a multiple of 5 in the code, and then set to non-blocking until the data is received.

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. Detailed explanation of setsockopt parameters
If you want to call closesocket from a socket that is already in the established state (generally distinguished by port number and identifier) ​​and continue to reuse the socket (generally it will not be closed immediately, it will go through the process of TIME_WAIT):

(1). If you want to continue to reuse the socket after calling closesocket from the socket that is already in the established state (generally distinguished by port number and identifier) ​​(generally it will not be closed immediately, it will go through the process of TIME_WAIT): BOOL bReuseaddr
  = TRUE
  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*) bReuseaddr, sizeof(BOOL));

(2). If you want to forcibly close the connected socket after calling closesocket, please do not experience the
  time waiting procedure:
  BOOL bDontLinger=FALSE
  setsockopt(s, SOL_SOCKET, SO_DONTLINGER, (const char*) bDontLinger, sizeof(BOOL) );

(3). In the process of SEND () send() and recv(), sometimes due to network conditions and other reasons, the sending and receiving time limit is set: int nNetTimeout=1000//1 second
  //
  sending time limit
  setsockopt( socket, SOL_S0CKET, SO_SNDTIMEO, (char *) nNetTimeout, sizeof(int));
  // receive time limit
  setsockopt(socket, SOL_S0CKET, SO_RCVTIMEO, (char *) nNetTimeout, sizeof(int));

(4). When sending (), what is returned is the byte actually sent (synchronous) or the byte sent to the socket buffer (asynchronous); the default state of the system is 8688 bytes (about 8.5k) sent and received once; in the actual process The amount of sending and receiving data is relatively large, you can set the socket buffer to avoid send () send (), recv () to continuously send and receive:
  //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). If you hope that the copy from the system buffer to the socket buffer will not affect the performance of the program when sending data:
  int nZero=0;
  setsockopt(socket, SOL_S0CKET, SO_SNDBUF, (char *) nZero , sizeof(nZero));

(6). Same as above, complete the above function in recv() (by default, copy the contents of the socket buffer to the system buffer):
  int nZero=0;
  setsockopt(socket, SOL_S0CKET, SO_RCVBUF, (char * ) nZero, sizeof(int));

(7). Usually, when sending UDP datagrams, it is hoped that the data sent by the socket has broadcast characteristics:
  BOOL bBroadcast=TRUE
  setsockopt(s, SOL_SOCKET, SO_BROADCAST, (const char*) bBroadcast, sizeof(BOOL));

(8). In the process of connecting the client to the server, if the non-blocking mode socket is in the connect() process, you can set the connect() delay until accpet() is called (this function setting only starts in the non-blocking process Significant effect, has no effect in blocking function calls)
  BOOL bConditionalAccept=TRUE
  setsockopt(s, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, (const char *)bconditionaccept, sizeof(BOOL));

Guess you like

Origin blog.csdn.net/qizhi321123/article/details/128862965