在进行本节之前,首先解决大家的一个疑惑点:client和client_socket有什么区别或分别代表的含义?
socket标准定义为套接字,应用于主流的网络设计程序,具有使用简单,多平台移植方便的特点。在socket应用中,使用一个套接字来记录网络的一个连接,套接字是一个整数,就像操作文件一样,利用一个文件描述符,进行打开、读、写、关闭等操作。在网络中,可以对socket 套接字进行类似的操作,比如开启一个网络的连接、读取连接主机发送来的数据、向连接的主机发送数据、终止连接等操作。lwip设计目的主要应用于嵌入式平台,对于socket的支持并不完全,只是通过对netconn进行封装实现部分功能,使得lwip也具有多平台应用的特性,通过socket方式的了解能够极大简化通信过程的理解,快速实现应用开发。
demo应用中,使用的开发板为mb-039,在工程中使用lwip+freertos,实验展示如何制作一个客户端并发送数据,板载ethernet相关的硬件部分电路如下:
mb-039 完整原理图可以通过mm32官网下载。
各个信号引脚对应如下:
通过配置复用相关引脚为rmii相关的功能,初始化以太网功能,执行freertos的启动。具体过程可参考样例初始化程序中代码。
在进行client_socket实验前,我们先了解需要使用到的应用功能函数:
(1)socket ()(2)connect ()(3)write ()
(1) socket ()
socket()指向lwip_socket(),功能为申请一个套接字,lwip_socket()源码如下:
intlwip_socket(int domain, int type, int protocol){ struct netconn *conn; int i; lwip_unused_arg(domain); /* @todo: check this */ /* create a netconn */ switch (type) { case sock_raw: conn = netconn_new_with_proto_and_callback(domain_to_netconn_type(domain, netconn_raw), (u8_t)protocol, default_socket_eventcb); lwip_debugf(sockets_debug, (lwip_socket(%s, sock_raw, %d) = , domain == pf_inet ? pf_inet : unknown, protocol)); break; case sock_dgram: conn = netconn_new_with_callback(domain_to_netconn_type(domain, ((protocol == ipproto_udplite) ? netconn_udplite : netconn_udp)), default_socket_eventcb); lwip_debugf(sockets_debug, (lwip_socket(%s, sock_dgram, %d) = , domain == pf_inet ? pf_inet : unknown, protocol));#if lwip_netbuf_recvinfo if (conn) { /* netconn layer enables pktinfo by default, sockets default to off */ conn->flags = ~netconn_flag_pktinfo; }#endif /* lwip_netbuf_recvinfo */ break; case sock_stream: conn = netconn_new_with_callback(domain_to_netconn_type(domain, netconn_tcp), default_socket_eventcb); lwip_debugf(sockets_debug, (lwip_socket(%s, sock_stream, %d) = , domain == pf_inet ? pf_inet : unknown, protocol)); break; default: lwip_debugf(sockets_debug, (lwip_socket(%d, %d/unknown, %d) = -1n, domain, type, protocol)); set_errno(einval); return -1; } if (!conn) { lwip_debugf(sockets_debug, (-1 / enobufs (could not create netconn)n)); set_errno(enobufs); return -1; } i = alloc_socket(conn, 0); if (i == -1) { netconn_delete(conn); set_errno(enfile); return -1; } conn->socket = i; done_socket( sockets[i - lwip_socket_offset]); lwip_debugf(sockets_debug, (%dn, i)); set_errno(0); return i;}
从源码中我们可以看出,本质上是对netconn_new()进行封装。我们关注一下其参数,domain表示协议簇,对于ip/tcp来说该值始终为af_inet。重点需要关注一下type,我们查看api手册对于几种类型的解释如下:
1. sock_stream:提供可靠的(即能保证数据正确传送到对方)面向连接socket服务,多用于资料(如文件)传输,如tcp协议。
2. sock_dgram:是提供无保障的面向消息的socket服务,主要用于在网络上发广播信息,如udp协议,提供无连接不可靠的数据报交付服务。
3. sock_raw:表示原始套接字,它允许应用程序访问网络层的原始数据包,这个套接字用得比较少,暂时不用理会它。
protocol指定套接字使用的协议,对于ipv4,tcp协议提供sock_stream服务,只有udp协议提供sock_dgram服务。
(2) connect ()
connect()指向lwip_connect()(源码较长,就不进行粘贴了),函数的作用与前文介绍netconn_connect功能一致,通过源码可以知道其是通过对netconn_connect的封装实现。在tcp客户端连接中,调用这个函数将发生握手过程,并最终建立新的tcp连接。对于udp来说调用这个函数只是在udp控制块中记录远端ip地址与端口号。
(3) write ()
write()指向lwip_write,源码如下,其通过调用lwip_send实现,flags为0。
ssize_tlwip_write(int s, const void *data, size_t size){ return lwip_send(s, data, size, 0);}
了解了以上3个api,接下来开始创建client_socket工程:
static void client(void *thread_param){ int sock = -1; struct sockaddr_in client_addr; ip4_addr_t ipaddr; uint8_t send_buf[]= this is mm32f3270 tcp client_socket demo n; ip4_addr( ipaddr,dest_ip_addr0,dest_ip_addr1,dest_ip_addr2,dest_ip_addr3); while(1) { sock = socket(af_inet, sock_stream, 0); //(1) if (sock < 0) { vtaskdelay(10); continue; } client_addr.sin_family = af_inet; //(2) client_addr.sin_port = htons(dest_port); //(3) client_addr.sin_addr.s_addr = ipaddr.addr; //(4) memset( (client_addr.sin_zero), 0, sizeof(client_addr.sin_zero)); if (connect(sock, (struct sockaddr *) client_addr, sizeof(struct sockaddr)) == -1) //(5) { printf(connect failed!n); closesocket(sock); vtaskdelay(10); continue; } while (1) { if(write(sock,send_buf,sizeof(send_buf)) < 0) //(6) break; vtaskdelay(1000); } closesocket(sock); }}
(1)申请一个套接字:socket
(2)协议簇类型(af_inet用于tcp/ip协议)
(3)将端口赋值给client_addr的sin_port成员
(4)将地址赋值给client_addr的sin_addr.s_addr成员
(5)创建连接,将sock与地址端口进行绑定,建立连接
(6)发送数据
到这里已经完成了client socket工程的创建,还有一步比较重要的是配置client与server端的ip,将数据发送给服务器端。
在windows下,通过打开命令行窗口输入:ipconfig可以获取本机地址与服务器的地址。
可以观察到pc地址为:192.168.105.34,在sys_arch.h文件中对dest_ip_addr0 、dest_ip_addr1、dest_ip_addr2、dest_ip_addr3进行修改,dest_port可选用空闲端口,设备ip需要设置在同一个网段内通信才能进行ip_addr0、ip_addr1 、ip_addr2,需要与pc地址保持一致,ip_addr3可以随意设置(和pc地址不一致即可)。
#define dest_ip_addr0 192#define dest_ip_addr1 168#define dest_ip_addr2 105#define dest_ip_addr3 34#define dest_port 5001#define ip_addr0 192#define ip_addr1 168#define ip_addr2 105#define ip_addr3 130
将程序下载入开发板中,使用sscom工具进行如下设置:
点击侦听:
可以观察到正常侦听并接收到数据,表明实验成功。demo程序可登录mindmotion的官网下载mm32f3270 lib_samples。
来源:灵动mm32mcu
浅谈PCB设计产生EMI的问题
一个LED超亮工程灯如何实现
被曝裁员亏损两个月后,暴风魔镜的新玩具能扭转VR寒冬吗?
LM339N与LM339有什么区别
博世工业4.0业绩销售额突破40亿欧元
基于MM32F3270以太网Client_Socket使用
商汤数字猫APP文创平台为影视行业打开通往元宇宙世界的大门
揭开华为云CodeArts TestPlan启发式测试设计神秘面纱!
科聪:轻松应对多样和差异的需求,构建移动机器人如此简单
中软国际电子哨兵终端通过OpenHarmony兼容性测评
2022年H1新型储能新增装机量达到12.7GW
文字处理软件有哪些
GGII:2022年中国正极补锂剂出货2500吨
在线扬尘监测系统助力建筑工地环境治理
MIPS与Tensilica推动Android上的SoC设计
BK3633,蓝牙ble5.2低功耗soc芯片,支持2.4g专有协议,mesh组网-layou指南
ARP协议的工作流程
物联网产业最新消息汇总 2029年左右开始将引入6G 英特尔推工业互联网赋能计划
广东电网统筹资源,引领海上风电产业发展
台积电除息分红 每股分配10元新台币红利