WIZnet2012

W3150A+评估板--EVB-PIC24 用户手册(三)

0
阅读(2627)

昨天给大家讲了关于W3150A+评估板--EVB-PIC24 关于程序员指南中,关于内存映射和EVB B/D固件的编译及管理程序的一些内容。

今天给大家介绍 程序员指南中,关于EVB B/D固件中应用的问题,包含回路程序, 网络服务器,和DHCP 客户端. 通过管理程序选择应用.

3.2.6.             应用

它是一个用iinChip™的网络应用,它包含回路程序, 网络服务器,和DHCP 客户端. 通过管理程序选择应用.

 3.2.6.1.     回路 TCP 服务器

回路TCP服务器程序中, EVB B/D 工作在服务器模式, AX1, PC 测试程序工作在客户端模式. AX1 连接到EVB B/D, 如果连接成功, AX1 通过 TCP信道发送数据流. EVB B/D通过TCP信道不经过处理发挥数据流.

回路TCP 服务器程序使用loopback_tcps() , <图 3.14> 展示了loopback_tcps()的处理流程.

 <图 3.14 : loopback_tcps() >

<表 3‑23: loopback_tcps()中的参考函数>

如果服务器socket在SOCK_CLOSED 状态,loopback_tcps()用Sn_MR_TCP, 监听端口号和 Option Flag等元素调用socket() 来创建TCP服务器socket.

 socket() 函数改变socket状态为SOCK_INIT而不顾原来的socket状态.如果服务器socket创建成功, 在以参数用服务器socket调用listen()之后,它处于TCP 服务器模式.listen()使得服务器socket 状态为监听状态并保持坚挺状态直到任何客户端的连接. 

此时, 当任一客户端尝试连接到服务器socket时,服务器 socket 状态从 “listen” 改为 “established”. 此时客户端和服务器之间的连接建立,在SOCK_ESTABLISHED状态可以进行数据传送.

数据的传送在SOCK_ESTABLISHED中使用 recv() 和send()实现.这里的数据传送是EVB B/D(服务器) 和AX1(客户端)之间1对1的传送. 

 在SOCK_ESTABLISHED状态,如果客户端请求关闭连接, 服务器 socket状态将从 SOCK_ESTABLISHED 改变为SOCK_CLOSE_WAIT. 在SOCK_CLOSE_WAIT 状态,不能进行数据通信并且必须关闭服务器 socket. 在 SOCK_CLOSE_WAIT状态, 调用disconnect() 关闭socket. disconnect() 改变socket 状态为SOCK_CLOSED 而不顾以前的socket状态. 

3.2.6.2.     回路 TCP 客户端

在 回路TCP 客户端程序, EVB B/D 工作在客户端模式 , AX1, PC 测试程序工作在服务器模式. EVB B/D 尝试连接到以服务器状态等待的 AX1, 如果连接成功,EVB B/D 通过TCP 信道接收数据流, 然后 EVB B/D将接收到的数据流不经过处理发送给AX1.

 回路 TCP 客户端程序是用 loopback_tcpc() 创建的, <图3.15>是loopback_tcpc()处理的进程.

如果客户端socket在SOCK_CLOSED 状态, loopback_tcpc()用Sn_MR_TCP, Any Port Number和Option Flag等元素调用socket()以创建TCP客户端socket.

 这里调用socket, get_system_any_port()使用任意端口号. 这是因为如果用同样的端口号尝试连接到同样的服务器连接有可能失败. 成功创建socket后, 用服务器socket的元素调用 connect()以连接到AX1服务器.

  connect()使得socket状态变成SOCK_SYNSENT,并使状态保持在SOCK_SYNSENT直到收到来自服务器的链接授权. 如果连接成功,socket状态从SOCK_SYNSENT改变为SOCK_ESTABLISHED. 在 SOCK_ESTABLISHED 状态,操作和loopback_tcps()一样. 

<图 3.15: loopback_tcpc()>

<表 3‑24: loopback_tcpc()中的参考函数>

3.2.6.3.     回路 UDP

回路UDP 程序用UDP协议的单播数据报通信.它的操作和回路TCP 服务器/客户端 程序一样. UDP 通信包括单播数据报通信和广播数据报通信, 基本上支持用一个信道实现多目的地的1到多通信. 

 回路 UDP 程序使用loopback_udp() , <图 3-16> 是 loopback_udp()处理进程.

