基於W5500的NetBIOS應用實例

已刊登在《無線電》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 PC中NetBIOS影子

1 WINS 下的NetBIOS 設置

Ping 主機名的第一個數據包就是NBNS (NetBIOS Name Server), 協議包,它是TCP/IP上的NetBIOS (NetBT) 協議族的一部分,它在基於NetBIOS 名稱訪問的網絡上提供主機名和地址映射方法。NBNS 是動態DNS 的一種,Microsoft NBNS 實現稱為WINSNetBIOS 的報文類型較多、結構複雜,不同的網絡環境及不同的用途中,會使用不同報文,可用端口進行區分,WINS 協議中,NetBIOS 名字報文、數據報報文及會話報文分別使用TCP 137 138 139 端口。

NetBIOS 數據報有很多不同格式,主要取決於服務和信息類型,以及用以傳送NetBIOS數據報的傳輸協議。NetBIOS 協議架構可見圖,其中包含三種基本服務:NAMESESSION DATAGRAM  ,其中NAME 所用協議就是NBNS 協議。

圖2 NetBIOS層次結構

:NetBIOS 協議架構

下面看一下WINS 協議使用的報文NETBIOS 的名字報文(NAME )的總體格式如表:

1 NetBIOS 名字報文格式

事物ID (2bytes ) 通用標誌(2bytes )
問題記錄個數(2bytes ) 回答記錄個數(2bytes )
權威記錄個數(2bytes ) 附加記錄個數(2bytes )
問題記錄(若干字節)
回答記錄(若干字節)
權威記錄(若干字節)
附加記錄(若干字節)

 

報文的前12 字節總稱為NETBIOS 名字報文的首部,通過首部我們可以判斷出是否為名字查詢的報文。

NETBIOS 名字報文中最常見的是攜帶問題記錄的報文,問題記錄的格式如表:

2 NetBIOS 名字報文中問題記錄格式

問題名稱(若干字節)

問題類型(2 bytes )

問題類別(2bytes )

 

通過攜帶問題記錄的報文,我們可以得到要查詢的名字字符,如果和本機名相符,就發送報文響應,響應中帶有IP 地址,發送廣播的主機就會得到該IP 地址。

W5500EVB 實現NETBIOS 名字報文解析

了解了NETBIOS 協議之後,下面就讓我們通過W5500EVB 做一個嵌入NetBIOS 的簡單實驗。

  1. 實驗目的:通過在DOS ping 該設備名“ WIZNRTW5500 ”,可以得到開發板的IP地址。
  2. 硬件環境
  1. 單片機:STM32F103RC ,256K 字節Flash ,48K 字節SRAM ,2K 字節EEPROM
  2. 以太網控制器:W5500 ,SPI 接口與單片機相連
  3. 電源:USB 供電
  4. 硬件外設:板載LED
  1. 開發工具: Keil
  2. 測試軟件:串口調試助手,網絡調試助手看代碼之前,我們還是先來了解一下整個的程序流程,如圖所示整個程序採用查詢方式通過DHCP 子程序成功獲取IP 後可執行NBNS 服務。同時W5500EVB 設置成HTTP Server ,可以接收,並處理TCP Client 發來的數據

     圖4NBNS流程圖

    :主程序流程圖

    本文主要討論如何在單片機上實現NETBIOS 名字解析服務,DHCP TCP Server相關部分子程序在此不再詳細介紹,根據NETBIOS 名字解析服務子程序流程圖(如圖示),我們可以得知當查詢到137 端口收到網絡的UDP 數據包時,讀取數據包並進行判斷是否為NETBIOS 名字報文,如果是就將解析出的名字與本機名比較,如果一致就回复報文。

    圖3 主程序流程圖

    :NBNS 程序流程圖

    在此貼出NETBIOS 部分代碼,要獲取完整代碼,請到http://pan.baidu.com/s/1nt9MQKh 上進行下載。

void do_netbios(void)

