FreeRTOS-Plus-TCP 协议支持与网络编程指南
目录
- 支持的协议
- [Berkeley Sockets API](#Berkeley Sockets API)
- [TCP 编程](#TCP 编程)
- [UDP 编程](#UDP 编程)
- 网络配置
- 协议详解
- 完整编程示例
支持的协议
FreeRTOS-Plus-TCP 是一个轻量级的 TCP/IP 协议栈,支持以下协议:
核心协议
| 协议 | 支持情况 | 说明 | 源文件 |
|---|---|---|---|
| IPv4 | ✅ 完全支持 | IP 协议版本 4 | FreeRTOS_IPv4.c |
| IPv6 | ✅ 完全支持 | IP 协议版本 6(V4.0+) | FreeRTOS_IPv6.c |
| TCP | ✅ 完全支持 | 传输控制协议(可靠传输) | FreeRTOS_TCP_*.c |
| UDP | ✅ 完全支持 | 用户数据报协议(无连接) | FreeRTOS_UDP_*.c |
| ARP | ✅ 完全支持 | 地址解析协议(IPv4) | FreeRTOS_ARP.c |
| ICMP | ✅ 完全支持 | Internet 控制消息协议(Ping) | FreeRTOS_ICMP.c |
| DHCP | ✅ 完全支持 | 动态主机配置协议(IPv4) | FreeRTOS_DHCP.c |
| DHCPv6 | ✅ 完全支持 | DHCP for IPv6 | FreeRTOS_DHCPv6.c |
| DNS | ✅ 完全支持 | 域名系统 | FreeRTOS_DNS.c |
| ND (Neighbor Discovery) | ✅ 完全支持 | IPv6 邻居发现协议 | FreeRTOS_ND.c |
| RA (Router Advertisement) | ✅ 完全支持 | IPv6 路由器通告 | FreeRTOS_RA.c |
| LLMNR | ✅ 可选支持 | 本地链路多播名称解析 | FreeRTOS_DNS.c |
| mDNS | ✅ 可选支持 | 多播 DNS | FreeRTOS_DNS.c |
| NBNS | ✅ 可选支持 | NetBIOS 名称服务 | FreeRTOS_DNS.c |
协议层次结构
应用层
↓
TCP / UDP
↓
IP (IPv4 / IPv6)
↓
ARP (IPv4) / ND (IPv6)
↓
Ethernet
协议启用配置
所有协议都可以通过 FreeRTOSIPConfig.h 配置启用或禁用:
c
// IPv4/IPv6 配置
#define ipconfigUSE_IPv4 1 // 启用 IPv4
#define ipconfigUSE_IPv6 1 // 启用 IPv6(V4.0+)
// 传输层协议
#define ipconfigUSE_TCP 1 // 启用 TCP
#define ipconfigUSE_UDP 1 // 启用 UDP(默认启用)
// 网络层协议
#define ipconfigUSE_ARP 1 // 启用 ARP(IPv4)
#define ipconfigUSE_ICMP 1 // 启用 ICMP
// 配置协议
#define ipconfigUSE_DHCP 1 // 启用 DHCP(IPv4)
#define ipconfigUSE_DHCPv6 0 // 启用 DHCPv6(IPv6,默认禁用)
#define ipconfigUSE_DNS 1 // 启用 DNS
// IPv6 协议
#define ipconfigUSE_ND 1 // 启用邻居发现
#define ipconfigUSE_RA 1 // 启用路由器通告
// 可选协议
#define ipconfigUSE_LLMNR 0 // 启用 LLMNR(默认禁用)
#define ipconfigUSE_MDNS 0 // 启用 mDNS(默认禁用)
#define ipconfigUSE_NBNS 0 // 启用 NBNS(默认禁用)
Berkeley Sockets API
FreeRTOS-Plus-TCP 提供标准的 Berkeley Sockets 接口,与 POSIX sockets API 兼容,便于移植现有代码。
Socket 类型
c
// Socket 域(地址族)
#define FREERTOS_AF_INET 2 // IPv4
#define FREERTOS_AF_INET6 10 // IPv6
// Socket 类型
#define FREERTOS_SOCK_STREAM 1 // TCP(流式)
#define FREERTOS_SOCK_DGRAM 2 // UDP(数据报)
// 协议
#define FREERTOS_IPPROTO_TCP 6 // TCP
#define FREERTOS_IPPROTO_UDP 17 // UDP
核心 Socket API
1. 创建 Socket
c
Socket_t FreeRTOS_socket(BaseType_t xDomain, // FREERTOS_AF_INET 或 FREERTOS_AF_INET6
BaseType_t xType, // FREERTOS_SOCK_STREAM 或 FREERTOS_SOCK_DGRAM
BaseType_t xProtocol); // FREERTOS_IPPROTO_TCP 或 FREERTOS_IPPROTO_UDP
// 返回值:
// - 成功:有效的 Socket_t 句柄
// - 失败:FREERTOS_INVALID_SOCKET
示例:
c
// 创建 TCP Socket
Socket_t xTCPSocket = FreeRTOS_socket(FREERTOS_AF_INET,
FREERTOS_SOCK_STREAM,
FREERTOS_IPPROTO_TCP);
// 创建 UDP Socket
Socket_t xUDPSocket = FreeRTOS_socket(FREERTOS_AF_INET,
FREERTOS_SOCK_DGRAM,
FREERTOS_IPPROTO_UDP);
2. 绑定地址和端口
c
BaseType_t FreeRTOS_bind(Socket_t xSocket,
const struct freertos_sockaddr *pxAddress,
socklen_t xAddressLength);
// 返回值:
// - 成功:0
// - 失败:负数(错误码)
地址结构:
c
struct freertos_sockaddr {
uint8_t sin_len; // 结构体长度
uint8_t sin_family; // FREERTOS_AF_INET 或 FREERTOS_AF_INET6
uint16_t sin_port; // 端口号(网络字节序)
uint32_t sin_flowinfo; // IPv6 流信息
IP_Address_t sin_address; // IP 地址
};
示例:
c
struct freertos_sockaddr xBindAddress;
// IPv4 绑定
xBindAddress.sin_family = FREERTOS_AF_INET;
xBindAddress.sin_port = FreeRTOS_htons(8080); // 端口 8080
xBindAddress.sin_address.ulIP_IPv4 = FreeRTOS_inet_addr("0.0.0.0"); // 任意地址
FreeRTOS_bind(xSocket, &xBindAddress, sizeof(xBindAddress));
3. 设置 Socket 选项
c
BaseType_t FreeRTOS_setsockopt(Socket_t xSocket,
int32_t lLevel,
int32_t lOptionName,
const void *pvOptionValue,
size_t uxOptionLength);
常用选项:
| 选项 | 说明 | 值类型 |
|---|---|---|
FREERTOS_SO_RCVTIMEO |
接收超时 | TickType_t |
FREERTOS_SO_SNDTIMEO |
发送超时 | TickType_t |
FREERTOS_SO_RCVBUF |
接收缓冲区大小(TCP) | int32_t |
FREERTOS_SO_SNDBUF |
发送缓冲区大小(TCP) | int32_t |
示例:
c
// 设置接收超时为 5 秒
TickType_t xReceiveTimeout = pdMS_TO_TICKS(5000);
FreeRTOS_setsockopt(xSocket, 0, FREERTOS_SO_RCVTIMEO,
&xReceiveTimeout, sizeof(xReceiveTimeout));
// 设置 TCP 接收缓冲区大小为 8KB
int32_t lRxBufSize = 8192;
FreeRTOS_setsockopt(xSocket, 0, FREERTOS_SO_RCVBUF,
&lRxBufSize, sizeof(lRxBufSize));
4. 关闭 Socket
c
BaseType_t FreeRTOS_closesocket(Socket_t xSocket);
Berkeley Sockets API 完整使用指南
API 与 POSIX Sockets 对比
FreeRTOS-Plus-TCP 的 Berkeley Sockets API 与标准 POSIX sockets API 高度兼容,主要差异如下:
| POSIX API | FreeRTOS API | 说明 |
|---|---|---|
socket() |
FreeRTOS_socket() |
创建 Socket |
bind() |
FreeRTOS_bind() |
绑定地址 |
listen() |
FreeRTOS_listen() |
TCP 监听 |
accept() |
FreeRTOS_accept() |
接受连接 |
connect() |
FreeRTOS_connect() |
建立连接 |
send() |
FreeRTOS_send() |
TCP 发送 |
recv() |
FreeRTOS_recv() |
TCP 接收 |
sendto() |
FreeRTOS_sendto() |
UDP 发送 |
recvfrom() |
FreeRTOS_recvfrom() |
UDP 接收 |
shutdown() |
FreeRTOS_shutdown() |
关闭连接 |
close() |
FreeRTOS_closesocket() |
关闭 Socket |
setsockopt() |
FreeRTOS_setsockopt() |
设置选项 |
select() |
FreeRTOS_select() |
多路复用 |
inet_addr() |
FreeRTOS_inet_addr() |
地址转换 |
inet_ntoa() |
FreeRTOS_inet_ntoa() |
地址转换 |
inet_pton() |
FreeRTOS_inet_pton() |
IPv4/IPv6 地址转换 |
inet_ntop() |
FreeRTOS_inet_ntop() |
IPv4/IPv6 地址转换 |
htons() / htonl() |
FreeRTOS_htons() / FreeRTOS_htonl() |
字节序转换 |
ntohs() / ntohl() |
FreeRTOS_ntohs() / FreeRTOS_ntohl() |
字节序转换 |
主要差异:
- 所有函数都带有
FreeRTOS_前缀 - Socket 类型为
Socket_t而非int - 地址结构为
struct freertos_sockaddr而非struct sockaddr - 错误码使用 FreeRTOS 的错误码系统
完整的 Socket API 列表
1. Socket 生命周期管理
c
// 创建 Socket
Socket_t FreeRTOS_socket(BaseType_t xDomain,
BaseType_t xType,
BaseType_t xProtocol);
// 绑定地址
BaseType_t FreeRTOS_bind(Socket_t xSocket,
const struct freertos_sockaddr *pxAddress,
socklen_t xAddressLength);
// 关闭 Socket
BaseType_t FreeRTOS_closesocket(Socket_t xSocket);
// 验证 Socket 有效性
BaseType_t xSocketValid(const ConstSocket_t xSocket);
2. TCP Socket API
c
// 连接到服务器
BaseType_t FreeRTOS_connect(Socket_t xClientSocket,
const struct freertos_sockaddr *pxAddress,
socklen_t xAddressLength);
// 开始监听
BaseType_t FreeRTOS_listen(Socket_t xSocket, BaseType_t xBacklog);
// 接受连接
Socket_t FreeRTOS_accept(Socket_t xServerSocket,
struct freertos_sockaddr *pxAddress,
socklen_t *pxAddressLength);
// 发送数据
BaseType_t FreeRTOS_send(Socket_t xSocket,
const void *pvBuffer,
size_t uxDataLength,
BaseType_t xFlags);
// 接收数据
BaseType_t FreeRTOS_recv(Socket_t xSocket,
void *pvBuffer,
size_t uxBufferLength,
BaseType_t xFlags);
// 关闭连接(半关闭)
BaseType_t FreeRTOS_shutdown(Socket_t xSocket, BaseType_t xHow);
// 检查连接状态
BaseType_t FreeRTOS_issocketconnected(const ConstSocket_t xSocket);
// 获取远程地址
BaseType_t FreeRTOS_GetRemoteAddress(const ConstSocket_t xSocket,
struct freertos_sockaddr *pxAddress);
// 获取本地地址
size_t FreeRTOS_GetLocalAddress(const ConstSocket_t xSocket,
struct freertos_sockaddr *pxAddress);
// 获取接收缓冲区大小
BaseType_t FreeRTOS_rx_size(const ConstSocket_t xSocket);
// 获取发送缓冲区可用空间
BaseType_t FreeRTOS_tx_space(const ConstSocket_t xSocket);
// 获取发送缓冲区已用大小
BaseType_t FreeRTOS_tx_size(const ConstSocket_t xSocket);
3. UDP Socket API
c
// 发送数据
int32_t FreeRTOS_sendto(Socket_t xSocket,
const void *pvBuffer,
size_t uxTotalDataLength,
BaseType_t xFlags,
const struct freertos_sockaddr *pxDestinationAddress,
socklen_t xDestinationAddressLength);
// 接收数据
int32_t FreeRTOS_recvfrom(const ConstSocket_t xSocket,
void *pvBuffer,
size_t uxBufferLength,
BaseType_t xFlags,
struct freertos_sockaddr *pxSourceAddress,
socklen_t *pxSourceAddressLength);
4. Socket 选项设置
c
BaseType_t FreeRTOS_setsockopt(Socket_t xSocket,
int32_t lLevel,
int32_t lOptionName,
const void *pvOptionValue,
size_t uxOptionLength);
完整的 Socket 选项列表:
| 选项 | 说明 | 值类型 | 适用协议 |
|---|---|---|---|
FREERTOS_SO_RCVTIMEO |
接收超时 | TickType_t |
TCP/UDP |
FREERTOS_SO_SNDTIMEO |
发送超时 | TickType_t |
TCP/UDP |
FREERTOS_SO_RCVBUF |
接收缓冲区大小 | int32_t |
TCP |
FREERTOS_SO_SNDBUF |
发送缓冲区大小 | int32_t |
TCP |
FREERTOS_SO_UDPCKSUM_OUT |
UDP 校验和 | BaseType_t |
UDP |
FREERTOS_SO_TCP_CONN_HANDLER |
TCP 连接回调 | F_TCP_UDP_Handler_t* |
TCP |
FREERTOS_SO_TCP_RECV_HANDLER |
TCP 接收回调 | F_TCP_UDP_Handler_t* |
TCP |
FREERTOS_SO_TCP_SENT_HANDLER |
TCP 发送回调 | F_TCP_UDP_Handler_t* |
TCP |
FREERTOS_SO_UDP_RECV_HANDLER |
UDP 接收回调 | F_TCP_UDP_Handler_t* |
UDP |
FREERTOS_SO_UDP_SENT_HANDLER |
UDP 发送回调 | F_TCP_UDP_Handler_t* |
UDP |
FREERTOS_SO_REUSE_LISTEN_SOCKET |
重用监听 Socket | BaseType_t |
TCP |
FREERTOS_SO_CLOSE_AFTER_SEND |
发送后关闭 | BaseType_t |
TCP |
FREERTOS_SO_WIN_PROPERTIES |
TCP 窗口属性 | WinProperties_t* |
TCP |
FREERTOS_SO_SET_FULL_SIZE |
拒绝小包 | BaseType_t |
TCP |
FREERTOS_SO_STOP_RX |
暂停接收 | BaseType_t |
TCP |
FREERTOS_SO_UDP_MAX_RX_PACKETS |
UDP 最大接收包数 | BaseType_t |
UDP |
5. 地址转换函数
c
// IPv4 地址:字符串转 32 位整数
uint32_t FreeRTOS_inet_addr(const char *pcIPAddress);
// 示例:FreeRTOS_inet_addr("192.168.1.100")
// IPv4 地址:32 位整数转字符串
const char *FreeRTOS_inet_ntoa(uint32_t ulIPAddress, char *pcBuffer);
// 示例:FreeRTOS_inet_ntoa(ulIP, cIPString);
// IPv4/IPv6 地址:字符串转二进制(POSIX 兼容)
BaseType_t FreeRTOS_inet_pton(BaseType_t xAddressFamily,
const char *pcSource,
void *pvDestination);
// 示例:FreeRTOS_inet_pton(FREERTOS_AF_INET, "192.168.1.100", &ulIP);
// IPv4/IPv6 地址:二进制转字符串(POSIX 兼容)
const char *FreeRTOS_inet_ntop(BaseType_t xAddressFamily,
const void *pvSource,
char *pcDestination,
socklen_t uxSize);
// 示例:FreeRTOS_inet_ntop(FREERTOS_AF_INET, &ulIP, cIPString, 16);
// IPv4 专用函数
BaseType_t FreeRTOS_inet_pton4(const char *pcSource, void *pvDestination);
const char *FreeRTOS_inet_ntop4(const void *pvSource,
char *pcDestination,
socklen_t uxSize);
// IPv6 专用函数
BaseType_t FreeRTOS_inet_pton6(const char *pcSource, void *pvDestination);
const char *FreeRTOS_inet_ntop6(const void *pvSource,
char *pcDestination,
socklen_t uxSize);
地址转换示例:
c
// IPv4 地址转换
uint32_t ulIPAddress;
char cIPString[16];
// 字符串 -> 整数
ulIPAddress = FreeRTOS_inet_addr("192.168.1.100");
// 整数 -> 字符串
FreeRTOS_inet_ntoa(ulIPAddress, cIPString);
// cIPString = "192.168.1.100"
// 使用 inet_pton/ntop(推荐,支持 IPv6)
FreeRTOS_inet_pton(FREERTOS_AF_INET, "192.168.1.100", &ulIPAddress);
FreeRTOS_inet_ntop(FREERTOS_AF_INET, &ulIPAddress, cIPString, sizeof(cIPString));
// IPv6 地址转换
IPv6_Address_t xIPv6Address;
char cIPv6String[40];
FreeRTOS_inet_pton6("2001:db8::1", &xIPv6Address);
FreeRTOS_inet_ntop6(&xIPv6Address, cIPv6String, sizeof(cIPv6String));
// cIPv6String = "2001:db8::1"
6. 字节序转换函数
c
// Host to Network(主机字节序转网络字节序)
uint16_t FreeRTOS_htons(uint16_t usHostShort);
uint32_t FreeRTOS_htonl(uint32_t ulHostLong);
// Network to Host(网络字节序转主机字节序)
uint16_t FreeRTOS_ntohs(uint16_t usNetworkShort);
uint32_t FreeRTOS_ntohl(uint32_t ulNetworkLong);
字节序转换示例:
c
// 端口号转换(必须使用网络字节序)
uint16_t usPort = 8080;
uint16_t usNetworkPort = FreeRTOS_htons(usPort); // 主机字节序 -> 网络字节序
// 接收数据后转换
uint16_t usReceivedPort = FreeRTOS_ntohs(usNetworkPort); // 网络字节序 -> 主机字节序
7. Select 多路复用(I/O 多路复用)
select() 函数允许同时监控多个 Socket,等待其中一个或多个 Socket 变为可读/可写状态。
配置:
c
// 在 FreeRTOSIPConfig.h 中启用
#define ipconfigSUPPORT_SELECT_FUNCTION 1
API:
c
// 创建 Socket 集合
SocketSet_t FreeRTOS_CreateSocketSet(void);
// 删除 Socket 集合
void FreeRTOS_DeleteSocketSet(SocketSet_t xSocketSet);
// 将 Socket 添加到集合,并设置关注的事件
void FreeRTOS_FD_SET(Socket_t xSocket,
SocketSet_t xSocketSet,
EventBits_t xBitsToSet);
// 从集合中清除事件位
void FreeRTOS_FD_CLR(Socket_t xSocket,
SocketSet_t xSocketSet,
EventBits_t xBitsToClear);
// 检查 Socket 是否有事件发生
EventBits_t FreeRTOS_FD_ISSET(const ConstSocket_t xSocket,
const ConstSocketSet_t xSocketSet);
// 等待 Socket 集合中的事件(阻塞)
BaseType_t FreeRTOS_select(SocketSet_t xSocketSet,
TickType_t xBlockTimeTicks);
事件类型:
c
typedef enum eSELECT_EVENT {
eSELECT_READ = 0x0001, // Socket 可读
eSELECT_WRITE = 0x0002, // Socket 可写
eSELECT_EXCEPT = 0x0004, // Socket 异常
eSELECT_INTR = 0x0008, // 中断
eSELECT_ALL = 0x000F // 所有事件
} eSelectEvent_t;
Select 使用示例:
c
void vSelectExample(void) {
Socket_t xTCPSocket, xUDPSocket;
SocketSet_t xSocketSet;
BaseType_t xResult;
// 创建 Socket 集合
xSocketSet = FreeRTOS_CreateSocketSet();
// 创建两个 Socket
xTCPSocket = FreeRTOS_socket(FREERTOS_AF_INET,
FREERTOS_SOCK_STREAM,
FREERTOS_IPPROTO_TCP);
xUDPSocket = FreeRTOS_socket(FREERTOS_AF_INET,
FREERTOS_SOCK_DGRAM,
FREERTOS_IPPROTO_UDP);
// 将 Socket 添加到集合,关注可读事件
FreeRTOS_FD_SET(xTCPSocket, xSocketSet, eSELECT_READ);
FreeRTOS_FD_SET(xUDPSocket, xSocketSet, eSELECT_READ);
for(;;) {
// 等待事件发生(阻塞 1 秒)
xResult = FreeRTOS_select(xSocketSet, pdMS_TO_TICKS(1000));
if( xResult > 0 ) {
// 检查 TCP Socket 是否可读
if( FreeRTOS_FD_ISSET(xTCPSocket, xSocketSet) & eSELECT_READ ) {
// TCP Socket 有数据可读
char cBuffer[128];
FreeRTOS_recv(xTCPSocket, cBuffer, sizeof(cBuffer), 0);
}
// 检查 UDP Socket 是否可读
if( FreeRTOS_FD_ISSET(xUDPSocket, xSocketSet) & eSELECT_READ ) {
// UDP Socket 有数据可读
char cBuffer[128];
struct freertos_sockaddr xFromAddress;
socklen_t xAddressLength = sizeof(xFromAddress);
FreeRTOS_recvfrom(xUDPSocket, cBuffer, sizeof(cBuffer), 0,
&xFromAddress, &xAddressLength);
}
} else if( xResult == 0 ) {
// 超时,没有事件发生
} else {
// 错误
}
}
// 清理
FreeRTOS_closesocket(xTCPSocket);
FreeRTOS_closesocket(xUDPSocket);
FreeRTOS_DeleteSocketSet(xSocketSet);
}
错误处理
错误码
FreeRTOS-Plus-TCP 使用 FreeRTOS 的错误码系统:
c
// 常见错误码(在 FreeRTOS.h 中定义)
#define pdFREERTOS_ERRNO_EWOULDBLOCK 11 // 操作会阻塞
#define pdFREERTOS_ERRNO_EINVAL 22 // 无效参数
#define pdFREERTOS_ERRNO_EADDRNOTAVAIL 99 // 地址不可用
#define pdFREERTOS_ERRNO_EADDRINUSE 98 // 地址已使用
#define pdFREERTOS_ERRNO_ENOBUFS 55 // 无缓冲区
#define pdFREERTOS_ERRNO_ENOTCONN 107 // 未连接
错误处理示例
c
BaseType_t xConnectWithRetry(Socket_t xSocket,
const struct freertos_sockaddr *pxAddress,
socklen_t xAddressLength,
TickType_t xTimeout) {
BaseType_t xResult;
TickType_t xStartTime = xTaskGetTickCount();
do {
xResult = FreeRTOS_connect(xSocket, pxAddress, xAddressLength);
if( xResult == 0 ) {
// 连接成功
return pdPASS;
}
// 检查是否超时
if( (xTaskGetTickCount() - xStartTime) > xTimeout ) {
return pdFAIL;
}
// 等待后重试
vTaskDelay(pdMS_TO_TICKS(100));
} while( xResult != 0 );
return pdFAIL;
}
// 使用示例
void vTCPClientWithErrorHandling(void) {
Socket_t xSocket;
struct freertos_sockaddr xServerAddress;
BaseType_t xResult;
char cRxBuffer[128];
// 创建 Socket
xSocket = FreeRTOS_socket(FREERTOS_AF_INET,
FREERTOS_SOCK_STREAM,
FREERTOS_IPPROTO_TCP);
if( xSocket == FREERTOS_INVALID_SOCKET ) {
// Socket 创建失败
return;
}
// 设置服务器地址
xServerAddress.sin_family = FREERTOS_AF_INET;
xServerAddress.sin_port = FreeRTOS_htons(80);
xServerAddress.sin_address.ulIP_IPv4 = FreeRTOS_inet_addr("192.168.1.100");
// 连接(带重试)
xResult = xConnectWithRetry(xSocket, &xServerAddress,
sizeof(xServerAddress),
pdMS_TO_TICKS(5000));
if( xResult == pdPASS ) {
// 发送数据
const char *pcMessage = "Hello";
BaseType_t lBytesSent = FreeRTOS_send(xSocket, pcMessage,
strlen(pcMessage), 0);
if( lBytesSent < 0 ) {
// 发送失败,检查错误码
if( lBytesSent == -pdFREERTOS_ERRNO_EWOULDBLOCK ) {
// 操作会阻塞(非阻塞模式)
} else if( lBytesSent == -pdFREERTOS_ERRNO_ENOTCONN ) {
// Socket 未连接
}
}
// 接收数据
BaseType_t lBytesReceived = FreeRTOS_recv(xSocket, cRxBuffer,
sizeof(cRxBuffer) - 1, 0);
if( lBytesReceived < 0 ) {
// 接收失败
if( lBytesReceived == -pdFREERTOS_ERRNO_EWOULDBLOCK ) {
// 没有数据可读(非阻塞模式)
}
} else if( lBytesReceived > 0 ) {
cRxBuffer[lBytesReceived] = '\0';
// 处理接收到的数据
}
}
// 关闭 Socket
FreeRTOS_closesocket(xSocket);
}
完整的编程流程
TCP 客户端完整流程
c
#include "FreeRTOS.h"
#include "FreeRTOS_Sockets.h"
#include "FreeRTOS_DNS.h"
#include "task.h"
BaseType_t xCreateTCPClient(Socket_t *pxSocket,
const char *pcHostName,
uint16_t usPort) {
struct freertos_sockaddr xServerAddress;
uint32_t ulServerIP;
TickType_t xTimeout;
// 1. DNS 查询
if( FreeRTOS_gethostbyname(pcHostName, &ulServerIP, 5000) != 0 ) {
return pdFAIL;
}
// 2. 创建 TCP Socket
*pxSocket = FreeRTOS_socket(FREERTOS_AF_INET,
FREERTOS_SOCK_STREAM,
FREERTOS_IPPROTO_TCP);
if( *pxSocket == FREERTOS_INVALID_SOCKET ) {
return pdFAIL;
}
// 3. 设置接收超时
xTimeout = pdMS_TO_TICKS(5000);
FreeRTOS_setsockopt(*pxSocket, 0, FREERTOS_SO_RCVTIMEO,
&xTimeout, sizeof(xTimeout));
// 4. 设置服务器地址
xServerAddress.sin_family = FREERTOS_AF_INET;
xServerAddress.sin_port = FreeRTOS_htons(usPort);
xServerAddress.sin_address.ulIP_IPv4 = ulServerIP;
// 5. 连接到服务器
if( FreeRTOS_connect(*pxSocket, &xServerAddress,
sizeof(xServerAddress)) != 0 ) {
FreeRTOS_closesocket(*pxSocket);
return pdFAIL;
}
return pdPASS;
}
void vTCPClientTask(void *pvParameters) {
Socket_t xSocket;
char cRxBuffer[512];
const char *pcMessage = "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n";
// 创建并连接
if( xCreateTCPClient(&xSocket, "www.example.com", 80) == pdPASS ) {
// 发送请求
FreeRTOS_send(xSocket, pcMessage, strlen(pcMessage), 0);
// 接收响应
BaseType_t lBytesReceived;
do {
lBytesReceived = FreeRTOS_recv(xSocket, cRxBuffer,
sizeof(cRxBuffer) - 1, 0);
if( lBytesReceived > 0 ) {
cRxBuffer[lBytesReceived] = '\0';
// 处理数据
}
} while( lBytesReceived > 0 );
// 关闭连接
FreeRTOS_shutdown(xSocket, FREERTOS_SHUT_RDWR);
FreeRTOS_closesocket(xSocket);
}
}
TCP 服务器完整流程
c
BaseType_t xCreateTCPServer(Socket_t *pxListeningSocket, uint16_t usPort) {
struct freertos_sockaddr xBindAddress;
// 1. 创建 TCP Socket
*pxListeningSocket = FreeRTOS_socket(FREERTOS_AF_INET,
FREERTOS_SOCK_STREAM,
FREERTOS_IPPROTO_TCP);
if( *pxListeningSocket == FREERTOS_INVALID_SOCKET ) {
return pdFAIL;
}
// 2. 绑定地址和端口
xBindAddress.sin_family = FREERTOS_AF_INET;
xBindAddress.sin_port = FreeRTOS_htons(usPort);
xBindAddress.sin_address.ulIP_IPv4 = FreeRTOS_inet_addr("0.0.0.0");
if( FreeRTOS_bind(*pxListeningSocket, &xBindAddress,
sizeof(xBindAddress)) != 0 ) {
FreeRTOS_closesocket(*pxListeningSocket);
return pdFAIL;
}
// 3. 开始监听
if( FreeRTOS_listen(*pxListeningSocket, 5) != 0 ) {
FreeRTOS_closesocket(*pxListeningSocket);
return pdFAIL;
}
return pdPASS;
}
void vTCPServerTask(void *pvParameters) {
Socket_t xListeningSocket, xClientSocket;
struct freertos_sockaddr xClientAddress;
socklen_t xAddressLength;
char cRxBuffer[128];
// 创建服务器
if( xCreateTCPServer(&xListeningSocket, 8080) == pdPASS ) {
for(;;) {
// 接受连接
xAddressLength = sizeof(xClientAddress);
xClientSocket = FreeRTOS_accept(xListeningSocket,
&xClientAddress,
&xAddressLength);
if( xClientSocket != FREERTOS_INVALID_SOCKET ) {
// 处理客户端请求
BaseType_t lBytesReceived = FreeRTOS_recv(xClientSocket,
cRxBuffer,
sizeof(cRxBuffer) - 1,
0);
if( lBytesReceived > 0 ) {
cRxBuffer[lBytesReceived] = '\0';
// 发送响应
const char *pcResponse = "OK";
FreeRTOS_send(xClientSocket, pcResponse,
strlen(pcResponse), 0);
}
// 关闭客户端连接
FreeRTOS_shutdown(xClientSocket, FREERTOS_SHUT_RDWR);
FreeRTOS_closesocket(xClientSocket);
}
}
}
}
最佳实践
1. Socket 资源管理
c
// 总是检查 Socket 创建是否成功
Socket_t xSocket = FreeRTOS_socket(...);
if( xSocket == FREERTOS_INVALID_SOCKET ) {
// 处理错误
return;
}
// 使用完毕后总是关闭 Socket
FreeRTOS_closesocket(xSocket);
2. 超时设置
c
// 为所有 Socket 设置合理的超时
TickType_t xTimeout = pdMS_TO_TICKS(5000); // 5 秒
FreeRTOS_setsockopt(xSocket, 0, FREERTOS_SO_RCVTIMEO,
&xTimeout, sizeof(xTimeout));
FreeRTOS_setsockopt(xSocket, 0, FREERTOS_SO_SNDTIMEO,
&xTimeout, sizeof(xTimeout));
3. 错误处理
c
// 检查所有 API 调用的返回值
BaseType_t xResult = FreeRTOS_connect(xSocket, ...);
if( xResult != 0 ) {
// 处理连接失败
// 检查错误码:xResult == -pdFREERTOS_ERRNO_Exxx
}
4. 非阻塞 I/O
c
// 使用非阻塞模式进行 I/O 操作
BaseType_t xFlags = FREERTOS_MSG_DONTWAIT;
BaseType_t lBytes = FreeRTOS_recv(xSocket, cBuffer, sizeof(cBuffer),
xFlags);
if( lBytes == -pdFREERTOS_ERRNO_EWOULDBLOCK ) {
// 没有数据可读,稍后重试
}
5. 使用 Select 进行多路复用
c
// 当需要同时处理多个 Socket 时,使用 select()
SocketSet_t xSocketSet = FreeRTOS_CreateSocketSet();
FreeRTOS_FD_SET(xSocket1, xSocketSet, eSELECT_READ);
FreeRTOS_FD_SET(xSocket2, xSocketSet, eSELECT_READ);
BaseType_t xResult = FreeRTOS_select(xSocketSet, pdMS_TO_TICKS(1000));
if( xResult > 0 ) {
// 检查哪个 Socket 有事件
}
6. 地址转换
c
// 使用 inet_pton/ntop 而不是 inet_addr/ntoa(支持 IPv6)
uint32_t ulIP;
FreeRTOS_inet_pton(FREERTOS_AF_INET, "192.168.1.100", &ulIP);
char cIPString[16];
FreeRTOS_inet_ntop(FREERTOS_AF_INET, &ulIP, cIPString, sizeof(cIPString));
7. 字节序转换
c
// 端口号必须使用网络字节序
uint16_t usPort = FreeRTOS_htons(8080); // 主机字节序 -> 网络字节序
// 接收后转换回主机字节序
uint16_t usReceivedPort = FreeRTOS_ntohs(usNetworkPort);
8. TCP 连接管理
c
// 检查连接状态
if( FreeRTOS_issocketconnected(xSocket) == pdFALSE ) {
// 连接已断开,需要重新连接
}
// 优雅关闭连接
FreeRTOS_shutdown(xSocket, FREERTOS_SHUT_RDWR); // 先关闭读写
FreeRTOS_closesocket(xSocket); // 再关闭 Socket
9. 缓冲区管理
c
// 检查 TCP 缓冲区状态
BaseType_t lRxSize = FreeRTOS_rx_size(xSocket); // 接收缓冲区数据量
BaseType_t lTxSpace = FreeRTOS_tx_space(xSocket); // 发送缓冲区可用空间
// 根据缓冲区状态决定是否发送/接收
if( lTxSpace > 0 ) {
// 可以发送数据
}
10. 任务优先级和网络任务
c
// 网络任务应该有合适的优先级
// IP 任务优先级应该高于应用任务
// 在 FreeRTOSIPConfig.h 中配置:
#define ipconfigIP_TASK_PRIORITY ( configMAX_PRIORITIES - 2 )
TCP 编程
TCP 客户端示例
c
#include "FreeRTOS.h"
#include "FreeRTOS_Sockets.h"
#include "task.h"
void vTCPClientTask(void *pvParameters) {
Socket_t xSocket;
struct freertos_sockaddr xServerAddress;
int32_t lBytesSent, lBytesReceived;
char cRxBuffer[128];
const char *pcMessage = "Hello from FreeRTOS!";
// 1. 创建 TCP Socket
xSocket = FreeRTOS_socket(FREERTOS_AF_INET,
FREERTOS_SOCK_STREAM,
FREERTOS_IPPROTO_TCP);
if( xSocket == FREERTOS_INVALID_SOCKET ) {
// 创建失败
return;
}
// 2. 设置服务器地址
xServerAddress.sin_family = FREERTOS_AF_INET;
xServerAddress.sin_port = FreeRTOS_htons(80); // HTTP 端口
xServerAddress.sin_address.ulIP_IPv4 = FreeRTOS_inet_addr("192.168.1.100");
// 3. 连接到服务器
if( FreeRTOS_connect(xSocket, &xServerAddress, sizeof(xServerAddress)) != 0 ) {
// 连接失败
FreeRTOS_closesocket(xSocket);
return;
}
// 4. 发送数据
lBytesSent = FreeRTOS_send(xSocket, pcMessage, strlen(pcMessage), 0);
if( lBytesSent < 0 ) {
// 发送失败
}
// 5. 接收数据
lBytesReceived = FreeRTOS_recv(xSocket, cRxBuffer, sizeof(cRxBuffer) - 1, 0);
if( lBytesReceived > 0 ) {
cRxBuffer[lBytesReceived] = '\0';
// 处理接收到的数据
}
// 6. 关闭连接
FreeRTOS_shutdown(xSocket, FREERTOS_SHUT_RDWR);
FreeRTOS_closesocket(xSocket);
}
TCP 服务器示例
c
void vTCPServerTask(void *pvParameters) {
Socket_t xListeningSocket, xClientSocket;
struct freertos_sockaddr xBindAddress, xClientAddress;
socklen_t xAddressLength = sizeof(xClientAddress);
char cRxBuffer[128];
int32_t lBytesReceived;
// 1. 创建监听 Socket
xListeningSocket = FreeRTOS_socket(FREERTOS_AF_INET,
FREERTOS_SOCK_STREAM,
FREERTOS_IPPROTO_TCP);
// 2. 绑定地址和端口
xBindAddress.sin_family = FREERTOS_AF_INET;
xBindAddress.sin_port = FreeRTOS_htons(8080);
xBindAddress.sin_address.ulIP_IPv4 = FreeRTOS_inet_addr("0.0.0.0");
FreeRTOS_bind(xListeningSocket, &xBindAddress, sizeof(xBindAddress));
// 3. 开始监听(backlog = 5)
FreeRTOS_listen(xListeningSocket, 5);
for(;;) {
// 4. 接受连接
xClientSocket = FreeRTOS_accept(xListeningSocket,
&xClientAddress,
&xAddressLength);
if( xClientSocket != FREERTOS_INVALID_SOCKET ) {
// 5. 接收数据
lBytesReceived = FreeRTOS_recv(xClientSocket,
cRxBuffer,
sizeof(cRxBuffer) - 1,
0);
if( lBytesReceived > 0 ) {
cRxBuffer[lBytesReceived] = '\0';
// 6. 发送响应
const char *pcResponse = "OK";
FreeRTOS_send(xClientSocket, pcResponse, strlen(pcResponse), 0);
}
// 7. 关闭客户端连接
FreeRTOS_shutdown(xClientSocket, FREERTOS_SHUT_RDWR);
FreeRTOS_closesocket(xClientSocket);
}
}
}
TCP 高级特性
1. 非阻塞 Socket
c
// 设置非阻塞模式
BaseType_t xFlags = FREERTOS_MSG_DONTWAIT;
FreeRTOS_setsockopt(xSocket, 0, FREERTOS_SO_RCVTIMEO, &xFlags, sizeof(xFlags));
// 非阻塞接收
int32_t lBytes = FreeRTOS_recv(xSocket, cBuffer, sizeof(cBuffer), FREERTOS_MSG_DONTWAIT);
if( lBytes == -pdFREERTOS_ERRNO_EWOULDBLOCK ) {
// 没有数据可读
}
2. 零拷贝接收(Zero-Copy)
c
void *pvBuffer;
int32_t lBytesReceived;
// 使用零拷贝接收
lBytesReceived = FreeRTOS_recv(xSocket, &pvBuffer, sizeof(pvBuffer), FREERTOS_ZERO_COPY);
if( lBytesReceived > 0 ) {
// 直接使用 pvBuffer(指向网络缓冲区)
// 处理数据...
// 释放缓冲区
FreeRTOS_ReleaseTCPPayloadBuffer(xSocket, pvBuffer, lBytesReceived);
}
3. TCP 窗口和缓冲区配置
c
// 设置 TCP 窗口属性
WinProperties_t xWinProps;
xWinProps.lTxBufSize = 8192; // 发送缓冲区:8KB
xWinProps.lTxWinSize = 4; // 发送窗口:4 MSS
xWinProps.lRxBufSize = 8192; // 接收缓冲区:8KB
xWinProps.lRxWinSize = 4; // 接收窗口:4 MSS
FreeRTOS_setsockopt(xSocket, 0, FREERTOS_SO_WIN_PROPERTIES,
&xWinProps, sizeof(xWinProps));
4. 检查连接状态
c
// 检查 Socket 是否已连接
if( FreeRTOS_issocketconnected(xSocket) == pdTRUE ) {
// Socket 已连接
}
// 获取远程地址
struct freertos_sockaddr xRemoteAddress;
FreeRTOS_GetRemoteAddress(xSocket, &xRemoteAddress);
UDP 编程
UDP 客户端示例
c
void vUDPClientTask(void *pvParameters) {
Socket_t xSocket;
struct freertos_sockaddr xServerAddress;
int32_t lBytesSent, lBytesReceived;
char cRxBuffer[128];
const char *pcMessage = "UDP Message";
// 1. 创建 UDP Socket
xSocket = FreeRTOS_socket(FREERTOS_AF_INET,
FREERTOS_SOCK_DGRAM,
FREERTOS_IPPROTO_UDP);
// 2. 设置服务器地址
xServerAddress.sin_family = FREERTOS_AF_INET;
xServerAddress.sin_port = FreeRTOS_htons(12345);
xServerAddress.sin_address.ulIP_IPv4 = FreeRTOS_inet_addr("192.168.1.100");
for(;;) {
// 3. 发送数据(UDP 无连接,每次发送都指定地址)
lBytesSent = FreeRTOS_sendto(xSocket,
pcMessage,
strlen(pcMessage),
0,
&xServerAddress,
sizeof(xServerAddress));
// 4. 接收数据
struct freertos_sockaddr xSourceAddress;
socklen_t xSourceAddressLength = sizeof(xSourceAddress);
lBytesReceived = FreeRTOS_recvfrom(xSocket,
cRxBuffer,
sizeof(cRxBuffer) - 1,
0,
&xSourceAddress,
&xSourceAddressLength);
if( lBytesReceived > 0 ) {
cRxBuffer[lBytesReceived] = '\0';
// 处理接收到的数据
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
FreeRTOS_closesocket(xSocket);
}
UDP 服务器示例
c
void vUDPServerTask(void *pvParameters) {
Socket_t xSocket;
struct freertos_sockaddr xBindAddress, xClientAddress;
socklen_t xAddressLength = sizeof(xClientAddress);
char cRxBuffer[128];
int32_t lBytesReceived;
// 1. 创建 UDP Socket
xSocket = FreeRTOS_socket(FREERTOS_AF_INET,
FREERTOS_SOCK_DGRAM,
FREERTOS_IPPROTO_UDP);
// 2. 绑定地址和端口
xBindAddress.sin_family = FREERTOS_AF_INET;
xBindAddress.sin_port = FreeRTOS_htons(12345);
xBindAddress.sin_address.ulIP_IPv4 = FreeRTOS_inet_addr("0.0.0.0");
FreeRTOS_bind(xSocket, &xBindAddress, sizeof(xBindAddress));
for(;;) {
// 3. 接收数据(UDP 无连接,直接接收)
lBytesReceived = FreeRTOS_recvfrom(xSocket,
cRxBuffer,
sizeof(cRxBuffer) - 1,
0,
&xClientAddress,
&xAddressLength);
if( lBytesReceived > 0 ) {
cRxBuffer[lBytesReceived] = '\0';
// 4. 发送响应(使用接收到的客户端地址)
const char *pcResponse = "Echo: ";
FreeRTOS_sendto(xSocket,
pcResponse,
strlen(pcResponse),
0,
&xClientAddress,
sizeof(xClientAddress));
}
}
FreeRTOS_closesocket(xSocket);
}
UDP 广播和组播
c
// UDP 广播示例
void vUDPBroadcastTask(void *pvParameters) {
Socket_t xSocket;
struct freertos_sockaddr xBroadcastAddress;
const char *pcBroadcastMessage = "Broadcast Message";
xSocket = FreeRTOS_socket(FREERTOS_AF_INET,
FREERTOS_SOCK_DGRAM,
FREERTOS_IPPROTO_UDP);
// 设置广播地址(255.255.255.255)
xBroadcastAddress.sin_family = FREERTOS_AF_INET;
xBroadcastAddress.sin_port = FreeRTOS_htons(12345);
xBroadcastAddress.sin_address.ulIP_IPv4 = FreeRTOS_inet_addr("255.255.255.255");
// 发送广播
FreeRTOS_sendto(xSocket,
pcBroadcastMessage,
strlen(pcBroadcastMessage),
0,
&xBroadcastAddress,
sizeof(xBroadcastAddress));
FreeRTOS_closesocket(xSocket);
}
网络配置
1. IP 地址配置
静态 IP 配置
c
#include "FreeRTOS_IP.h"
#include "FreeRTOS_Sockets.h"
void vConfigureStaticIP(void) {
struct xNetworkEndPoint *pxEndPoint;
NetworkInterface_t *pxInterface;
// 获取网络接口
pxInterface = pxFillInterfaceDescriptor();
// 创建端点
pxEndPoint = FreeRTOS_FirstEndPoint();
// 设置静态 IP 地址
FreeRTOS_SetEndPointConfiguration(
pxEndPoint,
FreeRTOS_inet_addr("192.168.1.100"), // IP 地址
FreeRTOS_inet_addr("255.255.255.0"), // 子网掩码
FreeRTOS_inet_addr("192.168.1.1"), // 网关
FreeRTOS_inet_addr("8.8.8.8") // DNS 服务器
);
// 初始化网络栈
FreeRTOS_IPInit_Multi();
}
DHCP 自动配置
c
void vConfigureDHCP(void) {
// 在 FreeRTOSIPConfig.h 中启用 DHCP
// #define ipconfigUSE_DHCP 1
// DHCP 会自动配置,等待网络就绪
while( FreeRTOS_IsNetworkUp() == pdFALSE ) {
vTaskDelay(pdMS_TO_TICKS(100));
}
// 获取分配的 IP 地址
uint32_t ulIPAddress = FreeRTOS_GetIPAddress();
// ulIPAddress 现在包含 DHCP 分配的 IP 地址
}
2. DNS 配置和使用
DNS 查询
c
#include "FreeRTOS_DNS.h"
void vDNSQueryTask(void *pvParameters) {
uint32_t ulIPAddress;
// 查询域名对应的 IP 地址
if( FreeRTOS_gethostbyname("www.example.com", &ulIPAddress, 5000) == 0 ) {
// 查询成功,ulIPAddress 包含 IP 地址
char cIPString[16];
FreeRTOS_inet_ntoa(ulIPAddress, cIPString);
// 使用 IP 地址连接
} else {
// 查询失败或超时
}
}
异步 DNS 查询
c
// DNS 回调函数
void vDNSQueryCallback(const char *pcName,
void *pvSearchID,
uint32_t ulIPAddress) {
if( ulIPAddress != 0 ) {
// DNS 查询成功
// ulIPAddress 包含解析的 IP 地址
} else {
// DNS 查询失败
}
}
// 启动异步 DNS 查询
void vStartAsyncDNSQuery(void) {
void *pvSearchID = NULL;
FreeRTOS_gethostbyname_a("www.example.com",
vDNSQueryCallback,
&pvSearchID,
5000);
// 查询在后台进行,完成后调用回调函数
}
3. ARP 缓存管理
c
#include "FreeRTOS_ARP.h"
void vARPCacheExample(void) {
uint32_t ulIPAddress = FreeRTOS_inet_addr("192.168.1.1");
MACAddress_t xMACAddress;
eARPLookupResult_t eResult;
// 查找 ARP 缓存
eResult = eARPGetCacheEntry(&ulIPAddress, &xMACAddress);
if( eResult == eARPCacheHit ) {
// ARP 缓存命中,可以使用 MAC 地址
} else if( eResult == eARPCacheMiss ) {
// ARP 缓存未命中,需要发送 ARP 请求
// FreeRTOS-Plus-TCP 会自动处理
}
// 清除 ARP 缓存
vARPClearCache();
// 打印 ARP 缓存
FreeRTOS_PrintARPCache();
}
4. ICMP Ping
接收 Ping(自动回复)
c
// 在 FreeRTOSIPConfig.h 中启用
#define ipconfigREPLY_TO_INCOMING_PINGS 1
// FreeRTOS-Plus-TCP 会自动回复 ICMP Echo Request
发送 Ping
c
#include "FreeRTOS_ICMP.h"
void vSendPing(void) {
uint32_t ulIPAddress = FreeRTOS_inet_addr("192.168.1.1");
uint16_t usIdentifier = 1;
uint16_t usSequenceNumber = 1;
size_t xDataLength = 32;
// 发送 ICMP Echo Request
BaseType_t xResult = FreeRTOS_SendPingRequest(ulIPAddress,
usIdentifier,
usSequenceNumber,
xDataLength);
if( xResult == pdPASS ) {
// Ping 请求已发送
}
}
协议详解
1. ARP(地址解析协议)
功能: 将 IP 地址解析为 MAC 地址
工作原理:
- 需要发送数据包到某个 IP 地址
- 查找 ARP 缓存表
- 如果未找到,发送 ARP 请求广播
- 目标主机回复 ARP 响应
- 更新 ARP 缓存表
配置:
c
#define ipconfigARP_CACHE_ENTRIES 6 // ARP 缓存条目数(默认 6)
API:
c
// 查找 ARP 缓存
eARPLookupResult_t eARPGetCacheEntry(uint32_t *pulIPAddress,
MACAddress_t *pxMACAddress);
// 刷新 ARP 缓存条目
void vARPRefreshCacheEntry(const MACAddress_t *pxMACAddress,
uint32_t ulIPAddress,
struct xNetworkEndPoint *pxEndPoint);
// 清除 ARP 缓存
void vARPClearCache(void);
// 打印 ARP 缓存(调试用)
void FreeRTOS_PrintARPCache(void);
2. DHCP(动态主机配置协议)
功能: 自动获取 IP 地址、子网掩码、网关、DNS 服务器
工作流程:
1. DISCOVER - 客户端广播发现请求
2. OFFER - 服务器提供 IP 地址
3. REQUEST - 客户端请求分配的 IP
4. ACK - 服务器确认
配置:
c
#define ipconfigUSE_DHCP 1 // 启用 DHCP
#define ipconfigDHCP_FALL_BACK_AUTO_IP 1 // DHCP 失败时使用自动 IP
API:
c
// 启动 DHCP(通常自动启动)
void vDHCPProcess( BaseType_t xReset,
struct xNetworkEndPoint *pxEndPoint );
// 手动更新 DHCP 租约
void vDHCP_Renew( struct xNetworkEndPoint *pxEndPoint );
// 获取 DHCP 状态
eDHCPCallbackAnswer_t xApplicationDHCPHook_Multi(
struct xNetworkEndPoint *pxEndPoint,
struct xDHCP_DATA *pxDHCPData );
DHCP Hook 示例:
c
eDHCPCallbackAnswer_t xApplicationDHCPHook_Multi(
struct xNetworkEndPoint *pxEndPoint,
struct xDHCP_DATA *pxDHCPData )
{
eDHCPCallbackAnswer_t eReturn = eDHCPContinue;
switch( pxDHCPData->eDHCPState ) {
case eDHCPStateGotIP:
// DHCP 成功获取 IP
// pxDHCPData->ulIPAddress 包含分配的 IP
break;
case eDHCPStateFailed:
// DHCP 失败
break;
default:
break;
}
return eReturn;
}
3. DNS(域名系统)
功能: 将域名解析为 IP 地址
支持的 DNS 特性:
- ✅ IPv4 DNS 查询(A 记录)
- ✅ IPv6 DNS 查询(AAAA 记录)
- ✅ DNS 缓存
- ✅ 多个 DNS 服务器支持
- ✅ 异步 DNS 查询
配置:
c
#define ipconfigUSE_DNS 1 // 启用 DNS
#define ipconfigDNS_CACHE_NAME_LENGTH 254 // DNS 缓存名称长度
#define ipconfigDNS_CACHE_ENTRIES 4 // DNS 缓存条目数
#define ipconfigENDPOINT_DNS_ADDRESS_COUNT 2 // 每个端点的 DNS 服务器数量
API:
c
// 同步 DNS 查询
BaseType_t FreeRTOS_gethostbyname(const char *pcHostName,
uint32_t *pulIPAddress,
TickType_t xTimeout);
// 异步 DNS 查询
BaseType_t FreeRTOS_gethostbyname_a(const char *pcHostName,
FOnDNSEvent pCallback,
void **pvSearchID,
TickType_t xTimeout);
// DNS 回调函数类型
typedef void (* FOnDNSEvent)(const char *pcName,
void *pvSearchID,
uint32_t ulIPAddress);
DNS 缓存:
c
// 清除 DNS 缓存
void FreeRTOS_dnsclear(void);
// 获取 DNS 缓存条目
const char *pcDNS_CacheGetHostName(uint32_t ulIPAddress);
4. ICMP(Internet 控制消息协议)
功能:
- Ping(Echo Request/Reply)
- 错误报告
- 网络诊断
配置:
c
#define ipconfigREPLY_TO_INCOMING_PINGS 1 // 自动回复 Ping
#define ipconfigSUPPORT_OUTGOING_PINGS 1 // 支持发送 Ping
API:
c
// 发送 Ping 请求
BaseType_t FreeRTOS_SendPingRequest(uint32_t ulIPAddress,
uint16_t usIdentifier,
uint16_t usSequenceNumber,
size_t xDataLength);
// 处理 ICMP 数据包(内部使用)
eFrameProcessingResult_t ProcessICMPPacket(
const NetworkBufferDescriptor_t *const pxNetworkBuffer );
5. IPv6 协议支持
IPv6 特性:
- ✅ IPv6 地址配置(静态/DHCPv6/SLAAC)
- ✅ 邻居发现(ND)
- ✅ 路由器通告(RA)
- ✅ IPv6 Socket API
- ✅ IPv4/IPv6 双栈支持
IPv6 地址配置:
c
#include "FreeRTOS_IPv6.h"
void vConfigureIPv6(void) {
struct xNetworkEndPoint *pxEndPoint;
IPv6_Address_t xIPv6Address;
// 设置 IPv6 地址
// 例如:2001:db8::1
xIPv6Address.ucBytes[0] = 0x20;
xIPv6Address.ucBytes[1] = 0x01;
// ... 设置其他字节
// 配置 IPv6 端点
FreeRTOS_SetIPv6Address(pxEndPoint, &xIPv6Address, 64); // /64 前缀
}
IPv6 Socket:
c
// 创建 IPv6 Socket
Socket_t xIPv6Socket = FreeRTOS_socket(FREERTOS_AF_INET6,
FREERTOS_SOCK_STREAM,
FREERTOS_IPPROTO_TCP);
// IPv6 地址结构
struct freertos_sockaddr xIPv6Address;
xIPv6Address.sin_family = FREERTOS_AF_INET6;
xIPv6Address.sin_port = FreeRTOS_htons(8080);
// 设置 IPv6 地址...
完整编程示例
示例 1:HTTP 客户端(TCP)
c
#include "FreeRTOS.h"
#include "FreeRTOS_Sockets.h"
#include "FreeRTOS_DNS.h"
#include "task.h"
void vHTTPClientTask(void *pvParameters) {
Socket_t xSocket;
struct freertos_sockaddr xServerAddress;
uint32_t ulServerIP;
char cRxBuffer[512];
const char *pcHTTPRequest =
"GET / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Connection: close\r\n"
"\r\n";
// 1. DNS 查询
if( FreeRTOS_gethostbyname("www.example.com", &ulServerIP, 5000) != 0 ) {
// DNS 查询失败
return;
}
// 2. 创建 TCP Socket
xSocket = FreeRTOS_socket(FREERTOS_AF_INET,
FREERTOS_SOCK_STREAM,
FREERTOS_IPPROTO_TCP);
if( xSocket == FREERTOS_INVALID_SOCKET ) {
return;
}
// 3. 设置接收超时
TickType_t xTimeout = pdMS_TO_TICKS(5000);
FreeRTOS_setsockopt(xSocket, 0, FREERTOS_SO_RCVTIMEO,
&xTimeout, sizeof(xTimeout));
// 4. 连接到服务器
xServerAddress.sin_family = FREERTOS_AF_INET;
xServerAddress.sin_port = FreeRTOS_htons(80);
xServerAddress.sin_address.ulIP_IPv4 = ulServerIP;
if( FreeRTOS_connect(xSocket, &xServerAddress, sizeof(xServerAddress)) != 0 ) {
FreeRTOS_closesocket(xSocket);
return;
}
// 5. 发送 HTTP 请求
FreeRTOS_send(xSocket, pcHTTPRequest, strlen(pcHTTPRequest), 0);
// 6. 接收 HTTP 响应
int32_t lBytesReceived;
do {
lBytesReceived = FreeRTOS_recv(xSocket, cRxBuffer, sizeof(cRxBuffer) - 1, 0);
if( lBytesReceived > 0 ) {
cRxBuffer[lBytesReceived] = '\0';
// 处理 HTTP 响应
}
} while( lBytesReceived > 0 );
// 7. 关闭连接
FreeRTOS_shutdown(xSocket, FREERTOS_SHUT_RDWR);
FreeRTOS_closesocket(xSocket);
}
示例 2:NTP 客户端(UDP)
c
#include "FreeRTOS.h"
#include "FreeRTOS_Sockets.h"
#include "FreeRTOS_DNS.h"
void vNTPClientTask(void *pvParameters) {
Socket_t xSocket;
struct freertos_sockaddr xNTPAddress;
uint32_t ulNTPIP;
uint8_t ucNTPPacket[48] = {0};
int32_t lBytesReceived;
// 1. DNS 查询 NTP 服务器
if( FreeRTOS_gethostbyname("pool.ntp.org", &ulNTPIP, 5000) != 0 ) {
return;
}
// 2. 创建 UDP Socket
xSocket = FreeRTOS_socket(FREERTOS_AF_INET,
FREERTOS_SOCK_DGRAM,
FREERTOS_IPPROTO_UDP);
// 3. 设置 NTP 服务器地址
xNTPAddress.sin_family = FREERTOS_AF_INET;
xNTPAddress.sin_port = FreeRTOS_htons(123); // NTP 端口
xNTPAddress.sin_address.ulIP_IPv4 = ulNTPIP;
// 4. 构建 NTP 请求包
ucNTPPacket[0] = 0x1B; // NTP 请求标志
// 5. 发送 NTP 请求
FreeRTOS_sendto(xSocket, ucNTPPacket, sizeof(ucNTPPacket),
0, &xNTPAddress, sizeof(xNTPAddress));
// 6. 接收 NTP 响应
struct freertos_sockaddr xSourceAddress;
socklen_t xAddressLength = sizeof(xSourceAddress);
lBytesReceived = FreeRTOS_recvfrom(xSocket, ucNTPPacket,
sizeof(ucNTPPacket), 0,
&xSourceAddress, &xAddressLength);
if( lBytesReceived == 48 ) {
// 解析 NTP 时间戳
uint32_t ulSeconds = (ucNTPPacket[40] << 24) |
(ucNTPPacket[41] << 16) |
(ucNTPPacket[42] << 8) |
ucNTPPacket[43];
// ulSeconds 是自 1900-01-01 以来的秒数
}
FreeRTOS_closesocket(xSocket);
}
示例 3:MQTT over TCP(与 coreMQTT 配合)
c
#include "FreeRTOS.h"
#include "FreeRTOS_Sockets.h"
#include "core_mqtt.h"
// 实现 TransportInterface 用于 coreMQTT
int32_t lNetworkSend(NetworkContext_t *pNetworkContext,
const void *pBuffer,
size_t bytesToSend) {
Socket_t xSocket = (Socket_t)pNetworkContext->pParams;
return FreeRTOS_send(xSocket, pBuffer, bytesToSend, 0);
}
int32_t lNetworkRecv(NetworkContext_t *pNetworkContext,
void *pBuffer,
size_t bytesToRecv) {
Socket_t xSocket = (Socket_t)pNetworkContext->pParams;
return FreeRTOS_recv(xSocket, pBuffer, bytesToRecv, 0);
}
void vMQTTClientTask(void *pvParameters) {
Socket_t xSocket;
MQTTContext_t xMQTTContext;
TransportInterface_t xTransport;
NetworkContext_t xNetworkContext;
struct freertos_sockaddr xBrokerAddress;
uint32_t ulBrokerIP;
// 1. DNS 查询 MQTT Broker
FreeRTOS_gethostbyname("mqtt.example.com", &ulBrokerIP, 5000);
// 2. 创建 TCP Socket
xSocket = FreeRTOS_socket(FREERTOS_AF_INET,
FREERTOS_SOCK_STREAM,
FREERTOS_IPPROTO_TCP);
// 3. 连接到 MQTT Broker
xBrokerAddress.sin_family = FREERTOS_AF_INET;
xBrokerAddress.sin_port = FreeRTOS_htons(1883); // MQTT 端口
xBrokerAddress.sin_address.ulIP_IPv4 = ulBrokerIP;
FreeRTOS_connect(xSocket, &xBrokerAddress, sizeof(xBrokerAddress));
// 4. 设置 TransportInterface
xNetworkContext.pParams = (void *)xSocket;
xTransport.pNetworkContext = &xNetworkContext;
xTransport.send = lNetworkSend;
xTransport.recv = lNetworkRecv;
// 5. 初始化 MQTT 上下文
xMQTTContext.transportInterface = xTransport;
// 6. MQTT 连接(使用 coreMQTT API)
MQTTConnectInfo_t xConnectInfo = {0};
xConnectInfo.keepAliveSeconds = 60;
xConnectInfo.pClientIdentifier = "FreeRTOS_Client";
xConnectInfo.clientIdentifierLength = strlen("FreeRTOS_Client");
MQTT_Connect(&xMQTTContext, &xConnectInfo, NULL, 5000, NULL);
// 7. MQTT 发布/订阅(使用 coreMQTT API)
// ...
FreeRTOS_closesocket(xSocket);
}
网络事件处理
网络状态回调
c
// 网络事件回调函数
void vApplicationIPNetworkEventHook_Multi(
struct xNetworkInterface *pxNetworkInterface,
eIPCallbackEvent_t eNetworkEvent )
{
switch( eNetworkEvent ) {
case eNetworkUp:
// 网络已启动
// IP 地址已配置(DHCP 或静态)
uint32_t ulIPAddress = FreeRTOS_GetIPAddress();
break;
case eNetworkDown:
// 网络已断开
break;
}
}
网络初始化流程
c
void vNetworkInit(void) {
NetworkInterface_t *pxInterface;
struct xNetworkEndPoint *pxEndPoint;
// 1. 填充网络接口描述符
pxInterface = pxFillInterfaceDescriptor();
// 2. 添加网络端点
pxEndPoint = FreeRTOS_FirstEndPoint();
// 3. 配置 IP 地址(静态或 DHCP)
#if ( ipconfigUSE_DHCP == 1 )
// DHCP 自动配置
#else
// 静态 IP 配置
FreeRTOS_SetEndPointConfiguration(
pxEndPoint,
FreeRTOS_inet_addr("192.168.1.100"),
FreeRTOS_inet_addr("255.255.255.0"),
FreeRTOS_inet_addr("192.168.1.1"),
FreeRTOS_inet_addr("8.8.8.8")
);
#endif
// 4. 初始化网络栈
FreeRTOS_IPInit_Multi();
// 5. 等待网络就绪
while( FreeRTOS_IsNetworkUp() == pdFALSE ) {
vTaskDelay(pdMS_TO_TICKS(100));
}
// 6. 网络已就绪,可以开始通信
}
实用工具函数
IP 地址转换
c
// 字符串转 IP 地址
uint32_t FreeRTOS_inet_addr(const char *pcIPAddress);
// 示例:FreeRTOS_inet_addr("192.168.1.100")
// IP 地址转字符串
char *FreeRTOS_inet_ntoa(uint32_t ulIPAddress, char *pcBuffer);
// 示例:FreeRTOS_inet_ntoa(ulIP, cIPString);
// 字节序转换
uint16_t FreeRTOS_htons(uint16_t usHostShort); // Host to Network Short
uint32_t FreeRTOS_htonl(uint32_t ulHostLong); // Host to Network Long
uint16_t FreeRTOS_ntohs(uint16_t usNetworkShort); // Network to Host Short
uint32_t FreeRTOS_ntohl(uint32_t ulNetworkLong); // Network to Host Long
网络信息查询
c
// 获取本机 IP 地址
uint32_t FreeRTOS_GetIPAddress(void);
// 获取子网掩码
uint32_t FreeRTOS_GetNetmask(void);
// 获取网关地址
uint32_t FreeRTOS_GetGatewayAddress(void);
// 获取 DNS 服务器地址
uint32_t FreeRTOS_GetDNSServerAddress(uint32_t ulIndex);
// 检查网络是否就绪
BaseType_t FreeRTOS_IsNetworkUp(void);
配置建议
最小配置(仅 UDP)
c
// FreeRTOSIPConfig.h
#define ipconfigUSE_IPv4 1
#define ipconfigUSE_IPv6 0
#define ipconfigUSE_TCP 0 // 禁用 TCP
#define ipconfigUSE_UDP 1
#define ipconfigUSE_DHCP 1
#define ipconfigUSE_DNS 1
#define ipconfigUSE_ARP 1
#define ipconfigUSE_ICMP 0 // 可选
完整配置(TCP + UDP)
c
// FreeRTOSIPConfig.h
#define ipconfigUSE_IPv4 1
#define ipconfigUSE_IPv6 1 // 可选
#define ipconfigUSE_TCP 1
#define ipconfigUSE_UDP 1
#define ipconfigUSE_DHCP 1
#define ipconfigUSE_DNS 1
#define ipconfigUSE_ARP 1
#define ipconfigUSE_ICMP 1
内存配置
c
// 网络缓冲区配置
#define ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS 10 // 网络缓冲区数量
#define ipconfigNETWORK_MTU 1500 // MTU 大小(字节)
// TCP 配置
#define ipconfigTCP_TX_BUFFER_LENGTH 4096 // TCP 发送缓冲区
#define ipconfigTCP_RX_BUFFER_LENGTH 4096 // TCP 接收缓冲区
#define ipconfigTCP_WIN_SEG_COUNT 4 // TCP 窗口段数
// UDP 配置
#define ipconfigUDP_MAX_RX_PACKETS 6 // UDP 最大接收包数
常见问题和解决方案
1. Socket 创建失败
原因:
- 内存不足
- 网络栈未初始化
- 已达到最大 Socket 数量
解决:
c
// 检查网络是否就绪
if( FreeRTOS_IsNetworkUp() == pdFALSE ) {
// 等待网络就绪
return;
}
// 增加网络缓冲区
#define ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS 20
2. 连接超时
原因:
- 目标地址不可达
- 防火墙阻止
- ARP 解析失败
解决:
c
// 增加连接超时
TickType_t xConnectTimeout = pdMS_TO_TICKS(10000); // 10 秒
// 注意:FreeRTOS_connect() 使用阻塞模式,超时由任务阻塞时间控制
3. DNS 查询失败
原因:
- DNS 服务器不可达
- 域名不存在
- 网络未就绪
解决:
c
// 使用多个 DNS 服务器
#define ipconfigENDPOINT_DNS_ADDRESS_COUNT 3
// 检查 DNS 服务器配置
uint32_t ulDNS1 = FreeRTOS_GetDNSServerAddress(0);
uint32_t ulDNS2 = FreeRTOS_GetDNSServerAddress(1);
4. TCP 连接断开
原因:
- 对端关闭连接
- 网络中断
- 超时
解决:
c
// 检查连接状态
if( FreeRTOS_issocketconnected(xSocket) == pdFALSE ) {
// 连接已断开,需要重新连接
}
// 实现重连机制
BaseType_t xReconnect(Socket_t *pxSocket) {
FreeRTOS_closesocket(*pxSocket);
vTaskDelay(pdMS_TO_TICKS(1000));
// 重新创建和连接
// ...
}
总结
FreeRTOS-Plus-TCP 提供了完整的 TCP/IP 协议栈支持:
支持的协议:
- ✅ IPv4/IPv6
- ✅ TCP/UDP
- ✅ ARP/ND
- ✅ DHCP/DHCPv6
- ✅ DNS
- ✅ ICMP
编程接口:
- ✅ 标准 Berkeley Sockets API
- ✅ 易于移植现有代码
- ✅ 支持阻塞和非阻塞操作
适用场景:
- ✅ IoT 设备网络通信
- ✅ HTTP/HTTPS 客户端
- ✅ MQTT 客户端(配合 coreMQTT)
- ✅ 自定义网络协议
- ✅ 网络服务器应用
通过合理配置和使用这些协议,可以在 FreeRTOS 上构建完整的网络应用。
最后更新:基于 FreeRTOS-Plus-TCP V4.2.5