<图 3.16: loopback_udp()>

 

<表 3‑25: loopback_udp()的参考函数>

如果udp socket 在 SOCK_CLOSED 状态, socket() 用Sn_MR_UDP, Port Number和Option Flag等元素调用以创建UDP socket.

UDP通信和TCP相反, 是不需要连接进程请求的数据报通信. 因此在socket建立后可立刻进行直接数据通信. 在创建 UDP socket后, udp socket 的状态将从SOCK_CLOSED改为SOCK_UDP.

这里, 不像TCP 数据通信使用send() 和recv(), 而是使用sendto()和ecvfrom().  

 这是因为TCP是已知目的地的一对一通信方法,但是UDP是不需要连接进程的一对多通信. sendto()发送数据到特定目的地的特定端口, recvfrom() 用于接收来自临时端口的入数据. 来自recvfrom()的目的信息将被用目的地址和目的端口告知用户,目的地址和目的端口以元素的形式发送.

在loopback_udp()中, 没有使用close()的例子, 但是万一不再需要UDP 通信, 同样可以调用close() 并关闭udp socket.  

 3.2.6.4.     网络服务器

网络服务器程序是一个TCP服务器程序,它使用基于TCP协议的HTTP协议. 在创建网络服务器程序之前, 需要理解在网络服务器和网络客户端(网络浏览器)之间发送的HTTP协议消息结构.HTTP, 是超文本传输协议的缩写,用于基于因特网的网络服务器和客户端浏览器之间的传输协议.

<表 3‑26: 网络浏览器的 HTTP 请求运行进程 >

网络服务器程序分析从网络浏览器接收到的 HTTP请求消息的方法和URI(统一资源标识符), 如果相关的URI只是简单的请求网页, 就发送网页. 如果请求是诸如CGI(公共网关接口)的动作,就接收该动作并在网页中显示结果.

 <图 3.17> 是网络服务器和网络客户端之间的HTTP 消息流,<表 3-28> 是HTTP消息结构.

<图 3.17: HTTP 消息流>

<表 3‑27: HTTP消息格式>

 

想了解更多有关HTTP消息的信息,请参考 RFC2616. HTTP 请求消息随着网络浏览器的不同而不同. 

 

<表 3-29> 是Windows 2000上的Internet Explores和EVB B/D的HTTP消息通信.

<表 3‑28: EVB B/D和网络浏览器之间的HTTP消息>

 

网络浏览器程序由管理HTTP服务器socket的web_server()和管理HTTP消息的 proc_http() 组成。

<图 3.18> 是处理进程.

 

<图 3.18: web_server()>

因为web_server() 是TCP服务器程序,它的创建和 Chapter 3.2.6.1所解释的loopback_tcps()相似. Difference between web_server()和loopback_tcps() 不同之处在于数据通信代码. web_server() 调用 proc_http(),proc_http()处理来自在http socket的SOCK_ESTABLISHED上的网络浏览器的HTTP请求消息.  

 在调用proc_http()函数后, web_server()处于等待状态,知道收到来自网络浏览器的HTTP请求消息的HTTP 回复消息, 然后调用 disconnect() 关闭 http socket.

这种 socket 关闭叫做Active Close, 在这种情况下, EVB B/D 先请求关闭客户端. 如果你喜欢,也可使用 Passive Close,它是客户端先提出断开连接请求. 网络浏览器支持Active Close的原因是因为 EVB B/D 支持其他客户端的链接.

<图 3.19: proc_http()>

proc_http() 调用 parse_http_request() 以分析接收到的网络浏览器的 HTTP 请求消息.被分析的HTTP请求消息的METHOD是“GET”, “HEAD”或 “POST”, 将调用get_http_uri_name()并且URI Name将从HTTP请求消息中提取出来.如果提取的URI Name是“/” ,就用“index.html”代替URI Name “/” ,“index.html”是EVB B/D 的网络浏览器默认页面,因为这意味着网络浏览器在请求网络服务器的默认页面.

