在windowsXP SP3+vc6.0下,该结构体为:
大小为24个字节;在使用WSAStringToAddress转换函数时,使用该结构体会失败,原因就是结构体大小小于函数所需要的28个字节。
查看了同一台机器上的visual studio 2005的头文件(ws2tcpip.h),上述结构体被命名为old,而同样名字则使用了另一个结构体,大小为28字节:
关于这个多出来的scope_id变量,具体应用和意义不是很清楚(link-local地址会有所涉及)。
网上搜了下,发现另一个系统建议的同时支持IPv4和IPv6的结构体:
此结构体大小为128字节,足够大了(大到目前还不知道多出来的空间用于存放什么数据)。
而linux下,sockaddr_in6结构体和windows下最新的结构体一致,sockaddr_storage大小也是128;所以对于windows下vc6.0以下,采用sockaddr_storage结构体,而其他则一律采用sockaddr_in6结构体。
(vc6.0对应的_MSC_VER为1200)
2. 关于IPv6服务器同时支持IPv4和IPv6客户端
按照UNPv3上说,可以在服务器上仅创建一个PF_INET6的socket,bind在IPv6的通配地址,此时该监听socket可以同时支持IPv4和IPv6的客户端接入(当然,前提条件是该服务器同时需具有IPv4地址和IPv6地址),而IPv4客户端接入进来后显示的地址为一个IPv4映射的IPv6地址(可以通过IN6_IS_ADDR_V4MAPPED宏辨别)。
同时,如果不需要这种特性,而强制只是支持IPv6客户端接入,可以通过设置socket属性:IPV6_V6ONLY来修改。
linux上一切ok,然而到了windows上,同样的情况下,windows默认只支持接入IPv6客户端(可恶的默认值),即IPV6_V6ONLY属性值为非0,
而要想在windows上设置该属性,需要vista或者更新的windows版本(完全忽视了windowsXP+vc6.0的用户)。
摘自MSDN:
Indicates if a socket created for the AF_INET6 address family is restricted to IPv6 communications only. Sockets created for the AF_INET6 address family may be used for both IPv6 and IPv4 communications. Some applications may want to restrict their use of a socket created for the AF_INET6 address family to IPv6 communications only.
When this value is non-zero (the default on Windows), a socket created for the AF_INET6 address family can be used to send and receive IPv6 packets only.
When this value is zero, a socket created for the AF_INET6 address family can be used to send and receive packets to and from an IPv6 address or an IPv4 address. Note that the ability to interact with an IPv4 address requires the use of IPv4 mapped addresses.
This socket option is supported on Windows Vista or later.
3. 关于WSAStringToAddress和WSAAddressToString函数
windows提供的这两个函数提供了sockaddr结构和字符串之间的转换,linux下类似的为inet_ntop和inet_pton。比较特殊的是windows这两个函数中的字符串格式不仅仅是ip地址,还包含了端口和网络接口信息,如:[fe80::21e:c9ff:fe45:832c%4]:55392,把sockaddr中所有有效的信息都返回了。同样的,在WSAStringToAddress中,可以把类似结构的字符串传入,得到对应的sockaddr结构体。
个人意见:KISS原则;按照这种做法,本来通过一次调用就可以获得的ip地址,现在还要额外做一次分析工作(不知到有没有提供相应的宏),其实这些函数的需求是将不可读的ip地址变为可读,其他信息,直接在需要时从sockaddr结构体中就可获得。
4. 关于link-local地址
实践下来,link-local地址是可以用于局域网内的通信(不过路由器),需注意的是,利用link-local地址进行通信,需指定本地网络接口。例如ping:
windows:ping6 fe80::21c:c0ff:feeb:5cc3%4 (本地网络接口id为4)
linux:ping6 -Ieth0 fe80::21e:c9ff:fe34:5112 (本地网络接口eth0)
而在具体编程中,通过strace上述命令,可以看到实际该接口id是设给了sockaddr_in6.sin6_scope_id = if_nametoindex("eth0");
但为什么link-local地址通信时,需要针对具体网络接口,还不知道原因?而相对的global地址,却不需要。
这个问题被我在迷糊中想到了一个解释(很灵异):之前,如果有多个网络接口的话,那么接口上的ip地址不能为同一网段,否则就会冲突;而现在,IPv6给每个网络接口都默认分配了一个link-local地址,即FE80开头的地址,显然都是同一网段的;此时如果和该网段的另一个地址通信,系统会不知道源地址采用哪一个接口上的地址,因为无论哪个其实都可以和目标进行通信,所以,必须人为的指定具体接口。
5. 关于组播组
介绍两个特别的组播组的用法。FF01::1和FF02::1,分别ping这两个组播组,凡是响应第一个的,都是支持IPv6的局域网内的机子(包括linux和windows);而响应第二个的,则仅是windows主机。原理是所有支持IPv6的主机都会加入FF01::1这个组播组,而windows主机还会加入FF02::1这个组播组。