完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
已刊登在《无线电》2月刊 一 实例背景 最近一个做智能家居的朋友面临这样的一个烦恼,他想让用户通过智能手机在家里方便地控制家居设备,又想让用户免除下载安装App的麻烦,通过浏览器直接打开设备内嵌的网页便可实现控制。但是设备的IP地址都是通过家里的路由器自动获得的,设备上又没有屏幕来显示其IP地址。问我有没有办法不输入IP地址来实现浏览器访问该设备网页的办法,就是类似DNS之类,但是无需连外网,只在家庭网络内能访问即可。这使我想起一个古老的协议,NetBIOS(Network Basic Input/Output System)。这个在上世纪80年代由IBM开发的协议,主要用于数十台左右计算机组成的小型局域网,该协议的主要用途之一就是把计算机名称解析为相应IP地址。如果每个设备有一个固定名字,在实现了NetBIOS的前提下,用户在浏览器里输入该设备的名字,然后通过NetBIOS解析,便可实现访问该设备网页的这个功能了。而且NetBIOS占用系统资源少,在单片机上运行不成问题。于是推荐这个朋友在他的设备上实现了NetBIOS协议,解决了他的烦恼。 除了智能家居,在当下物联网时代,想必还有其他应用也会遇到类似问题,就拿手头的WIZnet-W5500评估板实现了一下NetBIOS,希望能对做网络设备开发的朋友有所帮助。在用W5500实现之前,我们还是先在PC上看一下NetBIOS到底是一个什么东西。 二 NetBIOS协议 我们知道在DOS 命令下可以通过PING主机名获得另外一台电脑的IP地址,实际上就是通过 NETBIOS进行的。在Windows操作系统中,默认情况下在安装TCP/IP协议后会自动安装NetBIOS。查看方法如下:本地连接属性的中“高级TCP/IP设置”窗口中选择“WINS”选项卡,在“NetBIOS设置”区域中就可以设置相应的NetBIOS,如图1: 图1 WINS下的NetBIOS设置 Ping主机名的第一个数据包就是NBNS(NetBIOS Name Server),协议包,它是 TCP/IP 上的 NetBIOS (NetBT) 协议族的一部分,它在基于 NetBIOS 名称访问的网络上提供主机名和地址映射方法。NBNS是动态DNS的一种,Microsoft的NBNS实现称为WINS。NetBIOS的报文类型较多、结构复杂,不同的网络环境及不同的用途中,会使用不同报文,可用端口进行区分,WINS协议中,NetBIOS名字报文、数据报报文及会话报文分别使用TCP 137、138和139端口。NetBIOS 数据报有很多不同格式,主要取决于服务和信息类型,以及用以传送 NetBIOS 数据报的传输协议。 NetBIOS 协议架构可见图2,其中包含三种基本服务: NAME、SESSION 和 DATAGRAM ,其中NAME所用协议就是NBNS协议。 图2:NetBIOS协议架构 下面看一下WINS协议使用的报文NETBIOS的名字报文(NAME)的总体格式如表1:表1 NetBIOS名字报文格式
报文的前12字节总称为NETBIOS名字报文的首部,通过首部我们可以判断出是否为名字查询的报文。 NETBIOS名字报文中最常见的是携带问题记录的报文,问题记录的格式如表2: 表2 NetBIOS名字报文中问题记录格式
通过携带问题记录的报文,我们可以得到要查询的名字字符,如果和本机名相符,就发送报文响应,响应中带有IP地址,发送广播的主机就会得到该IP地址。 三 W5500EVB实现NETBIOS名字报文解析 了解了NETBIOS协议之后,下面就让我们通过W5500EVB做一个嵌入NetBIOS的简单实验。
void do_netbios(void) { unsigned char state; unsigned int len; 1 state = getSn_SR(NETBIOS_SOCK); switch(state) { case SOCK_UDP: 2 if((len=getSn_RX_RSR(NETBIOS_SOCK))>0) { unsigned char rem_ip_addr[4]; uint16 rem_udp_port; 3 char netbios_name[NETBIOS_NAME_LEN+1]; 4 NETBIOS_HDR* netbios_hdr; 5 NETBIOS_NAME_HDR* netbios_name_hdr; 6 len=recvfrom(NETBIOS_SOCK,(unsignedchar*)&netbios_rx_buf,len,rem_ip_addr,&rem_udp_port); printf(“rem_ip_addr=%d.%d.%d.%d:%drn”,rem_ip_addr[0],rem_ip_addr[1],rem_ip_addr[2],rem_ip_addr[3],rem_udp_port); 7 netbios_hdr = (NETBIOS_HDR*)netbios_rx_buf; 8 netbios_name_hdr = (NETBIOS_NAME_HDR*)(netbios_hdr+1); /* if the packet is a NetBIOS name query question */ 9 if(((netbios_hdr->flags& ntohs(NETB_HFLAG_OPCODE)) == ntohs(NETB_HFLAG_OPCODE_NAME_QUERY)) && ((netbios_hdr->flags & ntohs(NETB_HFLAG_RESPONSE)) == 0) && (netbios_hdr->questions == ntohs(1))) { printf(“netbios name query questionrn”); /* decode the NetBIOS name */ 10 netbios_name_decoding( (char*)(netbios_name_hdr->encname), netbios_name, sizeof(netbios_name)); printf(“name is %srn”,netbios_name); /* if the packet is for us */ 11 if (strcmp(netbios_name, NETBIOS_W5500_NAME) == 0) { uint8 ip_addr[4]; NETBIOS_RESP *resp = (NETBIOS_RESP*)netbios_tx_buf; /* prepare NetBIOS header response */ 12 resp->resp_hdr.trans_id = netbios_hdr->trans_id; resp->resp_hdr.flags = htons(NETB_HFLAG_RESPONSE | NETB_HFLAG_OPCODE_NAME_QUERY | NETB_HFLAG_AUTHORATIVE | NETB_HFLAG_RECURS_DESIRED); resp->resp_hdr.questions = 0; resp->resp_hdr.answerRRs = htons(1); resp->resp_hdr.authorityRRs = 0; resp->resp_hdr.additionalRRs = 0; /* prepare NetBIOS header datas */ memcpy( resp->resp_name.encname, netbios_name_hdr->encname, sizeof(netbios_name_hdr->encname)); resp->resp_name.nametype = netbios_name_hdr->nametype; resp->resp_name.type = netbios_name_hdr->type; resp->resp_name.cls = netbios_name_hdr->cls; resp->resp_name.ttl = htonl(NETBIOS_NAME_TTL); resp->resp_name.datalen = htons(sizeof(resp->resp_name.flags)+sizeof(resp->resp_name.addr)); resp->resp_name.flags = htons(NETB_NFLAG_NODETYPE_BNODE); getSIPR(ip_addr); memcpy(resp->resp_name.addr,ip_addr,4); /* send the NetBIOS response */ 13 sendto(NETBIOS_SOCK, (unsigned char*)resp, sizeof(NETBIOS_RESP), rem_ip_addr, rem_udp_port); printf(“send responsern”); } } } break; 14 case SOCK_CLOSED: close(NETBIOS_SOCK); socket(NETBIOS_SOCK,Sn_MR_UDP,NETBIOS_PORT,0); break; default: break; } } 主要代码解释: 第1、2段程序功能为通过SPI接口读取NBNS Socket寄存器状态,如果检测建立了UDP连接,并且收到数据则进行NBNS服务。第3段定义了NetBIOS name缓存区,Netbios name长度为16。第4、5段定义了NetBIOS 包头和其name部分结构体变量。第6段为读取137端口的UDP数据)netbios_rx_buf。接下来NBNS核心部分: 第7、8两段将接受缓存区数据对定义的包头进行赋值,第9,10段,判断数据NetBIOS 包头是否为名字查询,如果是名字查询则进行名字解析。第11行进行NetBIOS名字进一步比较。比较一致后,第12段程序准备回复NetBIOS包头和内容。第13段,发送NetBIOS回复响应。第14段为检测到NBNS Socket 为SOCK_CLOSED,则打开137端口的UDP Socket。 四 实验测试 试验中,我们通过W5500EVB对NetBIOS的解析,并用浏览器直接访问设备名称,来实现对设备的远程访问,以达实验目的。下面就来看一下实验测试全过程。
图5:W5500EVB通过DHCP获得可用IP地址 2. 在DOS下,ping W5500EVB设备名:WIZNET5500,可看到如图6中,获取设备IP地址为:192.168.1.100 。 图6:通过Ping命令获取W5500的IP地址 3. 运行NetBIOS解析程序,在串口调试助手中看到解析过程,如图7所示: 图7:W5500 EVB解析本地网络中的NetBIOS广播包 4. 最后,我们在浏览器中输入要访问的设备名称:wiznet5500,可以看到顺利访问到设备中的内置网页,浏览到设备的配置信息。NetBIOS解析成功!如图8所示: 图8:在IE浏览器中输入设备名称,可以访问内置网页 五 总结随着物联网事业的发展,越来越多的网络设备走进千家万户。对于普通用户来说,设定和查询IP地址显得稍微麻烦,即插即用的设备将会受到人们的青睐。本次实验,我们在STM32上实现了NetBIOS name Server,通过在浏览器中输入设备的名字就可以登录到设备中的网页中,然后就可以进行查看和控制。这样以来,只要知道该设备的名字,无需用客户端软件就能对设备进行控制。例子很简单,但希望能给大家提供一个思路,将其应用到你的智能家居DIY制作中,让智能家居真正的“智能”起来! |
|
相关推荐 |
|
只有小组成员才能发言,加入小组>>
492个成员聚集在这个小组
加入小组5129 浏览 1 评论
12260 浏览 5 评论
3546 浏览 2 评论
2996 浏览 0 评论
12826 浏览 2 评论
w5500 作为tcp server,客户端异常发送【RST,ACK】断开连接问题
2798浏览 1评论
1199浏览 0评论
使用stm32,通过spi的dma 收发,运行多次读写以后w5500发送正常,接收卡死,高人帮忙看看
6473浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-1-1 16:59 , Processed in 0.658848 second(s), Total 46, Slave 35 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (威廉希尔官方网站 图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号