通过调用find_http_uri_type()获得HTTP请求消息的HTTP Request Type之后,如果HTTP Request Type是 “CGI” ,就执行相关的 CGI 命令进程.

 在处理CGI 命令后,或HTTP Request Type不是CGI, 用内置在EVB B/D 的ROM File Image的URI Name搜索文件.

如果找到了文件, 创建 HTTP回复消息并发送. 

 HTTP回复消息由HTTP Response Header 传送和HTTP Response Body传送组成. 传送 HTTP Response Header时, 用HTTP Request Type作为元素调用make_http_response_head() 以创建 HTTP Response Header. 传送完已创建的HTTP Response Header后,再传送HTTP Response Bodyd. 例如, 如果 HTTP Response body 是 ROM File Image中的任意文件, 则比iinChip™的 MTU大得多. 因此在传送之前需要分开为iinChip™的最大大小. 此时,如果定义在EVB B/D中的系统环境变量存在于 HTTP Response Body 中, 就调用 replace_sys_env_value() 并用存储在EVB B/D中的系统环境值代替系统环境变量.

<表 3‑29: “evbctrl.html” 系统环境变量使用>

<表 3-30> EVB B/D的ROM File Image的部分“evbctrl.html”.

 系统环境变量的长度被定义成将被替代的系统环境值的长度. 例如, 如果EVB的源IP地址是最长16位的字符串. 因此 $SRC_IP_ADDRESS$的长度也是16. EVB B/D 的‘ROM File System’ 可用WIZnet提供的 “ROMFileMaker.exe”创建. 请参考 “ROM File Maker Manual Vx.x.pdf” 以获得更多信息.

  HTTP 请求消息能通过parse_http_request()分成 Method和Request-URI ,并存储在<表 3-31>定义的 ‘st_http_request’ Date Type中. 它用get_http_uri_type()得到请求URI Type.

<表 3‑30: “st_http_request” 数据>

<图 3.20: parse_http_request()>

<图 3.21: find_http_uri_type()>

Request-URI 保存在st_http_request 的URI [MAX_URI_SIZE]中,它在“?”符号前有 URI Name,在“?”符号后有Query String. 当 Request-URI 从网络浏览器传送到网络服务器时, SP(Space)以‘+’的形式发送,其他Reserved Texts 以“%HEXHEX”的形式发送.相应地, Request-URI中的Reserved Texts需要被译码成原来的值, 从 ‘+’ 到 SP,从%HEXHEX到相应的ASCII值. Request-URI译码的详细信息请参考RFC1738.用get_http_uri_name()提取Request-URI的URI name. Request-URI的Query String 可以包含一到多个用“&”作为分隔符的 “variable=value” 对. 通过get_http_param_value()函数,可以在Query String提取相应的变量值.

<图 3.22: get_http_uri_name() & get_http_parse_value()>

Web Server Program EVB B/D网络浏览器程序的CGI处理和一般的基于OS的网络浏览器程序不相同.基于OS的网络浏览器程序创建单独的进程独立地处理处理器之间的通信. 然而, EVB B/D网络服务器是无操作系统的, 因此它不是创建独立的进程, 而是调用相关函数直接进行CGI处理. EVB B/D 支持更新网络信息的 “NETCONF.CGI” 以及控制文本LCD和EVB B/D D1/D2 LED 的“LCDNLED.CGI” .<图 3.23>和 <图 3.24> 是两个CGI进程图. 

 

<图 3.23: NETCONF.CGI 处理>

<图 3.24: LCDNLED.CGI 处理>

NETCONF.CGI的<FORM>以“POST” Method提交. 用“POST” Method提交<FORM> 不是提交在 Query String而是提交在HTTP请求消息的. NETCONF.CGI的这些参数值同样用于使用get_http_param_value()函数来提取相关参数值. 

of LCDNLED.CGI的<FORM> 用“GET” Method提交, <FORM> 以 “GET” Method提交,是提交在Request-URI的Query String. Request-URI的Query String提交的参数同样可以用get_http_param_value()函数提取参数值. 

<表 3‑31: web_server()相关函数>

3.2.6.5.     DHCP 客户端

