本文章 来自原创专栏《ESP32教学专栏 (基于ESP-IDF)》 下的一个二级专栏 《ESP32 上的 WiFi 及 Lwip 协议栈》,讲解如何使用 ESP-IDF 构建 ESP32 程序,发布文章并会持续为已发布文章添加新内容! 每篇文章都经过了精打细磨!
↓↓↓通过下方链接进入专栏主页↓↓↓
-
专栏主页:点击进入→ESP32 教学专栏 (基于ESP-IDF)
-
二级专栏:点击进入→ESP32 上的 WiFi
文章目录
一、建立连接 —— ESP32 作 TCP Client客户端
1. TCP Client 的基本思路
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)则根据前两个参数自动选择协议 |