天天看点

《Windows网络与通信程序设计(第3版)》——2.2 Winsock的寻址方式和字节顺序include // 完整代码在配套光盘的LocalHostInfo工程下

本节书摘来自异步社区《windows网络与通信程序设计(第3版)》一书中的第2章,第2.2节,作者: 陈香凝 , 王烨阳 , 陈婷婷 , 张铮 更多章节内容可以访问云栖社区“异步社区”公众号查看。

本节讲述在winsock中主机地址信息的表示方法,以及相关的操作函数。

2.2.1 winsock寻址

因为winsock要兼容多个协议,所以必须使用通用的寻址方式。tcp/ip使用ip地址和端口号来指定一个地址,但是其他协议也许采用不同的形式。如果winsock强迫使用特定的寻址方式,添加其他协议就不大可能了。winsock的第一个版本使用sockaddr结构来解决此问题。

在这个结构中,第一个成员sa_family指定了这个地址使用的地址家族。sa_data成员存储的数据在不同的地址家族中可能不同。本书仅仅使用internet地址家族(tcp/ip),winsock已经定义了sockaddr结构的tcp/ip版本——sockaddr_in结构。它们本质上是相同的结构,但是第2个更容易操作。

在winsock中,应用程序通过sockaddr_in结构来指定ip地址和端口号,定义如下。

(1)sin_family域必须设为af_inet,它告诉winsock程序使用的是ip地址家族。

(2)sin_port域指定了tcp或udp通信服务的端口号。应用程序在选择端口号时必须小心,因为有一些端口号是保留给公共服务使用的,如ftp和http。基本上,端口号可分成如下3个范围:公共的、注册的、动态的(或私有的)。

0~1 023由iana(internet assigned numbers authority)管理,保留为公共的服务使用。

1 024~49 151是普通用户注册的端口号,由iana列出。

49 152~65 535是动态和/或私有的端口号。

普通用户应用程序应该选择1 024~49 151的注册了的端口号,以避免使用了一个其他应用程序或者系统服务已经使用的端口号。在49 152~65 535之间的端口号也可以自由地使用,因为没有服务注册这些端口号。

(3)sin_addr域用来存储ip地址(32位),它被定义为一个联合来处理整个32位的值,两个16位部分或者每个字节单独分开。描述32位ip地址的in_addr结构定义如下。

用字符串“aa.bb.cc.dd”表示ip地址时,字符串中由点分开的4个域是以字符串的形式对in_addr结构中的4个u_char值的描述。由于每个字节的数值范围是0~255,所以各域的值都不可超过255。

(4)最后一个域sin_zero没有使用,是为了与sockaddr结构大小相同才设置的。

应用程序可以使用inet_addr函数将一个由小数点分隔的十进制ip地址字符串转化成由32位二进制数表示的ip地址。inet_ntoa是inet_addr函数的逆函数,它将一个网络字节顺序的32位ip地址转化成字符串。

注意,inet_addr返回的32位二进制数是用网络顺序存储的,下一小节详细讲述字节顺序。

2.2.2 字节顺序

字节顺序是长度跨越多个字节的数据被存储的顺序。例如,一个32位的长整型0x12345678跨越4个字节(每个字节8位)。intel x86机器使用小尾顺序(little-endian),意思是最不重要的字节首先存储。因此,数据0x12345678在内存中的存放顺序是0x78、0x56、0x34、0x12。大多数不使用小尾顺序的机器使用大尾顺序(big-endian),即最重要的字节首先存储。同样的值在内存中的存放顺序将是0x12、0x34、0x56、0x78。因为协议数据要在这些机器间传输,所以就必须选定其中的一种方式做为标准,否则会引起混淆。

tcp/ip统一规定使用大尾方式传输数据,也称为网络字节顺序。例如,端口号(它是一个16位的数字)12345(0x3039)的存储顺序是0x30、0x39。32位的ip地址也是以这种方式存储的,ip地址的4部分存储在4个字节中,第一部分存储在第一个字节中。

上述sockaddr和sockaddr_in结构中,除了sin_family成员(它不是协议的一部分)外,其他所有值必须以网络字节顺序存储。winsock提供了一些函数来处理本地机器的字节顺序和网络字节顺序的转换。

这些api是平台无关的。使用它们可以保证程序正确地运行在所有机器上。

下面代码示例了如何初始化sockaddr_in结构。

2.2.3 获取地址信息

通常,主机上的接口被静态地指定一个ip地址,或者是由配置协议来分配,如动态主机配置协议(dhcp)。如果dhcp服务器不能到达,系统会使用automatic private ip addressing (apipa)自动分配169.254.0.0/16范围内的地址。

1.获取本机ip地址

获取本机的ip地址比较简单,下面的getallips例子打印出了本机使用的所有ip(一个适配器一个ip地址),程序代码如下。

getallips先调用gethostname取得本地主机的名称,然后通过主机名得到其地址信息。

2.获取mac地址

有时为了检测网络,或者为了一些其他特殊的目的,需要自己来直接操作原始数据帧(第9章再具体讲述),这就需要获取自己和lan中其他主机的mac地址。

获取本地机器的mac地址很容易,使用帮助函数getadaptersinfo即可。此函数的作用是获取本地机器的适配器信息,用法如下。

下面的例子localhostinfo打印出了本机的ip地址、网络(内部lan)的子网掩码、网关的ip地址和本机的mac地址。本书第9章讲述网络扫描与检测时还要使用本例中的代码。

调用自定义函数getglobaldata之后,程序运行结果如图2.1所示。

要取得lan中其他主机的mac地址,最简单的方法是使用sendarp函数向目标主机发送arp请求封包。这个函数返回指定的目的ip地址对应的物理地址,即mac地址。第9章再详细讨论arp,以及sendarp函数的用法。

《Windows网络与通信程序设计(第3版)》——2.2 Winsock的寻址方式和字节顺序include // 完整代码在配套光盘的LocalHostInfo工程下

继续阅读