DHCP客户端程序从网络中的DHCP服务器分配网络信息. 注意,如果DHCP客户端程序必须在其他程序之前开始,那是因为它管理网络信息设置. 首先, 回顾DHCP(动态主机配置协议)的基本信息, 然后得到 DHCP 客户端程序的进一步使用.  

 DHCP在传输层使用 UDP协议,用UDP 广播与DHCP服务器通信. 使用广播的原因是因为它没有IP地址,且不知道DHCP 服务器IP 地址. 当UDP 在iinChip™里广播时,目的 IP 地址需设置为255.255.255.255’ 以广播包传送.

<图3.25> 是DHCP 服务器和客户端之间的消息流.

<图 3.25: DHCP 消息流>

首先, DHCP客户端广播DISCOVERY 消息到本地网络. 如果DHCP服务器存在于网络中, DHCP服务器接收到 Discovery 消息,并且提供诸如IP,网关IP, 子网掩码和DNS服务器IP之类的能被DHCP客户端使用的网络信息以及如作为OFFER消息到DHCP客户端的租约时间等信息. DHCP客户端通过接收OFFER消息能检测到DHCP服务器,并发送REQUEST 消息到DHCP服务器以便使用DHCP服务器建议的网络信息. 接收到来自DHCP客户端的 REQUEST 消息后, DHCP服务器核实是否请求的网络信息可用.如果可用, DHCP服务器发送ACK 消息, 如果无效, NACK消息被发送到 DHCP客户端. 接收到来自DHCP服务器的 ACK 消息后, DHCP 客户端使用提供的网络信息. 网络信息只有在DHCP服务器建议的租约时间内才有效.因此, 如果DHCP 客户端想继续使用网络信息,通常要在一半的租约时间之后重新发送 REQUEST消息到DHCP服务器以保持网络信息. 在该进程中, DHCP 客户端可以从DHCP服务器获得一样的或是新的网络信息. 如果接收到新的网络信息,就一定要使用新的网络信息.

 DHCP服务器和客户端之间的消息格式如<图 3.26>所示,有544字节. 请参考‘RFC1541’ 文档以获得DHCP消息格式每个区的详细解释. 第一字节的op 区 决定请求/回复, ciaddr之后的区用于传送网络信息, 312字节的可选区用于传送消息类型或客户端标识符之类的信息.

<图 3.26: DHCP 消息格式>

 <表 3‑32: DHCP消息数据类型>

 

<图 3.26>所示的DHCP消息通过<表 3-33>所定义的RIP_MSG数据类型管理. 请参考“inet/dhcp.h”

 

 简单看一些DHCP消息的选项字段, 选项字段有 <图 3.27>所示的格式, 它包括 Magic Cookie Field, 一个4字节大小的Lease Identification Cookie 和 从Code 0到Code 255的Code Set. 从 Code1 到 Code 254, codes由{Code, Len, Value}对组成, Code 0 和Code 255只由{Code} 组成. 想知道Options Field的每个Code的更详细解释, 请参考RFC1533.

<图 3.27: DHCP 消息的选项字段格式>

<表 3‑33: DHCP消息 Option Code定义>

 

在312字节的选项字段中,没有使用的字节用0填补.   

 

<表 3-34>是在“inet/dhcp.h”定义成枚举数据类型的例子,并给出了在DHCP客户端程序中最常用的 Option Codes.

 

 没有在<表 3-34>中定义的其他代码在DHCP客户端程序中没有使用.

 

 The operation of DHCP客户端程序的运行在EVB B/D的 main()中显示. 请参考 <图3.3: EVB B/D的 main()>

 

 首先, 设置DHCP客户端在初始化时使用的 MAC地址. 网络中的所有设备的MAC地址是唯一的. MAC 地址是网络通信最基本的地址,也是DHCP服务器识别DHCP客户端的必要信息. 要得到DHCP客户端程序的MAC地址,用EVB B/D的MAC地址设置DHCP客户端的全局变量SRC_MAC_ADDR. 通过在SRC_MAC_ADDR设置后调用init_dhcp_client(), 可以注册两个函数,以便在DHCP服务器分配的IP地址发生冲突和从DHCP服务器更新IP地址时调用.   

 

 当调用init_dhcp_client()时, 如果每一个函数不是特定的, DHCP 客户端程序的set_DHCP_network() 和 proc_ip_conflict() 分别进行登记.

 

