FreeRTOS-Plus-TCP 协议支持与网络编程指南

FreeRTOS-Plus-TCP 协议支持与网络编程指南

目录

  1. 支持的协议
  2. [Berkeley Sockets API](#Berkeley Sockets API)
  3. [TCP 编程](#TCP 编程)
  4. [UDP 编程](#UDP 编程)
  5. 网络配置
  6. 协议详解
  7. 完整编程示例

支持的协议

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() 字节序转换

主要差异:

  1. 所有函数都带有 FreeRTOS_ 前缀
  2. Socket 类型为 Socket_t 而非 int
  3. 地址结构为 struct freertos_sockaddr 而非 struct sockaddr
  4. 错误码使用 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 地址

工作原理:

  1. 需要发送数据包到某个 IP 地址
  2. 查找 ARP 缓存表
  3. 如果未找到,发送 ARP 请求广播
  4. 目标主机回复 ARP 响应
  5. 更新 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

相关推荐
鱼跃鹰飞2 小时前
面试题:知道WebSocket协议吗?
网络·websocket·网络协议
啊阿狸不会拉杆3 小时前
《计算机操作系统》第十二章 - 保护和安全
开发语言·网络·c++·算法·安全·计算机组成原理·计算机操作系统
咕噜咕噜万3 小时前
测试用例执行进度实时同步工具指南:从流程打通到效率提效的全链路落地
运维·网络
郝学胜-神的一滴3 小时前
Linux网络字节序详解:从理论到实践
linux·服务器·c语言·开发语言·c++·网络协议·程序人生
石像鬼₧魂石3 小时前
netsh wlan 常用命令速查表
服务器·网络·php
科技圈快讯3 小时前
外链分享防泄露场景:企业网盘实测分享控制功能
网络
yongui478343 小时前
异构网络垂直切换算法MATLAB仿真实现
网络·算法·matlab
半路_出家ren3 小时前
5.RSA和AES加密(python)
服务器·网络·python·https·aes·rsa·加密算法
michael_ouyang3 小时前
IM 消息收发流程方案选型
前端·websocket·网络协议·typescript·electron