WIN32汇编语言教程:第16章 TCP/IP和网络通信 · 16.5 ICMP协议编程(2)
hConsoleInput参数为控制台的标准输入句柄,lpBuffer指向用来接收输入数据的缓冲区,nNumberOfCharsToRead参数指定要读取的数据长度,lpNumberOfCharsRead指向一个双字,函数在这里返回实际读取的字节数。
3. 截获Ctrl+Break
在控制台程序中往往需要截获Ctrl+Break(或Ctrl+C)的组合键来判断是否要中途退出,这在DOS时代的程序中靠截获Int 23h中断来实现,但在Win32中不能再使用这种方法。
Win32控制台程序使用SetConsoleCtrlHandler函数来将Ctrl+Break的处理代码重新定义到自己指定的子程序中:
invoke SetConsoleCtrlHandler,HandlerRoutine,Add
HandlerRoutine参数指定处理Ctrl+Break按键的子程序地址,Add参数指定为TRUE的时候,系统将设置这个子程序为Ctrl+Break处理程序,如果指定为FALSE,系统将取消这个设置。HandlerRoutine参数也可以为NULL,这时当Add参数设置为TRUE,系统将忽略对Ctrl+Break的处理,设置为FALSE的话系统将恢复原来的处理方式。
上面例子中的_ConsoleInit子程序使用SetConsoleCtrlHandler函数将处理子程序设置到_CtrlHandler中。
Ctrl+Break的处理子程序必须按照规定的格式定义:
HandlerRoutine proc dwCtrlType
该子程序有一个输入参数dwCtrlType,系统调用该子程序的时候将使用这个参数指明发生事件的类型,事件类型可能是以下几种:
● CTRL_C_EVENT——收到Ctrl+C字符。
● CTRL_BREAK_EVENT——收到Ctrl+Break字符。
● CTRL_CLOSE_EVENT——用户关闭了控制台窗口(比如按下了控制台窗口上面的关闭按钮或在控制台窗口的菜单上选择了“关闭”等)。
● CTRL_LOGOFF_EVENT——当前用户注销。
● CTRL_SHUTDOWN_EVENT——系统准备关闭。
在前面的例子代码中,_CtrlHandler子程序仅处理CTRL_BREAK_EVENT事件以及CTRL_C_EVENT事件,并在检测到这两个事件的时候设置程序中定义的dwOption变量,以便在主程序的循环中能够通过检测这个变量而退出程序。
4. 控制台程序的编译和链接
在控制台程序中如何创建一个控制台窗口呢?实际上这个窗口不是由控制台程序而是由Windows操作系统自动创建的,程序中并不需要自己去创建。
控制台程序和非控制台程序的PE文件头中的标志是不同的,当系统执行一个文件时首先对文件进行检测,如果是控制台程序就为它建立一个控制台窗口,而对非控制台程序则不作处理。
程序的PE头中的控制台标志是由链接程序设置的,所以指定一个程序是控制台程序的操作是在Link命令中实现的,如Ping例子的makefile文件如下:
NAME = Ping
OBJS = $(NAME).obj
$(NAME).exe: $(OBJS)
Link /SUBSYSTEM:CONSOLE $(OBJS)
$(NAME).obj: $(NAME).asm
ml /c /coff $(NAME).asm
如果链接的时候将Link.exe的subsystem选项指定为console,那么可执行文件就会被链接成控制台程序。
一个有趣的测试就是将一个标准的窗口界面程序按照/subsystem:console选项链接为控制台程序,运行以后就会发现:程序原来图形界面的工作不会有任何异常,但是屏幕上同时会有一个控制台窗口出现,这个窗口就是Windows主动搭配给它的。当然由于程序不会往上面输出信息,这个窗口中自始至终不会有任何东西出现。这也说明了控制台程序中可以使用任何Win32编程中的特征。
16.5.2 ICMP协议
1. ICMP协议简介
ICMP协议是Internet Control Message Protocol(网际控制消息协议)的缩写,它是由RFC 792文件定义的。ICMP协议用于处理错误消息和控制消息,当一个IP数据报在网络上的传输发生错误时,错误发送点的主机或路由器往往通过一个ICMP消息来通知数据报的发送者。
ICMP错误消息在以下几种情况下发送:数据报不能到达目的地时;网关已经失去缓存功能;网关能够引导主机在更短路由上发送。虽然ICMP协议用于传递错误消息,但它的本意并不是由此让IP协议变得可靠,而是仅为了当网络出现问题的时候能够返回信息而已。
ICMP协议使用IP协议做底层支持,当一个程序发送ICMP数据包的时候,这个数据包最后是被封装进一个IP数据报中传递的,从这个意义上看,ICMP协议似乎应该和TCP和UDP等传输层协议处于同一个层次,但是实际上它被定义在Internet层上,这是因为ICMP协议是当做IP协议的附属协议定义的,它由IP模块来实现,而且更多的情况下是被IP模块自己用来传输错误消息,所以在图15.1的TCP/IP模型中,ICMP协议是在IP协议的一个角上。
ICMP协议是无连接的,而且并不使用端口进行复用,从编程角度来看,只要发送端完成ICMP报文的封装并指定一个IP地址发送出去,这个报文就可以像UDP包一样自己到达目标地址。
2. ICMP报文的类型
在MASM32软件包的Windows.inc文件中,ICMP首部被定义成icmp_hdr结构:
icmp_hdr STRUCT
icmp_type BYTE ? ;类型
icmp_code BYTE ? ;代码
icmp_cksum WORD ? ;校验和
icmp_id WORD ?
icmp_seq WORD ?
icmp_data BYTE ?
icmp_hdr ENDS
其中icmp_type和icmp_code字段用来指定这个ICMP报文的用途,icmp_cksum 字段为整个报文的校验和,接下来的几个字段icmp_id,icmp_seq和icmp_data的定义可能是不确定的,因为它们实际上属于报文内容的一部分,而不同用途的报文其内容的定义方式是不同的。
正如前面所述,ICMP报文用于传递错误消息和控制消息,错误消息是当发出的IP报文段在网络上出现某种错误的时候,由错误发生点的设备发回来的;而控制消息用于当前主机主动向其他设备发送一个ICMP查询消息,对方会根据这个消息送回相应的回复消息,如Ping程序发送的就是“请求回显”ICMP消息,对方主机收到后送回一个“回显应答”ICMP消息。
消息的用途是由ICMP首部的类型和代码组合定义的,详细的定义请参考表16.3,表中同时列出了消息所属的类型。
表16.3 ICMP报文类型
类型 | 代码 | 作用 | 类型 |
0 | 0 | 回显应答 | 查询 |
3 | 0 | 网络不可达 | 错误消息 |
3 | 1 | 主机不可达 | 错误消息 |
3 | 2 | 协议不可达 | 错误消息 |
3 | 3 | 端口不可达 | 错误消息 |
3 | 4 | 需要进行分片但设置了不分片位 | 错误消息 |
3 | 5 | 源站选路失败 | 错误消息 |
3 | 6 | 目标网络不认识 | 错误消息 |
3 | 7 | 目标主机不认识 | 错误消息 |
3 | 9 | 目标网络被强制禁止 | 错误消息 |
3 | 10 | 目标主机被强制禁止 | 错误消息 |
3 | 11 | 由于服务类型TO S ,网络不可达 | 错误消息 |
3 | 12 | 由于服务类型TO S ,主机不可达 | 错误消息 |
3 | 13 | 由于过滤,通信被强制禁止 | 错误消息 |
3 | 14 | 主机越权 | 错误消息 |
3 | 15 | 优先权中止生效 | 错误消息 |
4 | 0 | 源端被关闭 | 错误消息 |
5 | 0 | 对网络重定向 | 错误消息 |
5 | 1 | 对主机重定向 | 错误消息 |
5 | 2 | 对服务类型和网络重定向 | 错误消息 |
5 | 3 | 对服务类型和主机重定向 | 错误消息 |
8 | 0 | 请求回显 | 查询 |
9 | 0 | 路由器通告 | 查询 |
10 | 0 | 路由器请求 | 查询 |
11 | 0 | 传输期间生存时间为0 | 错误消息 |
11 | 1 | 在数据报组装期间生存时间为0 | 错误消息 |
12 | 0 | 坏的IP首部 | 错误消息 |
12 | 1 | 缺少必需的选项 | 错误消息 |
13 | 0 | 时间戳请求 | 查询 |
14 | 0 | 时间戳应答 | 查询 |
17 | 0 | 地址掩码请求 | 查询 |
18 | 0 | 地址掩码应答 | 查询 |
上页:第16章 TCP/IP和网络通信 · 16.5 ICMP协议编程(1) 下页:第16章 TCP/IP和网络通信 · 16.5 ICMP协议编程(3)