<图 3.28: init_dhcp_client()>

当网络信息更新或发生IP冲突时, 注册evb_soft_reset() 以自动重启EVB B/D.

第二, 网络信息获取可以通过getIP_DHCPS()完成.

<图 3.29: getIP_DHCPS()>

getIP_DHCPS()用setSIPR()和setSHAR()等初始化iinChip™. 然后作为DHCP客户端程序状态初始化‘dhcp_state’ 变量为 ‘STATE_DHCP_DISCOVER’. 初始化之后, 调用 send_DHCP_DISCOVER() 以传送一个DHCP DISCOVERY消息给 DHCP 服务器.

在传送 DISCOVERY DHCP 消息后, 初始化定时器变量,定时器变量是网络信息的租约时间,网络信息是通过调用reset_DHCP_time()并且用set_timer()间隔1秒使用‘DHCP Timer’从DHCP服务器获得的.用0初始化DHCP_Timeout后,等待接收来自DHCP服务器的DHCP消息, 只要 ‘DHCP_WAIT_TIME’ 定义了并且和它在‘MAX_DHCP_RETRY’定义一样.在等待‘DHCP_WAIT_TIME x MAX_DHCP_RETRY’ 时间时,getIP_DHCPS()通过check_DHCP_state()不断检查dhcp_state是否改变为STATE_DHCP_LEASED. 

STATE_DHCP_LEASED 状态表示网络信息已被DHCP客户端得到并且意味着getIP_DHCP() 运行成功. 如果在‘DHCP_WAIT_TIME x MAX_DHCP_RETRY’等待时间内没有从DHCP服务器获得网络信息, check_DHCP_state() 将设置DHCP_Timeout为1. 当DHCP_Timeout是1时, 在释放DHCP定时器之后getIP_DHCPS() 返回失败.  

如果从DHCP服务器获取网络信息失败, EVB B/D 用默认网络信息或先前获取的网络信息设置网络配置.

<表 3-35>是DHCP客户端的状态, 超时和 重试计数的定义.

第三, 从DHCP服务器得到的网络信息的管理可通过check_DHCP_state()实现.<图 3.30>展示了在 check_DHCP_state() 进程DHCP客户端状态改变时的DHCP消息流.

 <表 3‑34: DHCP 客户端状态 & 超时定义>

在getIP_DHCP()中,‘DHCP_XID’ 是可变的,用于设置如 <图 3.26: DHCP 消息格式>所示DHCP消息的xid Field, ‘DHCP_XID’必须是唯一的,且一直保持同一个值直到网络信息的租约时间过期. DHCP_XID 在这里设成固定值 ‘0x12345678’, 但是推荐使用随机值.

当初始化iinChip™为DHCP服务器通信模式时,建议设置源IP地址为‘0.0.0.0’. 可以使用任意IP地址设置iinChip™的源IP地址但是使用‘0.0.0.0’更好些, 因为‘0.0.0.0’ 在IPv4中相当于A级地址并且是一个实际上不被使用的空IP地址. 由于这个原因,就不可能与其他网络发生冲突.  

对于DHCP服务器传送UDP广播包, 注意DHCP消息的标志位MSB必须设成1. 请参考 <图3.26: DHCP 消息格式>.
<表 3-36> 是设置标识字段的一部分代码.

 <表 3‑35: DHCP 消息标识字段设置>

第三, 从DHCP服务器得到的网络信息的管理可通过check_DHCP_state()实现.<图 3.30>展示了在 check_DHCP_state() 进程DHCP客户端状态改变时的DHCP消息流.

<图 3.30: 通过DHCP 客户端状态的DHCP 消息流>

check_DHCP_state() 检查是否有来自DHCP服务器的DHCP消息, 它接收并分析DHCP消息. 通过被分析的DHCP消息的字节, 如果是可接收的DHCP消息,在按照<图 3.30>所示的DHCP消息流改变DHCP客户端状态后,check_DHCP_state() 改变成下一个状态.

<图 3.31: check_DHCP_state()>

