请教高人?何谓组播?,还有什么c_root\c_leaf d_root\d_root 在线听课 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 使用WinSock 2实现多址广播清华大学计算中心 蒋东兴 多址广播(multicast,也译作多点传送或组播)是一种一对多的传输方式,传输发起者通过一次传输就将信息传送到一组接收者,与单点传送(unicast)和广播(broadcast)相对应。多址广播使用最广泛的是IP multicast,它标准IP网络层协议的扩展,由Steve Deering定义的Host Extensions for IP Multicasting(RFC 1112)奠定基础。IP Multicasting的定义为:到一个“主机组”的IP数据报的传送,主机组是由零个或多个用同一IP目的地址标识的主机集合。Multicast数据报被传递到其目的主机组的所有成员,并且同常规单点传送的IP数据报一样可靠。主机组的成员是动态的,也就是说,主机可以在任何时间加入或离开主机组。主机组中成员在位置上和数量上都没有限制,一个主机可以同时是一个以上主机组的成员。 在Windows Sockets 1.1中没有定义多址广播,因此,绝大多数Windows Sockets 1.1实现不支持多址广播。Windows Sockets 2为支持IP multicast而定义了一组新的与协议无关的多址广播应用程序接口,归纳起来可以用表1表示。表1 WinSock 2的多址广播APIWSAEnumProtocols() 检测多址广播支持 WSASocket() 指定多址广播类型 WSAJoinLeaf() 加入一个多址广播组并指定角色(发送者和/或接收者) WSAIoctl() SIO_MULTICAST_SCOPE 设置IP生存时间 WSAIoctl() SIO_MULTIPOINT_LOOPBACK 禁止内部回送(loopback) 函数WSAEnumProtocols()返回当前系统中安装的协议的详细描述,这些信息存放在一个协议信息结构(WSAPROTOCOL_INFO)的数组中。在其中的域dwServiceFlags1中的一些标识指示此服务由IP/UDP协议提供,并且位标识XP1_SUPPORT_MULTIPOINT指示该服务支持IP multicast。 为了适用不同的多址广播模式,WinSock 2定义了数据平面(data plane)和控制平面(control plane)两个概念,每一个平面都可以是“有根(rooted)”的或“无根(non-rooted)”的。关于这些概念的详细解释,参见WinSock 2规范附录B。IP multicast是一种无根的数据平面和控制平面。在使用WSASocket()函数请求一个多址广播套接字时需要指定这些角色。这通过在WSASocket()函数的参数dwFlags中使用四个标志位来实现:· WSA_FLAG_MULTIPOINT_C_ROOT,用来创建一个作为c_root节点的套接字,并且只有在相应的WSAPROTOCOL_INFO入口中指示了使用rooted控制平面时才允许。· WSA_FLAG_MULTIPOINT_C_LEAF,用来创建一个作为c_leaf节点的套接字,并且只有在相应的WSAPROTOCOL_INFO入口中指示了XP1_SUPPORT_MULTIPOINT时才允许。· WSA_FLAG_MULTIPOINT_D_ROOT,用来创建一个作为d_root节点的套接字,并且只有在相应的WSAPROTOCOL_INFO入口中指示了使用rooted数据平面时才允许。· 用来创建一个作为d_leaf节点的套接字,并且只有在相应的WSAPROTOCOL_INFO入口中指示了XP1_SUPPORT_MULTIPOINT时才允许。 注意,在IP multicast中,只有标志WSA_FLAG_MULTIPOINT_C_LEAF 和WSA_FLAG_MULTIPOINT_D_LEAF能用来作为WSASocket()函数的dwFlags参数。 WSAIoctl()函数的SIO_MULTIPOINT_LOOP命令码用来设置是否允许内部回送。当d_leaf套接字用于non-rooted数据平面时,它通常是希望能够控制发送出去的通信流量是否能够在同一个套接字上也被接收。WSAIoctl()函数的SIO_MULTIPOINT_LOOP命令码用来允许或禁止多址广播的通信流量的内部回送。 WSAIoctl()函数的SIO_MULTICAST_SCOPE命令码用来设置多址广播范围。当使用多址广播时,常常需要指定多址广播传播的范围。范围由包括的路由网段来定义。范围为0指示多址广播不传播信息到“网线”上,但是可以在本地主机的多个套接字间传播。范围为1(默认值)指示将传播信息到“网线”上,但是不跨越路由器。更高的范围值决定了可以跨越的路由器数量。注意这相当于IP multicast的生存时间(time-to-live,TTL)。 WSAJoinLeaf()函数用来加入一个叶子节点到多址广播会话,其函数原型为:SOCKET WSAAPI WSAJoinLeaf ( SOCKET s, const struct sockaddr FAR * name, int namelen, LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS, DWORD dwFlags );WSAJoinLeaf()具有与WSAConnect()相同的参数和语法,除了它还返回一个套接字描述符(这和函数WSAAccept()一样)及多了一个dwFlags参数外。参数dwFlags用来指示套接字是用来作为发送者还是接收者还是两者兼具。在此函数中,只有多址广播套接字可以用来作为输入参数s。如果此多址广播套接字处于非阻塞模式,返回的套接字描述符只有在接收到相应的FD_CONNECT指示后才能使用。参数name指示套接字将加入的多址广播的地址。 另外,在上面的API中没有提到如何离开一个多址广播组。唯一与协议无关的API是关闭套接字,即使用标准的closesocket()函数,它可以用来离开多址广播组。 Windows 95不支持IP multicast,如果要在Windows 95 上开发IP multicast程序,需要下载WinSock 2的软件开发包WS295SDK,这可以通过Internet免费下载。相关站点地址为:http://www.microsoft.com/win32dev/netwrk/winsock2/ws295sdk.html。Windows 98支持IP multicast,如果使用Windows 98,则不必运行其中的ws2setup.exe。 下面给出一个简单的例子说明WinSock 2多址广播程序的设计。#include <winsock2.h>#include <stdio.h>#define BUFSIZE 1024#define MAXADDRSTR 16#define LOOPCOUNT 100/* 检查系统中是否安装了合适版本的WinSock DLL。 */ int CheckWinsockVersion(VOID) {WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested = MAKEWORD(2, 2); // 异步I/O和多址广播只有在WinSock 2.0以上版本才支持err = WSAStartup(wVersionRequested, &wsaData); if (err==0) { if ((LOBYTE(wsaData.wVersion)==2) && (HIBYTE(wsaData.wVersion)==2)) // 确认WinSock DLL支持2.0 return 0; /* WinSock DLL可接受,成功返回 */ WSACleanup(); err = WSAVERNOTSUPPORTED; /* 不支持,失败返回 */ } /* Tell the user that we couldn't find a usable WinSock DLL.*/ printf("WinSock DLL does not support requested API version.\n"); return err;} int main() { int nRet, i; int nIP_TTL = 1; // 在本子网中传播。如果要跨路由器,则路由器必须支持IGMP协议 BOOL bFlag; DWORD dFlag, cbRet; int iLen = MAXADDRSTR; char strDestMulti[MAXADDRSTR] = "224.1.1.1"; // 多址广播地址范围从224到239。 SOCKADDR_IN stSrcAddr, stDestAddr; SOCKET hSock, hNewSock; u_short nDestPort = 6666; WSABUF stWSABuf; char achInBuf [BUFSIZE]; char achOutBuf[] = "Message number: "; nRet = CheckWinsockVersion(); // 检查WinSock版本号 if (nRet) { printf ("WSAStartup failed: %d\r\n", nRet); exit (1); } // 将字符串地址转换为套接字地址 nRet = WSAStringToAddress ( strDestMulti, /* address string */ AF_INET, /* address family */ NULL, /* protocol info structure */ (LPSOCKADDR)&stDestAddr, /* socket address string */ &iLen); /* length of socket structure */ if (nRet) { printf ("WSAStringToAddress(%s) failed, Err: %d\n", strDestMulti, WSAGetLastError()); exit(1); } // 创建一个多址广播套接字hSock = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, (LPWSAPROTOCOL_INFO)NULL, 0, WSA_FLAG_OVERLAPPED | WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF); if (hSock == INVALID_SOCKET) { printf ("WSASocket() failed, Err: %d\n", WSAGetLastError()); exit (1); } bFlag = TRUE; // 设置套接字为可重用端口地址 nRet = setsockopt(hSock, /* socket */ SOL_SOCKET, /* socket level */ SO_REUSEADDR, /* socket option */ (char *)&bFlag, /* option value */ sizeof (bFlag)); /* size of value */ if (nRet == SOCKET_ERROR) printf("setsockopt() SO_REUSEADDR failed, Err: %d\n", WSAGetLastError()); // 将套接字绑扎到用户指定端口及默认的接口 stSrcAddr.sin_family = PF_INET; stSrcAddr.sin_port = htons (nDestPort); stSrcAddr.sin_addr.s_addr = INADDR_ANY; nRet = bind (hSock, (struct sockaddr FAR *)&stSrcAddr, sizeof(struct sockaddr)); if (nRet == SOCKET_ERROR) printf ("bind failed, Err: %d\n", WSAGetLastError()); // 设置多址广播数据报传播范围(TTL) nRet = WSAIoctl (hSock, /* socket */ SIO_MULTICAST_SCOPE, /* IP Time-To-Live */ &nIP_TTL, /* input */ sizeof (nIP_TTL), /* size */ NULL, /* output */ 0, /* size */ &cbRet, /* bytes returned */ NULL, /* overlapped */ NULL); /* completion routine */ if (nRet) printf ("WSAIoctl(SIO_MULTICAST_SCOPE) failed, Err: %d\n", WSAGetLastError()); // 允许内部回送(LOOPBACK)。Windows 95不支持改选项 bFlag = TRUE; nRet = WSAIoctl (hSock, /* socket */ SIO_MULTIPOINT_LOOPBACK, /* LoopBack on or off */ &bFlag, /* input */ sizeof (bFlag), /* size */ NULL, /* output */ 0, /* size */ &cbRet, /* bytes returned */ NULL, /* overlapped */ NULL); /* completion routine */ if (nRet) printf("WSAIoctl(SIO_MULTIPOINT_LOOPBACK) failed, Err: %d\n", WSAGetLastError()); stDestAddr.sin_family = PF_INET; nRet = WSAHtons( hSock, /* socket */ nDestPort, /* host order value */ &(stDestAddr.sin_port)); /* network order value */ if (nRet == SOCKET_ERROR) printf("WSAHtons() failed, Err: %d\n", WSAGetLastError()); // 加入到指定多址广播组,指定为既作发送者又作接收者 // 在IP multicast中,返回的套接字描述符和输入的套接字描述符相同。 hNewSock = WSAJoinLeaf (hSock, /* socket */ (PSOCKADDR)&stDestAddr, /* multicast address */ sizeof (stDestAddr), /* length of addr struct */ NULL, /* caller data buffer */ NULL, /* callee data buffer */ NULL, /* socket QOS setting */ NULL, /* socket group QOS */ JL_BOTH); /* do both: send *and* receive */ if (hNewSock == INVALID_SOCKET) printf ("WSAJoinLeaf() failed, Err: %d\n", WSAGetLastError()); // 在循环中发送/接收数据。测试时可以改为无限循环 for (i=0;i<LOOPCOUNT;i++) { static iCounter = 1; stWSABuf.buf = achOutBuf; stWSABuf.len = lstrlen(achOutBuf); cbRet = 0; itoa(iCounter++, &achOutBuf[16], 10); nRet = WSASendTo (hSock, /* socket */ &stWSABuf, /* output buffer structure */ 1, /* buffer count */ &cbRet, /* number of bytes sent */ 0, /* flags */ (struct sockaddr*)&stDestAddr, /* destination address */ sizeof(struct sockaddr), /* size of addr structure */ NULL, /* overlapped structure */ NULL); /* overlapped callback function */ if (nRet == SOCKET_ERROR) printf("WSASendTo() failed, Err: %d\n", WSAGetLastError()); stWSABuf.buf = achInBuf; stWSABuf.len = BUFSIZE; cbRet = 0; iLen = sizeof (stSrcAddr); dFlag = 0; nRet = WSARecvFrom (hSock, /* socket */ &stWSABuf, /* input buffer structure */ 1, /* buffer count */ &cbRet, /* number of bytes recv'd */ &dFlag, /* flags */ (struct sockaddr *)&stSrcAddr /* source address */ &iLen, /* size of addr structure */ NULL, /* overlapped structure */ NULL); /* overlapped callback function */ if (nRet == SOCKET_ERROR) printf("WSARecvFrom() failed, Err:%d\n", WSAGetLastError()); else { u_short nPort = 0; char achAddr[MAXADDRSTR+3] = {0}; // 如果长度太小则返回Windows错误122 iLen = MAXADDRSTR+3; nRet = WSAAddressToString( // 将地址转换为字符串 (struct sockaddr *)&stSrcAddr, /* source address */ sizeof(stSrcAddr), /* size of addr struct */ NULL, /* protocol info */ achAddr, /* address string */ &iLen); /* addr string buf len */ if (nRet == SOCKET_ERROR) printf("WSAAddressToString() failed, Err: %d\n", WSAGetLastError()); nRet = WSANtohs(hSock, /* socket */ stSrcAddr.sin_port, /* host order value */ &nPort); /* network order value */ if (nRet == SOCKET_ERROR) printf("WSANtohs() failed, Err: %d\n", WSAGetLastError()); printf ("WSARecvFrom() received %d bytes from %s, port %d :%s\r\n", cbRet, achAddr[0] ? achAddr : "??", nPort, achInBuf); } } /* end for(;;) */ closesocket(hNewSock); closesocket(hSock); // 卸载WinSock DLL WSACleanup(); return (0);} /* end main() */ 谈谈我的看法:组播是一种一点对多点的传输方式.其他的传输方式还有广播和单播.如果在以太网里面用广播的方式传送数据,那么所有的用户不管你愿不愿意,都要接收,他会占用每个用户的带宽的.如果是组播的方式,那么用户可以自己决定是否接收,如果不接收,他就不加入这个多播组,也就不占用他的带宽.对于发送组播的服务器来说,给一个人发送和给1000个人发送所占用服务器的带宽是一样的.还有一种就是单播,就是点对点的传送,只能给一个客户传送数据.如果你想给1000个人传送数据,你就需要建立1000个连接,这样对于发送数据的服务器来说,给1000个人传送要占用的带宽是给一个人传送的带宽的1000倍,一般应用于真正的视频点播.所以,应用组播方式是一种非常节省带宽的传送方式,而且用户可以决定是否接收.同样组播地址也有特别的规范,一般在224.0.0.0---239.255.255.255范围内属于组播地址.具体的规范可以看"tcp/ip协议祥解"这本书. http://expert.csdn.net/Expert/topic/1299/1299165.xml?temp=.7460596 MFC我想做个局域网的语音聊天程序 模拟回车按键自动关闭消息框的弹出窗口 移动开发~~醒目,为什么西门子C35手机无法被正常访问。 在vc编译器中,为何调试时观测的值与最后屏幕输出的值不一样呢? 这么晚了,想请前面几位朋友来喝点可乐,呵呵 如何对接收和发送进行时间控制和客户端与服务器多线程连接, CADORecordBinding如何才能继承?我在Class Wizard中的Base Class看不见它? OpenGL中物体旋转的问题 BoundsChecker 救命!! 串口通信的问题!!!100分!!! 大家都来看看吧……
清华大学计算中心 蒋东兴 多址广播(multicast,也译作多点传送或组播)是一种一对多的传输方式,传输发起者通过一次传输就将信息传送到一组接收者,与单点传送(unicast)和广播(broadcast)相对应。多址广播使用最广泛的是IP multicast,它标准IP网络层协议的扩展,由Steve Deering定义的Host Extensions for IP Multicasting(RFC 1112)奠定基础。IP Multicasting的定义为:到一个“主机组”的IP数据报的传送,主机组是由零个或多个用同一IP目的地址标识的主机集合。Multicast数据报被传递到其目的主机组的所有成员,并且同常规单点传送的IP数据报一样可靠。主机组的成员是动态的,也就是说,主机可以在任何时间加入或离开主机组。主机组中成员在位置上和数量上都没有限制,一个主机可以同时是一个以上主机组的成员。 在Windows Sockets 1.1中没有定义多址广播,因此,绝大多数Windows Sockets 1.1实现不支持多址广播。Windows Sockets 2为支持IP multicast而定义了一组新的与协议无关的多址广播应用程序接口,归纳起来可以用表1表示。表1 WinSock 2的多址广播APIWSAEnumProtocols()
检测多址广播支持
WSASocket()
指定多址广播类型
WSAJoinLeaf()
加入一个多址广播组并指定角色(发送者和/或接收者)
WSAIoctl() SIO_MULTICAST_SCOPE
设置IP生存时间
WSAIoctl() SIO_MULTIPOINT_LOOPBACK
禁止内部回送(loopback)
函数WSAEnumProtocols()返回当前系统中安装的协议的详细描述,这些信息存放在一个协议信息结构(WSAPROTOCOL_INFO)的数组中。在其中的域dwServiceFlags1中的一些标识指示此服务由IP/UDP协议提供,并且位标识XP1_SUPPORT_MULTIPOINT指示该服务支持IP multicast。 为了适用不同的多址广播模式,WinSock 2定义了数据平面(data plane)和控制平面(control plane)两个概念,每一个平面都可以是“有根(rooted)”的或“无根(non-rooted)”的。关于这些概念的详细解释,参见WinSock 2规范附录B。IP multicast是一种无根的数据平面和控制平面。在使用WSASocket()函数请求一个多址广播套接字时需要指定这些角色。这通过在WSASocket()函数的参数dwFlags中使用四个标志位来实现:· WSA_FLAG_MULTIPOINT_C_ROOT,用来创建一个作为c_root节点的套接字,并且只有在相应的WSAPROTOCOL_INFO入口中指示了使用rooted控制平面时才允许。· WSA_FLAG_MULTIPOINT_C_LEAF,用来创建一个作为c_leaf节点的套接字,并且只有在相应的WSAPROTOCOL_INFO入口中指示了XP1_SUPPORT_MULTIPOINT时才允许。· WSA_FLAG_MULTIPOINT_D_ROOT,用来创建一个作为d_root节点的套接字,并且只有在相应的WSAPROTOCOL_INFO入口中指示了使用rooted数据平面时才允许。· 用来创建一个作为d_leaf节点的套接字,并且只有在相应的WSAPROTOCOL_INFO入口中指示了XP1_SUPPORT_MULTIPOINT时才允许。 注意,在IP multicast中,只有标志WSA_FLAG_MULTIPOINT_C_LEAF 和WSA_FLAG_MULTIPOINT_D_LEAF能用来作为WSASocket()函数的dwFlags参数。 WSAIoctl()函数的SIO_MULTIPOINT_LOOP命令码用来设置是否允许内部回送。当d_leaf套接字用于non-rooted数据平面时,它通常是希望能够控制发送出去的通信流量是否能够在同一个套接字上也被接收。WSAIoctl()函数的SIO_MULTIPOINT_LOOP命令码用来允许或禁止多址广播的通信流量的内部回送。 WSAIoctl()函数的SIO_MULTICAST_SCOPE命令码用来设置多址广播范围。当使用多址广播时,常常需要指定多址广播传播的范围。范围由包括的路由网段来定义。范围为0指示多址广播不传播信息到“网线”上,但是可以在本地主机的多个套接字间传播。范围为1(默认值)指示将传播信息到“网线”上,但是不跨越路由器。更高的范围值决定了可以跨越的路由器数量。注意这相当于IP multicast的生存时间(time-to-live,TTL)。 WSAJoinLeaf()函数用来加入一个叶子节点到多址广播会话,其函数原型为:SOCKET WSAAPI WSAJoinLeaf ( SOCKET s, const struct sockaddr FAR * name, int namelen, LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS, DWORD dwFlags );WSAJoinLeaf()具有与WSAConnect()相同的参数和语法,除了它还返回一个套接字描述符(这和函数WSAAccept()一样)及多了一个dwFlags参数外。参数dwFlags用来指示套接字是用来作为发送者还是接收者还是两者兼具。在此函数中,只有多址广播套接字可以用来作为输入参数s。如果此多址广播套接字处于非阻塞模式,返回的套接字描述符只有在接收到相应的FD_CONNECT指示后才能使用。参数name指示套接字将加入的多址广播的地址。 另外,在上面的API中没有提到如何离开一个多址广播组。唯一与协议无关的API是关闭套接字,即使用标准的closesocket()函数,它可以用来离开多址广播组。 Windows 95不支持IP multicast,如果要在Windows 95 上开发IP multicast程序,需要下载WinSock 2的软件开发包WS295SDK,这可以通过Internet免费下载。相关站点地址为:http://www.microsoft.com/win32dev/netwrk/winsock2/ws295sdk.html。Windows 98支持IP multicast,如果使用Windows 98,则不必运行其中的ws2setup.exe。
#include <winsock2.h>
#include <stdio.h>#define BUFSIZE 1024
#define MAXADDRSTR 16
#define LOOPCOUNT 100
/* 检查系统中是否安装了合适版本的WinSock DLL。 */ int CheckWinsockVersion(VOID) {
WORD wVersionRequested;
WSADATA wsaData;
int err;wVersionRequested = MAKEWORD(2, 2); // 异步I/O和多址广播只有在WinSock 2.0以上版本才支持
err = WSAStartup(wVersionRequested, &wsaData); if (err==0) { if ((LOBYTE(wsaData.wVersion)==2) && (HIBYTE(wsaData.wVersion)==2)) // 确认WinSock DLL支持2.0 return 0; /* WinSock DLL可接受,成功返回 */ WSACleanup(); err = WSAVERNOTSUPPORTED; /* 不支持,失败返回 */ } /* Tell the user that we couldn't find a usable WinSock DLL.*/ printf("WinSock DLL does not support requested API version.\n"); return err;} int main() { int nRet, i;
int nIP_TTL = 1; // 在本子网中传播。如果要跨路由器,则路由器必须支持IGMP协议
BOOL bFlag;
DWORD dFlag, cbRet;
int iLen = MAXADDRSTR;
char strDestMulti[MAXADDRSTR] = "224.1.1.1"; // 多址广播地址范围从224到239。 SOCKADDR_IN stSrcAddr, stDestAddr;
SOCKET hSock, hNewSock;
u_short nDestPort = 6666;
WSABUF stWSABuf;
char achInBuf [BUFSIZE];
char achOutBuf[] = "Message number: "; nRet = CheckWinsockVersion(); // 检查WinSock版本号 if (nRet) { printf ("WSAStartup failed: %d\r\n", nRet); exit (1); } // 将字符串地址转换为套接字地址 nRet = WSAStringToAddress ( strDestMulti, /* address string */ AF_INET, /* address family */ NULL, /* protocol info structure */ (LPSOCKADDR)&stDestAddr, /* socket address string */ &iLen); /* length of socket structure */ if (nRet) { printf ("WSAStringToAddress(%s) failed, Err: %d\n", strDestMulti, WSAGetLastError()); exit(1); } // 创建一个多址广播套接字hSock = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, (LPWSAPROTOCOL_INFO)NULL, 0, WSA_FLAG_OVERLAPPED | WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF); if (hSock == INVALID_SOCKET) {
printf ("WSASocket() failed, Err: %d\n", WSAGetLastError());
exit (1); } bFlag = TRUE; // 设置套接字为可重用端口地址
nRet = setsockopt(hSock, /* socket */
SOL_SOCKET, /* socket level */
SO_REUSEADDR, /* socket option */
(char *)&bFlag, /* option value */
sizeof (bFlag)); /* size of value */
if (nRet == SOCKET_ERROR) printf("setsockopt() SO_REUSEADDR failed, Err: %d\n", WSAGetLastError()); // 将套接字绑扎到用户指定端口及默认的接口 stSrcAddr.sin_family = PF_INET; stSrcAddr.sin_port = htons (nDestPort); stSrcAddr.sin_addr.s_addr = INADDR_ANY; nRet = bind (hSock, (struct sockaddr FAR *)&stSrcAddr, sizeof(struct sockaddr)); if (nRet == SOCKET_ERROR) printf ("bind failed, Err: %d\n", WSAGetLastError()); // 设置多址广播数据报传播范围(TTL) nRet = WSAIoctl (hSock, /* socket */ SIO_MULTICAST_SCOPE, /* IP Time-To-Live */ &nIP_TTL, /* input */ sizeof (nIP_TTL), /* size */ NULL, /* output */ 0, /* size */ &cbRet, /* bytes returned */ NULL, /* overlapped */ NULL); /* completion routine */ if (nRet) printf ("WSAIoctl(SIO_MULTICAST_SCOPE) failed, Err: %d\n", WSAGetLastError()); // 允许内部回送(LOOPBACK)。Windows 95不支持改选项 bFlag = TRUE; nRet = WSAIoctl (hSock, /* socket */ SIO_MULTIPOINT_LOOPBACK, /* LoopBack on or off */ &bFlag, /* input */ sizeof (bFlag), /* size */ NULL, /* output */ 0, /* size */ &cbRet, /* bytes returned */ NULL, /* overlapped */ NULL); /* completion routine */
stDestAddr.sin_family = PF_INET;
nRet = WSAHtons( hSock, /* socket */
nDestPort, /* host order value */
&(stDestAddr.sin_port)); /* network order value */ if (nRet == SOCKET_ERROR) printf("WSAHtons() failed, Err: %d\n", WSAGetLastError()); // 加入到指定多址广播组,指定为既作发送者又作接收者 // 在IP multicast中,返回的套接字描述符和输入的套接字描述符相同。 hNewSock = WSAJoinLeaf (hSock, /* socket */ (PSOCKADDR)&stDestAddr, /* multicast address */ sizeof (stDestAddr), /* length of addr struct */ NULL, /* caller data buffer */ NULL, /* callee data buffer */ NULL, /* socket QOS setting */ NULL, /* socket group QOS */ JL_BOTH); /* do both: send *and* receive */ if (hNewSock == INVALID_SOCKET) printf ("WSAJoinLeaf() failed, Err: %d\n", WSAGetLastError()); // 在循环中发送/接收数据。测试时可以改为无限循环 for (i=0;i<LOOPCOUNT;i++) { static iCounter = 1; stWSABuf.buf = achOutBuf; stWSABuf.len = lstrlen(achOutBuf); cbRet = 0; itoa(iCounter++, &achOutBuf[16], 10); nRet = WSASendTo (hSock, /* socket */ &stWSABuf, /* output buffer structure */ 1, /* buffer count */ &cbRet, /* number of bytes sent */ 0, /* flags */ (struct sockaddr*)&stDestAddr, /* destination address */ sizeof(struct sockaddr), /* size of addr structure */ NULL, /* overlapped structure */ NULL); /* overlapped callback function */ if (nRet == SOCKET_ERROR) printf("WSASendTo() failed, Err: %d\n", WSAGetLastError()); stWSABuf.buf = achInBuf; stWSABuf.len = BUFSIZE; cbRet = 0; iLen = sizeof (stSrcAddr); dFlag = 0; nRet = WSARecvFrom (hSock, /* socket */ &stWSABuf, /* input buffer structure */ 1, /* buffer count */ &cbRet, /* number of bytes recv'd */ &dFlag, /* flags */ (struct sockaddr *)&stSrcAddr /* source address */ &iLen, /* size of addr structure */ NULL, /* overlapped structure */ NULL); /* overlapped callback function */ if (nRet == SOCKET_ERROR) printf("WSARecvFrom() failed, Err:%d\n", WSAGetLastError()); else { u_short nPort = 0; char achAddr[MAXADDRSTR+3] = {0}; // 如果长度太小则返回Windows错误122 iLen = MAXADDRSTR+3; nRet = WSAAddressToString( // 将地址转换为字符串 (struct sockaddr *)&stSrcAddr, /* source address */ sizeof(stSrcAddr), /* size of addr struct */ NULL, /* protocol info */ achAddr, /* address string */ &iLen); /* addr string buf len */ if (nRet == SOCKET_ERROR) printf("WSAAddressToString() failed, Err: %d\n", WSAGetLastError()); nRet = WSANtohs(hSock, /* socket */ stSrcAddr.sin_port, /* host order value */ &nPort); /* network order value */ if (nRet == SOCKET_ERROR) printf("WSANtohs() failed, Err: %d\n", WSAGetLastError());
printf ("WSARecvFrom() received %d bytes from %s, port %d :%s\r\n",
cbRet, achAddr[0] ? achAddr : "??", nPort, achInBuf);
}
} /* end for(;;) */
closesocket(hNewSock);
closesocket(hSock);
// 卸载WinSock DLL
WSACleanup();
return (0);
} /* end main() */
组播是一种一点对多点的传输方式.其他的传输方式还有广播和单播.
如果在以太网里面用广播的方式传送数据,那么所有的用户不管你愿不愿意,都要接收,他会占用每个用户的带宽的.如果是组播的方式,那么用户可以自己决定是否接收,如果不接收,他就不加入这个多播组,也就不占用他的带宽.对于发送组播的服务器来说,给一个人发送和给1000个人发送所占用服务器的带宽是一样的.还有一种就是单播,就是点对点的传送,只能给一个客户传送数据.如果你想给1000个人传送数据,你就需要建立1000个连接,这样对于发送数据的服务器来说,给1000个人传送要占用的带宽是给一个人传送的带宽的1000倍,一般应用于真正的视频点播.所以,应用组播方式是一种非常节省带宽的传送方式,而且用户可以决定是否接收.同样组播地址也有特别的规范,一般在224.0.0.0---239.255.255.255范围内属于组播地址.
具体的规范可以看"tcp/ip协议祥解"这本书.