ESP-C3入门9. 创建TCP Server
Posted 编程圈子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ESP-C3入门9. 创建TCP Server相关的知识,希望对你有一定的参考价值。
ESP-C3入门9. 创建TCP Server
一、ESP32 IDF的TCP/IP协议栈
TCP/IP协议栈是ESP32 IDF的一个核心组件。它实现了TCP、UDP、IP、DHCP、DNS和其他网络协议,使ESP32可以与其他设备通信。具体来说,ESP32 IDF的TCP/IP协议栈包括以下几个主要模块:
- WiFi协议栈
- TCP/IP协议栈
- LWIP协议栈
- SPI Flash文件系统
通过TCP/IP协议栈,ESP32可以轻松地实现各种网络应用,例如HTTP服务器、MQTT客户端、TCP/UDP服务器和客户端等等。同时,ESP32 IDF提供了丰富的API和组件,使得开发人员可以快速地实现自己的应用程序。
ESP32主要使用LwIP协议栈,支持函数:
- BSD风格 Sockets API
- Netconn API (未正式启用)
官方文档提示:
在使用任何lwIP API(除BSD Sockets API之外)时,请确保它是线程安全的。
要检查特定的API调用是否安全,请启用CONFIG_LWIP_CHECK_THREAD_SAFETY并运行应用程序。这样,lwIP会断言TCP/IP核心功能被正确访问,如果没有正确锁定或从正确的任务(lwIP FreeRTOS任务)访问,则执行中止。
一般建议使用ESP-NETIF组件与lwIP交互。
ESP-IDF对lwIP进行了一些封装,以间接支持部分功能,如:
- DHCP
- SNTP
- ICMP Ping
- NetBios查找可使用的lwIP API
- mDNS
- 串行PPP接口
等。
二、BSD套接字API介绍
BSD Sockets API 是一个常见的跨平台TCP/IP套接字API, 有时被称为POSIX Sockets或 Berkeley Sockets。
lwIP支持BSD Sockets API的所有常见用法,一些功能如下:
- socket()
- bind()
- accept()
- shutdown()
- getpeername()
- getsockopt()
- setsockopt()
- close()
- read(), readv() write() writev()
- recv() recvmsg() recfrom()
- send() sendmsg() sendto()
- select()
- poll()
- fcntl()
另外lwIP还支持ioctl()非标准的功能。
三、创建TCP Server的步骤
1. 引用TCP/IP协议栈
在CMakeLists.txt包含以下内容:
idf_component_register(SRCS "your_source_files.c"
INCLUDE_DIRS "include"
REQUIRES "tcpip_adapter")
2. 创建 TCP套接字拼绑定端口
创建一个TCP套接字,并将其绑定到指定的端口。然后使用listen()函数将服务器套接字设置为侦听连接。
#include <string.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netdb.h>
#include <lwip/sockets.h>
#define PORT CONFIG_EXAMPLE_PORT
static struct sockaddr_in server_addr;
static int server_socket = -1;
void create_tcp_server()
server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (server_socket < 0)
ESP_LOGE(TAG, "Failed to create server socket!");
return;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(PORT);
int err = bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (err != 0)
ESP_LOGE(TAG, "Failed to bind server socket!");
close(server_socket);
return;
err = listen(server_socket, 1);
if (err != 0)
ESP_LOGE(TAG, "Failed to listen on server socket!");
close(server_socket);
return;
ESP_LOGI(TAG, "TCP server started on port %d", PORT);
3. 接收客户端请求
使用accept()函数从服务器套接字接受新连接。
下面代码里,创建一个TCP客户端,使用结构体client_addr存储客户端的信息。
- 接收到一般消息时,自动原文回复;
- 收到ping时,回复pong;
- 收到bye时,回复bye并关闭连接。
关闭连接后,程序会重新开始等待新连接。
void process_tcp_client()
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addr_len);
if(client_socket <0)
ESP_LOGE(TAG, "Failed to accept client socket!");
return;
ESP_LOGI(TAG, "Accepted new client connection");
// 处理客户端请求
bool end=false;
while (!end)
char buffer[1024];
// 初始化数组
memset(buffer, 0, sizeof(buffer));
int len = recv(client_socket, buffer, sizeof(buffer), 0);
if (len > 0)
const char *response;
if (strncmp(buffer, "ping", 4) == 0)
response = "pong";
ESP_LOGI(TAG, "Received ping, sending pong");
else if (strncmp(buffer, "bye", 3) == 0)
response = "bye";
ESP_LOGI(TAG, "Received bye, closing connection");
end = true;
else
response = buffer;
ESP_LOGI(TAG, "Received data: %s", response);
send(client_socket, response, strlen(response), 0);
close(client_socket);
4. 启动服务
在应用程序的main()函数中调用create_tcp_server()函数来启动TCP服务器
void app_main()
create_tcp_server();
while (1)
process_tcp_client();
四、完整代码
本文代码会基于前一章内容实现,将wifi连接部分独立文件放。
项目框架:
1. wifi.h
#ifndef WIFI_LIB
#define WIFI_LIB
#include <string.h>
/* 宏定义 */
#include <esp_event_base.h>
#include "esp_log.h"
#define LIGHT_ESP_WIFI_SSID "你的wifi账号"
#define LIGHT_ESP_WIFI_PASS "你的wifi密码"
#define LIGHT_ESP_MAXIMUM_RETRY 5
// 事件组允许每个事件有多个位,这里只使用两个事件:
// 已经连接到AP并获得了IP
// 在最大重试次数后仍未连接
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
/* 函数声明 */
void wifi_initialize(void);
void wifi_station_initialize(void);
void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data);
#endif /* WIFI_LIB */
2. wifi.c
#include <freertos/FreeRTOS.h>
#include "main/network/include/wifi.h"
#include <freertos/event_groups.h>
#include <esp_wifi.h>
static const char *TAG = "wifi lib";
// FreeRTOS事件组,连接成功时发出信号
static EventGroupHandle_t s_wifi_event_group = NULL;
static int s_retry_num = 0;
// 事件回调
void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
// 如果是Wi-Fi事件,并且事件ID是Wi-Fi事件STA_START
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
esp_wifi_connect();
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
// 如果是Wi-Fi事件,并且事件ID是Wi-Fi事件STA_DISCONNECTED
/* 如果重试次数小于最大重试次数 */
if (s_retry_num < LIGHT_ESP_MAXIMUM_RETRY)
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
else
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
ESP_LOGI(TAG, "connect to the AP fail");
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
// 如果是IP事件,并且事件ID是IP事件STA_GOT_IP
// 获取事件结果
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
// 将重试次数重置为 0;
s_retry_num = 0;
// 通过调用 xEventGroupSetBits 函数,将 WIFI_CONNECTED_BIT 设置到事件组中,表示成功连接到 AP
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
void wifi_initialize(void)
// 创建一个事件组,用于管理Wi-Fi连接事件。
s_wifi_event_group = xEventGroupCreate();
// 初始化 TCP/IP 协议栈。
ESP_ERROR_CHECK(esp_netif_init());
// 创建默认事件循环。
ESP_ERROR_CHECK(esp_event_loop_create_default());
// 创建默认的Wi-Fi网络接口。
esp_netif_create_default_wifi_sta();
// 设置 Wi-Fi 初始化配置为默认配置
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// 注册事件处理器,以处理 Wi-Fi 和 IP 相关事件
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
void wifi_station_initialize(void)
// 配置WiFi的配置信息
wifi_config_t wifi_config =
.sta =
.ssid = LIGHT_ESP_WIFI_SSID,
.password = LIGHT_ESP_WIFI_PASS,
// 启用WPA2模式,常用的WiFi连接方式
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
.pmf_cfg =
.capable = true,
.required = false
,
,
;
// WiFi工作模式设置为STA
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
// 设置WiFi工作模式
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
// 启动WiFi
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi_station_initialize finished.");
/* 等待连接建立(WIFI_CONNECTED_BIT)或连接失败的次数达到最大值(WIFI_FAIL_BIT)。
* 这些位通过 event_handler() 设置(详见上面)*/
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
/* xEventGroupWaitBits() 返回调用前的 bits,因此我们可以测试实际发生了什么事件。 */
if (bits & WIFI_CONNECTED_BIT)
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", LIGHT_ESP_WIFI_SSID, LIGHT_ESP_WIFI_PASS);
else if (bits & WIFI_FAIL_BIT)
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", LIGHT_ESP_WIFI_SSID, LIGHT_ESP_WIFI_PASS);
else
ESP_LOGE(TAG, "UNEXPECTED EVENT");
3. tcpServer.h
#ifndef TCP_SERVER
#define TCP_SERVER
#include <string.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netdb.h>
#include <lwip/sockets.h>
#define PORT 3000
void create_tcp_server();
void process_tcp_client();
#endif
4. tcpServer.c
//
//
//
#include <esp_log.h>
#include "tcpServer.h"
static struct sockaddr_in server_addr;
static int server_socket = -1;
static const char* TAG = "TCP SERVER";
void create_tcp_server()
server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if(server_socket<0)
ESP_LOGE(TAG, "Failed to create server socket!");
return;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(PORT);
int err = bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));
if(err !=0)
ESP_LOGE(TAG, "Failed to bind server socket!");
close(server_socket);
return;
err = listen(server_socket, 1);
if(err != 0)
ESP_LOGE(TAG, "Failed to listen on server socket!");
close(server_socket);
return;
ESP_LOGI(TAG, "TCP server started on port %d", PORT);
void process_tcp_client()
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addr_len);
if(client_socket <0)
ESP_LOGE(TAG, "Failed to accept client socket!");
return;
ESP_LOGI(TAG, "Accepted new client connection");
// 处理客户端请求
bool end=false;
while (!end)
char buffer[1024];
// 初始化数组
memset(buffer, 0, sizeof(buffer));
int len = recv(client_socket, buffer, sizeof(buffer), 0);
if (len > 0)
const char *response;
if (strncmp(buffer, "ping", 4) == 0)
response = "pong";
ESP_LOGI(TAG, "Received ping, sending pong");
else if (strncmp(buffer, "bye", 3) == 0)
response = "bye";
ESP_LOGI(TAG, "Received bye, closing connection");
end = true;
else
response = buffer;
ESP_LOGI(TAG, "Received data: %s", response);
send(client_socket, response, strlen(response), 0);
close(client_socket);
5. main.c
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <nvs_flash.h>
#include "network/include/wifi.h"
#include "network/include/tcpServer.h"
static const char *TAG = "wifi connection";
void app_main()
int i = 0;
ESP_LOGE(TAG, "app_main");
// 初始化NVS存储区
ESP_ERROR_CHECK(nvs_flash_init());
// Wi-Fi初始化
ESP_LOGI(TAG, "Wi-Fi initialization");
wifi_initialize();
// Wi-Fi Station初始化
wifi_station_initialize();
// 创建 tcp server
create_tcp_server();
while (1)
ESP_LOGI(TAG, "[%02d] Wait Client Connection!", i++);
process_tcp_client();
vTaskDelay(pdMS_TO_TICKS(5000));
6. CmakeLists.txt
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
以上是关于ESP-C3入门9. 创建TCP Server的主要内容,如果未能解决你的问题,请参考以下文章