完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
简介
随着物联网的发展,越来越多产品需要基于网络进行数据传输。在实际开发中,往往要求网络传输时不能阻塞当前线程,以致无法及时处理其他消息。在用户无法直接套用简单的 socket demo 时,RT-Thread 提供基于多线程的非阻塞 socket 编程示例,方便用户进行应用程序开发。 在 RT-Thread 使用 socket 网络编程时,当一个任务调用 socket的 recv()函数接收数据时,如果 socket 上并没有接收到数据,这个任务将阻塞在这个 recv() 函数里。这个时候,这个任务想要处理一些其他事情,例如进行一些数据采集,发送一些额外数据到网络上等,将变得不可能了。与此同时,其他线程也需要将数据上传同一个服务器,如果直接多个线程共同使用一个 socket 操作,这将会破坏底层 lwip 的消息事件模型。 本文准备资料如下:
socket 编程模型如下图所示: 客户端使用流程:
非阻塞 socket 编程简介 在 RT-Thread 中,自 v3.0.0 以来更标准化,支持更多的 POSIX API。这其中就包括 poll / select 接口实现,并且可以进行 socket 和设备文件的联合 poll / select。select、poll的内部实现机制相似,由于本文选用 select 方式,故在此不对 poll 展开介绍。 下面结合框图进一步说明如何使用 select 和 pipe 来解决这类问题。 图中存在有三个线程:应用线程 thread1、thread2 和客户端线程 thread client,其中 thread client 完成 select 功能。
select select() 可以阻塞地同时探测一组支持非阻塞的 I / O 设备是否有事件发生(如可读,可写,出现异常等等),直至某一个设备触发了事件或者超过了指定的等待时间。此时我们可以把需要的数据源通道放到 select 的探测范围内,只要相应的数据源准备好 select 就会返回,这时就能无阻塞地读取到数据。 select() 主要用来处理 I / O 多路复用的情况,适用如下场合:
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);[tr]参数描述[/tr]
pipe 是一个基于文件描述符的单向数据通道,可用于线程间的通信。 在 RT-Thread 里面,pipe 支持文件描述符的形式操作,而且 pipe 不需要控制协议,操作简单。 提示 在 msh />中,输入 list_fd 可查看当前打开的文件描述符,详情如下: msh />list_fdfd type ref magic path-- ------ --- ----- ------ 0 file 1 fdfd /uart0 1 socket 1 fdfd 2 file 1 fdfd /pipe0 3 file 1 fdfd /pipe0msh />下面将详细介绍代码的实现情况。 tcpclient 示例 tcpclient.c 是上文提出的 select、pipe 方案的具体实现代码,该源码采用面向对象的思想实现,提供 TCP 连接、发送、关闭以及注册接收回调四个 API 提供用户使用。 下面的序列图为 tcpclient.c的运行流程: 各流程详细解释如下所示:
下面代码的核心代码: static void select_handle(rt_tcpclient_t *thiz, char *pipe_buff, char *sock_buff){ fd_set fds; rt_int32_t max_fd = 0, res = 0; max_fd = MAX_VAL(thiz->sock_fd, thiz->pipe_read_fd) + 1; /* 清空可读事件描述符列表 */ FD_ZERO(&fds); while (1) { /* 将需要监听可读事件的描述符加入列表 */ FD_SET(thiz->sock_fd, &fds); FD_SET(thiz->pipe_read_fd, &fds); /* 等待设定的网络描述符有事件发生 */ res = select(max_fd, &fds, RT_NULL, RT_NULL, RT_NULL); /* select 返回错误及超时处理 */ EXCEPTION_HANDLE(res, "select handle", "error", "timeout"); /* 查看 sock 描述符上有没有发生可读事件 */ if (FD_ISSET(thiz->sock_fd, &fds)) { /* 从 sock 连接中接收最大BUFSZ - 1字节数据 */ res = recv(thiz->sock_fd, sock_buff, BUFF_SIZE, 0); /* recv 返回异常 */ EXCEPTION_HANDLE(res, "socket recv handle", "error", "TCP disconnected"); /* 有接收到数据,把末端清零 */ sock_buff[res] = '/0'; /* 通过回调函数的方式,数据发给 thread1 */ RX_CB_HANDLE(sock_buff, res); /* 如果接收的是exit,关闭这个连接 */ EXIT_HANDLE(sock_buff); } /* 查看 pipe 描述符上有没有发生可读事件 */ if (FD_ISSET(thiz->pipe_read_fd, &fds)) { /* 从 pipe 连接中接收最大BUFSZ - 1字节数据 */ res = read(thiz->pipe_read_fd, pipe_buff, BUFF_SIZE); /* recv 返回异常 */ EXCEPTION_HANDLE(res, "pipe recv handle", "error", RT_NULL); /* 有接收到数据,把末端清零 */ pipe_buff[res] = '/0'; /* 读取 pipe 的数据,转发给 server */ send(thiz->sock_fd, pipe_buff, res, 0); /* recv 返回异常 */ EXCEPTION_HANDLE(res, "socket write handle", "error", "warning"); /* 如果接收的是 exit,关闭这个连接 */ EXIT_HANDLE(pipe_buff); } }exit: /* 释放接收缓冲 */ free(pipe_buff); free(sock_buff);}这段代码是 tcpclient 线程的核心部分,按照例程配置 select,根据 FD_ISSET() 宏检查描述符。
首先在 github 上拉取 tcpclient.c 的源码,然后将tcpclient 文件夹放在 rt-thread/bsp/qemu-vexpress-a9目录下,详情如下: 在 Env 里使用 scons 命令编译 QEMU 工程,详情如下: 在 Env 里使用 ./qemu.bat 命令启动,详情如下: QEMU 成功启动,下面来介绍代码运行情况。 设置网络调试助手端口号,详情如下: 在 cmd 命令行输入 ipconfig 查看本机 ip 地址,详情如下: > ipconfig...IPv4 Address. . . . . . . . . . . : 192.168.12.53...example 代码中通过 rt_tcpclient_start() API 设置服务器 IP 地址和端口号,详情如下: rt_tcpclient_start("192.168.12.53", 9008);注意事项 这里需要根据自己的环境设置 ip 地址和端口号!!! 在 msh /> 里,输入 rt_tc_test_init 详情如下: msh />rt_tc_test_init运行效果 在 example.c 里建立两个线程,一个是 thread1,另一个是 thread2,两个线程交替给服务端发送数据。服务端每秒钟往客户端发送数据。 网络助手发送 i am server ,thread1 接收并且打印出来,详情如下: msh />D/tc_rx_cb [-30-01-01 00:00:00 tcpc] (packages/tcpclient/examples/tcpclient_example.c:52)recv data: i am server总结
|
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
AI模型部署边缘设备的奇妙之旅:边缘端设备的局域网视频流传输方案
1290 浏览 0 评论
1431 浏览 0 评论
AI模型部署边缘设备的奇妙之旅:如何在边缘端部署OpenCV
6264 浏览 0 评论
tms320280021 adc采样波形,为什么adc采样频率上来波形就不好了?
1835 浏览 0 评论
2953 浏览 0 评论
77025 浏览 21 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-1-12 05:02 , Processed in 0.587555 second(s), Total 71, Slave 55 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号