{

  unsigned char state;

  unsigned int len;

 state = getSn_SR(NETBIOS_SOCK);

  switch(state)

  {

  case SOCK_UDP:

    if((len=getSn_RX_RSR(NETBIOS_SOCK))>0)

    {

      unsigned char rem_ip_addr[4];

      uint16 rem_udp_port;

     char netbios_name[NETBIOS_NAME_LEN+1];

     NETBIOS_HDR* netbios_hdr;

     NETBIOS_NAME_HDR* netbios_name_hdr;

      len=recvfrom(NETBIOS_SOCK,(unsignedchar*)&netbios_rx_buf,len,rem_ip_addr,&rem_udp_port);

     printf(“rem_ip_addr=%d.%d.%d.%d:%d\r\n”,rem_ip_addr[0],rem_ip_addr[1],rem_ip_addr[2],rem_ip_addr[3],rem_udp_port);

      netbios_hdr = (NETBIOS_HDR*)netbios_rx_buf;

  netbios_name_hdr = (NETBIOS_NAME_HDR*)(netbios_hdr+1);

      /* if the packet is a NetBIOS name query question */

      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 question\r\n”);

        /* decode the NetBIOS name */

10        netbios_name_decoding( (char*)(netbios_name_hdr->encname), netbios_name, sizeof(netbios_name));

        printf(“name is %s\r\n”,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 response\r\n”);

        }

      }

 

    }

    break;

14    case SOCK_CLOSED:

    close(NETBIOS_SOCK);

  socket(NETBIOS_SOCK,Sn_MR_UDP,NETBIOS_PORT,0);

    break;

  default:

    break;

  }

}

主要代碼解釋:

12段程序功能為通過SPI接口讀取NBNS Socket寄存器狀態,如果檢測建立了UDP連接,並且收到數據則進行NBNS服務。3段定義了NetBIOS name緩存區,Netbios name長度為1645段定義了NetBIOS包頭和其name部分結構體變量。6段為讀取137端口的UDP數據)netbios_rx_buf接下來NBNS核心部分:

78兩段將接受緩存區數據對定義的包頭進行賦值,第9,10段,判斷數據NetBIOS包頭是否為名字查詢,如果是名字查詢則進行名字解析。11行進行NetBIOS名字進一步比較。比較一致後,第12段程序準備回复NetBIOS包頭和內容。13段,發送NetBIOS回复響應。14段為檢測到NBNS SocketSOCK_CLOSED,則打開137端口的UDP Socket

 實驗測試

試驗中,我們通過W5500EVB對NetBIOS的解析,並用瀏覽器直接訪問設備名稱,來實現對設備的遠程訪問,以達實驗目的。下面就來看一下實驗測試全過程。

  1. 首先,打開串口調試助手,運行DHCP相關程序。可看到圖5中所示,W5500EVB成功通過DHCP獲得可用IP地址。

     netbios

:W5500EVB 通過DHCP 獲得可用IP 地址

2.在DOS 下,ping W5500EVB 設備名:WIZNET5500 ,可看到如圖中,獲取設備IP地址為:192.168.1.100 圖5 Ping 圖

:通過Ping 命令獲取W5500 IP 地址

3.運行NetBIOS 解析程序,在串口調試助手中看到解析過程,如圖所示:

圖6 收到廣播包

:W5500 EVB 解析本地網絡中的NetBIOS 廣播包

4.最後,我們在瀏覽器中輸入要訪問的設備名稱:wiznet5500 ,可以看到順利訪問到設備中的內置網頁,瀏覽到設備的配置信息。NetBIOS 解析成功!如圖所示:

圖7 網頁中輸入域名進行訪問 :在IE 瀏覽器中輸入設備名稱,可以訪問內置網頁 

五總結

隨著物聯網事業的發展,越來越多的網絡設備走進千家萬戶。對於普通用戶來說,設定和查詢IP 地址顯得稍微麻煩,即插即用的設備將會受到人們的青睞。本次實驗,我們在STM32 上實現了NetBIOS name Server ,通過在瀏覽器中輸入設備的名字就可以登錄到設備中的網頁中,然後就可以進行查看和控制。這樣以來,只要知道該設備的名字,無需用客戶端軟件就能對設備進行控制。例子很簡單,但希望能給大家提供一個思路,將其應用到你的智能家居DIY 製作中,讓智能家居真正的“智能”起來!