check_DHCP_state() 通过<图 3.31>所示的流程和DHCP客户端状态相应地进行处理. 如果看一下check_DHCP_state()的DHCP_STATE_LEASED状态,从DHCP服务器获得的租约时间是有限的, 假如一半的租约时间过去了, check_DHCP_state()发送 DHCP_REQEUST消息到DHCP 服务器并且在备份源IP后改变它为DHCP_STATE_REREQUEST. 由于它连续不断地发送DHCP_REQUEST 给服务器, 网络信息得到保持.

<图 3.32: parse_DHCPMSG() & check_DHCP_Timeout()>

parseDHCPMSG() 从DHCP服务器接收DHCP消息, 对DHCP消息的类型进行分类并保存网络信息. 当运行check_DHCP_state()时, 以防在DHCP_WAIT_TIME期间没有收到DHCP消息或收到的DHCP消息不是所期望的,就调用check_DHCP_Timeout()以重新发送DHCP消息到DHCP服务器.如果DHCP消息的重新发送次数达到 MAX_DHCP_RETRY, 在parseDHCPMSG()初始化所有变量以开始连接DHCP服务器和DHCP客户端后,parseDHCPMSG() 发送 DHCP_DISCOVER 消息到DHCP服务器.

<表3‑36: DHCP 客户端参考函数>

 3.2.6.6.     DNS 客户端

在介绍客户端设置之前简单看一下DNS(域名系统).

DNS 是一个可以实现因特网域名和因特网IP地址之间相互转换的系统. DNS由包含IP地址和域名之间的映射表的名称服务器以及通过转换查询为名称服务器来接收查询结果的DNS解析器组成.  

DNS 解析器查询IP地址或域名以转换成本地名称服务器. 本地名称服务器接收查询,搜寻它的数据库,并回应解析器. 如果解析器不能找到它所寻找的信息, 本地名称服务器在更高层发送接收到的查询给名称服务器,并可把接收到的答案发送给解析器.    

<图3.33: 域名系统结构 & DNS 消息流>

 在 <图 3.33>可见, DNS 查询 和 DNS应答消息在DNS解析器和名称服务器是可变换的,由<图 3.34>所示的5部分组组成.

<图 3.34: DNS消息格式>

Header Section是固定的2字节长,其他4部分是变长的. Answer、Authority和Additional Section被叫做Resource Records(RRs). Header, Questio,和RRs中的每一个都不同的格式.  

DNS消息的Header Section有消息类型, DNS 查询类型和变长的计数信息.

在<图 3.35: Header Section 格式>中, 当DNS消息是从解析器到名称服务器的请求时,QR Field 是 0;当它是从名称服务器到解析器时,QR Field 是 1. 当DNS消息以IP地址查询域名时,Opcode Field 是 0 ;当它查询名称服务器状态时, QR Field 是 1.

QDCOUNT, ANCOUNT, NSCOUNT和 ARCOUNT Field计算变长信息, 代表由Question, Answer, Authority和 Additional Section组成的 Block Count. Question Section 由<图 3.36: Question Section格式> 所示格式组成. Answer, Authority和Additional Sections由<图 3.37>所示组成.

 比如, 如果QDCOUNT 是 1, ANCOUNT 是10, NSCOUNT 是10, 并且 ARCOUNT 是10 ,那么 Question Section是由 <图 3.36: Question Section 格式>的第一块所示组成, Answer, Authority和Additional Section 由<图 3.37>的10块所示的部分组成.

<图 3.37>的NAME, <图 3.36>的QNAME Filed以及 RDDATA Field 也是变长的. QNAME 和 NAME 是变长字段,它们由<图 3.36> Format所示的部分组成并且处理每一个字段. RDDATA是变长字段,它用RDLENGTH Field的数据长度进行处理. 

 要获得更多的信息,请参考 RFC1034 和RFC1035

DNS 消息通过在<表 3-38>定义的Data Type运行. 请参考 “inet/dns.h”

<表 3‑37: DNS 消息数据类型>

DNS 解析器基于gethostbyaddr() 和gethostbyname()运行. gethostbyaddr() 发送因特网IP 地址到Internet Domain Name,并且gethostbyname() 发送 因特网Domain Name到因特网 IP地址. gethostbyaddr() 和gethostbyname() 测试 DNS 名称服务器 IP 地址的设置,并搜索连接DNS名称服务器所需的iinChip™ 空闲频道.如果iinChip™存在空闲频道, gethostbyaddr() 和gethostbyname() 用‘BYNAME’或‘BYIP’作为元素调用 dns_query(). 

