ESP32 之 ESP-IDF 教学WiFi篇(二)—— LwIP 之 TCP 通信

本文章 来自原创专栏《ESP32教学专栏 (基于ESP-IDF)》 下的一个二级专栏 《ESP32 上的 WiFi 及 Lwip 协议栈》,讲解如何使用 ESP-IDF 构建 ESP32 程序,发布文章并会持续为已发布文章添加新内容! 每篇文章都经过了精打细磨!

↓↓↓通过下方链接进入专栏主页↓↓↓


文章目录

一、建立连接 —— ESP32 作 TCP Client客户端

1. TCP Client 的基本思路

ESP32 之 ESP-IDF 教学WiFi篇(二)—— LwIP 之 TCP 通信

2. TCP Client 代码示例

以下代码是一个函数tcp_task_client实现的是一个FreeRTOS的Task,在这个任务里完成了以下内容:

  • 等待WiFi连接成功(基于FreeRTOS API 之 事件组Event Group
  • 创建Socket对象并初始为TCP(注意代码第7行),并检测Socket是否创建成功。
  • 配置连接参数结构(结构体struct sockaddr_in类型的desk_addr
  • 循环连接服务端,直至连接成功

具体请见注释

void tcp_task_client(void *arg){
    xEventGroupWaitBits(wifiEvent, wifiConnectedBit, pdFALSE, pdFALSE, portMAX_DELAY);
    int _socket;

createSocket:	// 标签

    _socket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    // AF_INET表示IPv4,AF_INET6表示IPv6

    // SOCK_STREAM表示TCP;
    // SOCK_DGRAM表示UDP;
    // SOCK_RAW表示RAW

    // protocol, 规定套接字发送和接送哪类型协议数据。
    // 最常见的是 IPPROTO_TCP, IPPROTO_UDP, IPPROTO_UDPLITE、IPPROTO_ICMP。
    // 如果 domain 和 type已经确定唯一的协议,“0(IPPROTO_IP)” 可以用来表示选择一个默认的协议。

    if(_socket < 0){
        printf("Socket 创建失败, 错误代码:%d\n", errno);
        vTaskDelete(NULL);
    }else{
        printf("Socket 创建成功");
    }

    struct sockaddr_in desk_addr = {
    // 下边这个IP 192.168.31.138和端口号8080是自己设置的,请按需要修改
        .sin_addr.s_addr = inet_addr("192.168.31.138"),	
        .sin_family = AF_INET,
        .sin_port = htons(8080),    // 小端模式转为大端模式
    };


    int err;
    while (1) {
        err = connect(_socket, (struct sockaddr*)&desk_addr, sizeof(desk_addr));
        if (err == 0){
            ESP_LOGI("CLIENT", "连接成功");
            break;
        }else{
            ESP_LOGI("CLIENT", "连接失败,错误代码:%d", errno);
            close(_socket);
            vTaskDelay(pdMS_TO_TICKS(200));
            
            goto createSocket; // goto标签
            
        }
    }
    vTaskDelete(NULL);
}

二、建立连接 —— ESP32 做 TCP Server服务器

1. 代码示例

以下代码是一个函数tcp_task_server实现的是一个FreeRTOS的Task,在这个任务里完成了以下内容:

  • 等待WiFi连接成功(基于FreeRTOS API 之 事件组Event Group
  • 创建Socket对象并初始为TCP(注意代码第7行),并检测Socket是否创建成功。
  • 配置连接参数结构(结构体struct sockaddr_in类型的desk_addr
  • 绑定
  • 开始监听
  • 执行accept函数,从已建立TCP连接的客户端Sockets中去除Socket(此函数会阻塞,因此请按需使其出现在恰当的位置)

具体请见注释

void tcp_task_server(void *arg){
    xEventGroupWaitBits(wifiEvent, wifiConnectedBit, pdFALSE, pdFALSE, portMAX_DELAY);

    int _socket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    if(_socket < 0){
        printf("Socket 创建失败, 错误代码:%d\n", errno);
        vTaskDelete(NULL);
    }else{
        printf("Socket 创建成功");
    }

    struct sockaddr_in desk_addr = {
        .sin_addr.s_addr = INADDR_ANY,
        .sin_family = AF_INET,
        .sin_port = htons(2501),  // 小端模式转为大端模式
    };

    int err;
    while(1){
        err = bind(_socket, (struct sockaddr*)&desk_addr, sizeof(desk_addr));
        if(err == 0){
            ESP_LOGI("SERVICE", "绑定成功");
            break;
        }else{
            ESP_LOGI("SERVICE", "绑定失败, 错误代码: %d", errno);
        }
        vTaskDelay(pdMS_TO_TICKS(1000));
    }

    err = listen(_socket, 5);
    if (err != 0){
        ESP_LOGI("SERVICE", "监听失败,错误代码: %d", errno);
        vTaskDelete(NULL);
    }else{
        ESP_LOGI("SERVICE", "监听成功");
    }

    while (1){
        struct sockaddr_in6 source_addr;
        uint addr_len;
        ESP_LOGI("SERVICE", "准备accept");
        int remote_sock = accept(_socket, (struct sockaddr *) &source_addr, &addr_len);
        //xTaskNotifyGive(tcp_client_handle);

        if(remote_sock < 0){
            ESP_LOGI("SERVICE", "accept失败,错误代码: %d", errno);
        }else{
            ESP_LOGI("SERVICE", "连接成功");
            break;
        }
        vTaskDelay(pdMS_TO_TICKS(1000));
    }

    vTaskDelete(NULL);
}

三、收发数据

四、LwIP BSD API 与TCP/UDP 有关的函数

socket()
bind()
listen()
connect()
accept()
send(), recv(), sendto(), recvfrom()
close()
gethostbyname()
select()
pool()
getsockopt(), setsockopt()
函数名 socket()
函数原型 int socket(int domain, int type, int protocol)
含义 函数socket()为通信创建一个端点,并为该套接字返回一个文件描述符。
返回值 int,若发生错误则返回-1
参数 domain类型为:int;表示欲创建的协议族。
如:AF_INET表示IPv4,
AF_INET6表示IPv6,
AF_UNIX表示本地套接字


type类型为:int
如:SOCK_STREAM表示TCP,
SOCK_DGRAM表示UDP,
SOCK_SEQPACKET表示可靠的顺序包服务,
SOCK_RAW表示网络层上的原始协议


protocol类型为:int;表示指定要使用的实际传输协议。最常见的有IPPROTO_TCP, IPPROTO_SCTP, IPPROTO_UDP, IPPROTO_DCCP。如果填0(IPPRORO_IP)则根据前两个参数自动选择协议
上一篇:花指令


下一篇:如何在tpl模版的div块中加ztree