TCP/IP协议原理
TCP/IP协议是Internet互联网最基本的协议,TCP/IP协议的应用层的主要协议有HTTP、Telnet、FTP、SMTP等,是用来读取来自传输层的数据或者将数据传输写入传输层;传输层的主要协议有UDP、TCP,实现端对端的数据传输;网络层的主要协议有ICMP、IP、IGMP,主要负责网络中数据包的传送等;链路层有时也称作数据链路层或网络接口层,主要协议有ARP、RARP,通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡,它们一起处理与传输媒介(如电缆或其他物理设备)的物理接口细节。
TCP协议是一个面向连接的、可靠的传输协议,它提供一种可靠的字节流,能保证数据完整、无损并且按顺序到达。TCP尽量连续不断地测试网络的负载并且控制发送数据的速度以避免网络过载。另外,TCP试图将数据按照规定的顺序发送。
ESP32作为热点+TCP服务端
参考乐鑫开源程序,设置wifi模式为AP,运行TCP服务端程序。
ESP32作为站点+TCP客户端
参考乐鑫开源程序,设置wifi模式为STA,运行TCP客户端程序。
AP+TCP服务端程序主函数
#include #include "freertos/FreeRTOS.h"#include "freertos/task.h"#include "esp_mac.h"#include "esp_wifi.h"#include "esp_event.h"#include "esp_log.h"#include "nvs_flash.h"#include "lwip/err.h"#include "lwip/sys.h"#include "tcp_server.h"/* The examples use WiFi configuration that you can set via project configuration menu. If you'd rather not, just change the below entries to strings with the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"*///#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID//#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD#define EXAMPLE_ESP_WIFI_SSID "myesp32AP"#define EXAMPLE_ESP_WIFI_PASS "esp45678"#define EXAMPLE_ESP_WIFI_CHANNEL CONFIG_ESP_WIFI_CHANNEL#define EXAMPLE_MAX_STA_CONN CONFIG_ESP_MAX_STA_CONN#define WIFI_TAG "wifi softAP"//static const char *WIFI_TAG = "wifi softAP";static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){ if (event_id == WIFI_EVENT_AP_STACONNECTED) { wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data; ESP_LOGI(WIFI_TAG, "station "MACSTR" join, AID=%d", MAC2STR(event->mac), event->aid); } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) { wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data; ESP_LOGI(WIFI_TAG, "station "MACSTR" leave, AID=%d", MAC2STR(event->mac), event->aid); }}void wifi_init_softap(void){ ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_ap(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL)); wifi_config_t wifi_config = { .ap = { .ssid = EXAMPLE_ESP_WIFI_SSID, .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID), .channel = EXAMPLE_ESP_WIFI_CHANNEL, .password = EXAMPLE_ESP_WIFI_PASS, .max_connection = EXAMPLE_MAX_STA_CONN, .authmode = WIFI_AUTH_WPA_WPA2_PSK, .pmf_cfg = { .required = false, }, }, }; if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) { wifi_config.ap.authmode = WIFI_AUTH_OPEN; } ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); ESP_LOGI(WIFI_TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d", EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);}void app_main(void){ //Initialize NVS esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); ESP_LOGI(WIFI_TAG, "ESP_WIFI_MODE_AP"); wifi_init_softap(); vTaskDelay(1000 / portTICK_PERIOD_MS); tcpServerStart();}
tcp_server.c
/*#include #include #include "freertos/FreeRTOS.h"#include "freertos/task.h"#include "esp_system.h"#include "esp_wifi.h"#include "esp_event.h"#include "esp_log.h"#include "nvs_flash.h"#include "esp_netif.h"#include "protocol_examples_common.h"#include "lwip/err.h"#include "lwip/sockets.h"#include "lwip/sys.h"#include */#include "tcp_server.h"#define PORT CONFIG_EXAMPLE_PORT#define KEEPALIVE_IDLE CONFIG_EXAMPLE_KEEPALIVE_IDLE#define KEEPALIVE_INTERVAL CONFIG_EXAMPLE_KEEPALIVE_INTERVAL#define KEEPALIVE_COUNT CONFIG_EXAMPLE_KEEPALIVE_COUNT#define TCP_TAG "TCP"//static const char *TCP_TAG= "example";static void do_retransmit(const int sock){ int len; char rx_buffer[128]; char tx_buffer[128]; do { len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0); if (len 0) { int written = send(sock, rx_buffer + (len - to_write), to_write, 0); if (written 0);}static void tcp_server_task(void *pvParameters){ char addr_str[128]; int addr_family = (int)pvParameters; int ip_protocol = 0; int keepAlive = 1; int keepIdle = KEEPALIVE_IDLE; int keepInterval = KEEPALIVE_INTERVAL; int keepCount = KEEPALIVE_COUNT; struct sockaddr_storage dest_addr; if (addr_family == AF_INET) { struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr; dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY); dest_addr_ip4->sin_family = AF_INET; dest_addr_ip4->sin_port = htons(PORT); ip_protocol = IPPROTO_IP; }#ifdef CONFIG_EXAMPLE_IPV6 else if (addr_family == AF_INET6) { struct sockaddr_in6 *dest_addr_ip6 = (struct sockaddr_in6 *)&dest_addr; bzero(&dest_addr_ip6->sin6_addr.un, sizeof(dest_addr_ip6->sin6_addr.un)); dest_addr_ip6->sin6_family = AF_INET6; dest_addr_ip6->sin6_port = htons(PORT); ip_protocol = IPPROTO_IPV6; }#endif int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol); if (listen_sock < 0) { ESP_LOGE(TCP_TAG, "Unable to create socket: errno %d", errno); vTaskDelete(NULL); return; } int opt = 1; setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6) // Note that by default IPV6 binds to both protocols, it is must be disabled // if both protocols used at the same time (used in CI) setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));#endif ESP_LOGI(TCP_TAG, "Socket created"); int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); if (err != 0) { ESP_LOGE(TCP_TAG, "Socket unable to bind: errno %d", errno); ESP_LOGE(TCP_TAG, "IPPROTO: %d", addr_family); goto CLEAN_UP; } ESP_LOGI(TCP_TAG, "Socket bound, port %d", PORT); err = listen(listen_sock, 1); if (err != 0) { ESP_LOGE(TCP_TAG, "Error occurred during listen: errno %d", errno); goto CLEAN_UP; } while (1) { ESP_LOGI(TCP_TAG, "Socket listening"); struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6 socklen_t addr_len = sizeof(source_addr); int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len); if (sock sin_addr, addr_str, sizeof(addr_str) - 1); }#ifdef CONFIG_EXAMPLE_IPV6 else if (source_addr.ss_family == PF_INET6) { inet6_ntoa_r(((struct sockaddr_in6 *)&source_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1); }#endif ESP_LOGI(TCP_TAG, "Socket accepted ip address: %s", addr_str); do_retransmit(sock); shutdown(sock, 0); close(sock); }CLEAN_UP: close(listen_sock); vTaskDelete(NULL);}void tcpServerStart(void){ //ESP_ERROR_CHECK(nvs_flash_init()); //ESP_ERROR_CHECK(esp_netif_init()); //ESP_ERROR_CHECK(esp_event_loop_create_default()); /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. * Read "Establishing Wi-Fi or Ethernet Connection" section in * examples/protocols/README.md for more information about this function. */ //ESP_ERROR_CHECK(example_connect());#ifdef CONFIG_EXAMPLE_IPV4 xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET, 5, NULL);#endif#ifdef CONFIG_EXAMPLE_IPV6 xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET6, 5, NULL);#endif}
ATP+TCP客户端程序主函数
#include #include #include "freertos/FreeRTOS.h"#include "freertos/task.h"#include "freertos/event_groups.h"#include "esp_system.h"#include "esp_wifi.h"#include "esp_event.h"#include "esp_log.h"#include "nvs_flash.h"#include "esp_netif.h"#include "addr_from_stdin.h"#include "lwip/err.h"#include "lwip/sockets.h"#include "my_wifi.h"#include "tcp_client.h"void app_main(void){ //wifi conect wifiStaStart(); //tcp client tcpClientStart();}
my_wifi.c
#include "my_wifi.h"/* The examples use WiFi configuration that you can set via project configuration menu If you'd rather not, just change the below entries to strings with the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"*/#define EXAMPLE_ESP_WIFI_SSID "myesp32AP"#define EXAMPLE_ESP_WIFI_PASS "esp45678"#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY#if CONFIG_ESP_WIFI_AUTH_OPEN#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN#elif CONFIG_ESP_WIFI_AUTH_WEP#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP#elif CONFIG_ESP_WIFI_AUTH_WPA_PSK#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK#elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK#elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK#elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK#elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK#elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK#endif/* FreeRTOS event group to signal when we are connected*/static EventGroupHandle_t s_wifi_event_group;/* The event group allows multiple bits for each event, but we only care about two events: * - we are connected to the AP with an IP * - we failed to connect after the maximum amount of retries */#define WIFI_CONNECTED_BIT BIT0#define WIFI_FAIL_BIT BIT1static const char *TAG = "wifi station";static int s_retry_num = 0;static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){ 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) { if (s_retry_num ip_info.ip)); s_retry_num = 0; xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); }}void wifi_init_sta(void){ s_wifi_event_group = xEventGroupCreate(); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_sta(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); esp_event_handler_instance_t instance_any_id; esp_event_handler_instance_t instance_got_ip; ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip)); wifi_config_t wifi_config = { .sta = { .ssid = EXAMPLE_ESP_WIFI_SSID, .password = EXAMPLE_ESP_WIFI_PASS, /* Authmode threshold resets to WPA2 as default if password matches WPA2 standards (pasword len => 8). * If you want to connect the device to deprecated WEP/WPA networks, Please set the threshold value * to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set the password with length and format matching to * WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards. */ .threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD, .sae_pwe_h2e = WPA3_SAE_PWE_BOTH, }, }; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); ESP_ERROR_CHECK(esp_wifi_start() ); ESP_LOGI(TAG, "wifi_init_sta finished."); /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually * happened. */ if (bits & WIFI_CONNECTED_BIT) { ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); } else if (bits & WIFI_FAIL_BIT) { ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); } else { ESP_LOGE(TAG, "UNEXPECTED EVENT"); }}void wifiStaStart(void){ //Initialize NVS esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); ESP_LOGI(TAG, "ESP_WIFI_MODE_STA"); wifi_init_sta(); //tcpStart();}
tcp_client.c
#include "tcp_client.h"#if defined(CONFIG_EXAMPLE_IPV4)#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR#elif defined(CONFIG_EXAMPLE_IPV6)#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR#else#define HOST_IP_ADDR ""#endif#define PORT CONFIG_EXAMPLE_PORTstatic const char *TAGC = "example";static const char *payload = "Message from Client1:tem,hum,c2h4 ";static void tcp_client_task(void *pvParameters){ char rx_buffer[128]; char host_ip[] = HOST_IP_ADDR; int addr_family = 0; int ip_protocol = 0; while (1) {#if defined(CONFIG_EXAMPLE_IPV4) 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;#elif defined(CONFIG_EXAMPLE_IPV6) struct sockaddr_in6 dest_addr = { 0 }; inet6_aton(host_ip, &dest_addr.sin6_addr); dest_addr.sin6_family = AF_INET6; dest_addr.sin6_port = htons(PORT); dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE); addr_family = AF_INET6; ip_protocol = IPPROTO_IPV6;#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN) struct sockaddr_storage dest_addr = { 0 }; ESP_ERROR_CHECK(get_addr_from_stdin(PORT, SOCK_STREAM, &ip_protocol, &addr_family, &dest_addr));#endif int sock = socket(addr_family, SOCK_STREAM, ip_protocol); if (sock < 0) { ESP_LOGE(TAGC, "Unable to create socket: errno %d", errno); break; } ESP_LOGI(TAGC, "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(TAGC, "Socket unable to connect: errno %d", errno); break; } ESP_LOGI(TAGC, "Successfully connected"); while (1) { int err = send(sock, payload, strlen(payload), 0); if (err < 0) { ESP_LOGE(TAGC, "Error occurred during sending: errno %d", errno); break; } int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0); // Error occurred during receiving if (len < 0) { ESP_LOGE(TAGC, "recv failed: errno %d", errno); break; } // Data received else { rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string ESP_LOGI(TAGC, "Received %d bytes from %s:", len, host_ip); ESP_LOGI(TAGC, "%s", rx_buffer); } vTaskDelay(2000 / portTICK_PERIOD_MS); } if (sock != -1) { ESP_LOGE(TAGC, "Shutting down socket and restarting..."); shutdown(sock, 0); close(sock); } } vTaskDelete(NULL);}void tcpClientStart(void){ // ESP_ERROR_CHECK(nvs_flash_init()); // ESP_ERROR_CHECK(esp_netif_init()); // ESP_ERROR_CHECK(esp_event_loop_create_default()); /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. * Read "Establishing Wi-Fi or Ethernet Connection" section in * examples/protocols/README.md for more information about this function. */ // ESP_ERROR_CHECK(example_connect()); xTaskCreate(tcp_client_task, "tcp_client", 4096, NULL, 5, NULL);}
注意
需要在Kconfig.projbuild文件中预先进行一些配置如IP,端口号等。CMakeLists.txt需要包含主函数调用的所有c文件。
结果
成功通信运行的结果如下图所示
服务端运行
客户端运行