gethostbyaddr()和gethostbyname()的例子, 请参考 Chapter 3.2.5.3 Ping Request Program.

  和DNS名称服务器的实际连接是通过 dns_query()进行的, gethostbyaddr() 和 gethostbyname() 只是报告dns_query()的结果.

<表 3‑38: dns_query()查询类型定义>

<图 3.38: gethostbyaddr() & gethostbyname()>

dns_query() 初始化用于DNS之间相互工作的缓冲器,并基于Query Type ‘BYNAME’和‘BYIP’创建Question Section的QNAME.如果Query Type 是 ‘BYNAME’,也就是说, 当用IP地址查询域名时,域名可以在不发送的情况下被使用.

当Query Type 时 ‘BYIP’, 也就是说, 当用IP地址查询域名时, 改变 IP 地址为 IP 地址串, 并且在把 “in-addr.arpa” 加到改变的 IP 地址串后使用QNAME. 在创建QNAME之后, 创建UDP Socket用于 DNS 相互工作 ,并通过调用dns_make_query()创建DNS 请求消息. 如果DNS请求消息创建成功, DNS请求消息通过UDP Socket发送给DNS名称服务器. 发送DNS请求消息后,接收DNS回应消息或者等待直到等待时间失效.    

如果在等待时间期间从DNS名称服务器接收到DNS回复消息,用dns_parse_response()分析所接收到的DNS回复消息.dns_query()根据Query Type返回 IP地址或 域名.  

<图 3.39>是 dns_query()的进程图

 

<图 3.39: dns_query()>

 

 

<图 3.40: dns_makequery()>

dns_makequery()创建 DNS请求消息以被发送给DNS名称服务器. 因为DNS请求消息可以只用Header和 Question Section 进行查询,不需要创建RRs Sections.如果在 dns_makequery()里查询header section的创建, 首先, 在DNS消息相互工作里设置ID Field值为任意值. 在这里, ID设置成 0x1122, 在以后的互相工作中,该值每次递增1. QR, Opcode, AA, TC和RD Field通过MAKE_FLAG0()分别设置成 QR_QUERY, OP_QUERY/OP_IQUERY, 0, 0和1, 且 RA, Z和RCODE Field 通过MAKE_FLAG1()分别设置成0, 0和 0.

<表 3‑39: Header Section用到的常数和MACRO >

 因为计数字段, QDCOUNT, ANCOUNT, NSCOUNT和 ARCOUNT只有1个question,每一个分别设置成 1, 0, 0和0.

我们看一下Question Section. QNAME Field是设置IP地址串的字段. 域名和IP地址串由1字节的Label length 和最大63字节的Label组成. QNAME的结尾总是设置成0以找出QNAME的变长. <图 3.41> 是发送在QNAME字段中的域名“www.wiznet.co.kr”的实际例子.

 当把域名当成QNAME 时,Question Section的QTYPE Field设置成‘TYPE_PTR’. 当是IP地址时,Question Section的QTYPE Field设置成 ‘TYPE_A’ ,因为QCLASS Field包含在因特网中,设置QCLASS Field为 ‘CLASS_IN’.

表 3-41是用在QTYPE & QCLASS Fields中的常数的定义.

 <表 3‑40 : QTYPE & QCLASS 字段的参数定义>

<图 3.42: dns_parse_response()>

 <图 3.42>的dns_parse_response() 分析通过DNS名称服务器接收到的回复消息. dns_parse_response() 检查回复消息是否和发送给DNS名称服务器的请求消息ID一致,并且通过检查Header Section的QR Field检查接收到的消息是否是一个回复消息. 如果接收到的消息是来自DNS名称服务器的回复,通过检查Header Section的RCODE Field值可以决定改变的成功.

<表 3-42> 是RCODE Field用到的常数的定义.  

 <表 3‑41 : Header Section’s RCODE字段的常数定义>

