文章目录
- 仓库地址
- 关键接口适配
- FreeRTOS_read
- FreeRTOS_write
- NetworkInit && NetworkConnect && NetworkDisconnect
- 总结
仓库地址
https://github.com/eclipse/paho.mqtt.embedded-c
这里官方给了一些平台适配案例,这里参考FreeRTOS的
关键接口适配
使用使用了lwip的一些接口
FreeRTOS_read
int FreeRTOS_read(Network* n, unsigned char* buffer, int len, int timeout_ms)
{
// 将超时时间转换为 timeval 结构体表示的时间间隔
struct timeval interval = {timeout_ms / 1000, (timeout_ms % 1000) * 1000};
// 如果超时时间小于等于 0,将超时时间设置为 0.1 秒(100 微秒)
if (interval.tv_sec < 0 || (interval.tv_sec == 0 && interval.tv_usec <= 0)) {
interval.tv_sec = 0;
interval.tv_usec = 100;
}
fd_set read_fds;
// 初始化文件描述符集合,清空 read_fds
FD_ZERO(&read_fds);
// 将网络结构体中的套接字加入到文件描述符集合中,表示关注该套接字的可读事件
FD_SET(n->my_socket, &read_fds);
int bytes = 0;
while(bytes < len) {
// 使用 lwip_select 等待套接字可读事件,设置超时时间为 interval
int result = lwip_select(n->my_socket + 1, &read_fds, NULL, NULL, &interval);
// 如果返回值小于等于 0,表示没有可读事件发生或者发生错误,跳出循环
if (result <= 0)
break;
// 套接字可读
int rc = lwip_recv(n->my_socket, &buffer[bytes], (len - bytes), 0);
// 如果接收返回值为 -1,表示接收出错
if (rc == -1) {
// 如果错误不是 EAGAIN(资源暂时不可用)或 EWOULDBLOCK(操作会阻塞)
if (errno!= EAGAIN && errno!= EWOULDBLOCK)
bytes = -1;
break;
}
// 如果接收返回值为 0,表示连接已关闭
else if (rc == 0) {
bytes = 0;
break;
}
// 如果接收成功,更新已接收的字节数
else
bytes += rc;
}
return bytes;
}
FreeRTOS_write
int FreeRTOS_write(Network* n, unsigned char* buffer, int len, int timeout_ms)
{
TickType_t xTicksToWait = timeout_ms / portTICK_PERIOD_MS; /* convert milliseconds to ticks */
TimeOut_t xTimeOut;
int sentLen = 0;
int flags = 0;
vTaskSetTimeOutState(&xTimeOut); /* Record the time at which this function was entered. */
do {
int rc = 0;
// 设置 lwip_send 为非阻塞方式
flags = MSG_DONTWAIT;
rc = lwip_send(n->my_socket, buffer + sentLen, len - sentLen, flags);
if (rc > 0)
sentLen += rc;
else if (rc == 0 || (rc < 0 && errno == EAGAIN)) {
// 缓冲区已满,等待一段时间再尝试发送
vTaskDelay(pdMS_TO_TICKS(10)); // 延时10ms再尝试发送
} else if (rc < 0) {
sentLen = rc;
break;
}
} while (sentLen < len && xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE);
return sentLen;
}
NetworkInit && NetworkConnect && NetworkDisconnect
// 初始化网络结构体
void NetworkInit(Network* n)
{
// 将套接字初始化为无效值
n->my_socket = -1;
// 设置网络结构体的读取函数指针为 FreeRTOS_read
n->mqttread = FreeRTOS_read;
// 设置网络结构体的写入函数指针为 FreeRTOS_write
n->mqttwrite = FreeRTOS_write;
}
// 连接到指定地址和端口
int NetworkConnect(Network* n, char* addr, int port)
{
// 打开连接并获取套接字描述符
n->my_socket = transport_open((int8_t*)addr, port);
// 如果套接字描述符有效(不等于 -1),表示连接成功
if (-1!= n->my_socket) {
return 0;
}
// 连接失败,返回 -1
return -1;
}
// 断开网络连接
void NetworkDisconnect(Network* n)
{
// 如果套接字描述符有效(大于 0)
if (n->my_socket > 0 ) {
// 关闭套接字
closesocket(n->my_socket);
// 将套接字描述符重置为无效值
n->my_socket = -1;
}
}
int32_t transport_open(int8_t* servip, int32_t port)
{
int32_t *sock = &mysock;
int32_t ret;
int32_t opt;
struct sockaddr_in addr;
// 初始化服务器信息
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
// 填写服务器端口号
addr.sin_port = PP_HTONS(port);
// 填写服务器 IP 地址
addr.sin_addr.s_addr = inet_addr((const char*)servip);
// 创建套接字
*sock = socket(AF_INET, SOCK_STREAM, 0);
// 连接服务器
ret = connect(*sock, (struct sockaddr*)&addr, sizeof(addr));
if(ret!= 0)
{
// 连接失败,关闭链接
close(*sock);
// 返回 -1 表示连接失败
return -1;
}
// 连接成功,设置 TCP_NODELAY 选项(禁用 Nagle 算法)
opt = 1;
setsockopt(*sock, IPPROTO_TCP, TCP_NODELAY, (void *)&opt, sizeof(int));
// 返回套接字描述符
return *sock;
}
总结
实际使用过程中,遇到了lwip阻塞的阻塞情况,注意采用非阻塞方式进行读取。