如果RCODE是RC_NO_ERROR,那么诸如Question, Answer, Authority和Additional Section的变长字段将被分析. 因为必要的信息设置在 Answer Section里,在这种情况下,分析Answer Section, 不对其他字段进行分析和处理.如果需要 Authority 和Addition Section的消息,可以自己轻松地得到.  

 通过调用dns_parse_question(),Question Section被处理多达Header Section的QDCOUNT次.通过调用dns_parse_question(), Answer Section被处理多达Header Section的ANCOUNT次.

 

Answer Section的TYPE有一个值来自 <表 3-41 : QTYPE & QCLASS Filed的常数定义>,该值是 TYPE_A 或 TYPE_PTR. 如果域名改变成了IP地址, 可以从TYPE_A获得改变的IP地址;如果IP地址改变成了域名,可以从TYPE_PTR 得到域名. 改变域名或IP地址也通过parse_name()处理和提取.

如果RCODE是RC_NO_ERROR,那么诸如Question, Answer, Authority和Additional Section的变长字段将被分析. 因为必要的信息设置在 Answer Section里,在这种情况下,分析Answer Section, 不对其他字段进行分析和处理.如果需要 Authority 和Addition Section的消息,可以自己轻松地得到.  

 通过调用dns_parse_question(),Question Section被处理多达Header Section的QDCOUNT次.通过调用dns_parse_question(), Answer Section被处理多达Header Section的ANCOUNT次.

 

dns_parse_question() 分析和处理 Question Section. DNS请求消息的Question Section中实际上没有信息,但是必须进行处理以获得Answer Section的开始位置. 因为 Question Section的QNAME Field是变长的, parse_name() 处理QNAME Field 以处理变长的进程,且跳过QTYPE和QCLASS Field.   

dns_answer() 分析并处理Answer Section. Answer Section 是实际上发生转变的部分,并给Answer Section的TYPE Field提供合适的处理.  

Answer Section的TYPE有一个值来自 <表 3-41 : QTYPE & QCLASS Filed的常数定义>,该值是 TYPE_A 或 TYPE_PTR. 如果域名改变成了IP地址, 可以从TYPE_A获得改变的IP地址;如果IP地址改变成了域名,可以从TYPE_PTR 得到域名. 改变域名或IP地址也通过parse_name()处理和提取.

<图 3.44: parse_name()>

parse_name() 处理Question Section的QNAME Field或RRs Section 的NAME和 RDDATA Field. QNAME, NAME和RDDATA Field大多数情况下由如 <图 3.41: Question Section的QNAME Field转变例子 >所示组成. 然而, 它们可被压缩以减少 DNS消息大小. 压缩情况用2字节表示. 第一个字节里,如果上面2比特是’11,’ 说明 Label被压缩. 偏移位由第一字节除去上面2比特的部分和第2字节组成.

该偏移是DNS消息的Offset,意味着Label的实际值的位置是从DNS消息的开始的偏移值处. 当压缩图想重新使用已经使用于DNS消息的域名时, 相关的域名设置已经在DNS消息中的偏移为Indirect,以至于能减小DNS消息的大小. <图 3.45>是DNS消息的压缩图和它的应用的一个例子.

 

<图 3.45: DNS消息压缩图>

<图3.45>的压缩图例子展示了 “F.ISI.ARPA”, “FOO.F.ISI.ARPA”, “ARPA”和ROOT情况下的DNS消息. “F.ISI.ARPA” 以<图3.41: Question Section的QNAME Field转换例子 >的格式不经过压缩以DNS消息的20位偏移被处理.

 在 “FOO.F.ISI.ARPA”中,因为除了“FOO”的剩下的部分和先前被处理的Name一样, “FOO”以 <图 3.41: Question Section的QNAME Field转换例子 > 格式不经过压缩被处理,剩余的名称以Offset 26被处理. ROOT是最高级的域名,Label Length Field是0时被处理.    

 在Name的分析之前,parse_name()检查Label Length Byte的高2比特是否是11,如果是 ‘11’,相关的Label在Label所在的DNS消息偏移位处分析Label. 如果不是 ‘11’, Label以<图3.41: Question Section的QNAME Field转换例子 >的形式被分析以及处理.

<表 3‑42 : DNS客户端的参考函数>

 

感谢关注!

相关内容可以参考WIZnet中文博客:

W3150A+评估板–EVB-PIC24 用户手册(一)

W3150A+评估板–EVB-PIC24 用户手册(二)