Visual C++实现局域网IP多播效果

Visual C++实现局域网IP多播效果,第1张

在局域网中 管理员常常需要将某条信息发送给一组用户 如果使用一对一的发送方法 虽然是可行的 但是过于麻烦 也常会出现漏发 错发 为了更有效的解决这种组通信问题 出现了一种多播技术(也常称为组播通信) 它是基于IP层的通信技术 为了帮助读者理解 下面将简要的介绍一下多播的概念  众所周知 普通IP通信是在一个发送者和一个接收者之间进行的 我们常把它称为点对点的通信 但对于有些应用 这种点对点的通信模式不能有效地满足实际应用的需求 例如 一个数字电话弊肢指会议系统由多个会场组成 当在其中一个会场的参会人发言时 要求其它会场都能即时的得到此发言的内容 这是一个典型的一对多的通信应用 通常把这种一对多的通信称为多播通信 采用多播通信技术 不仅可以实现一个发送者和多个接收者之间进行通信的功能 而且可以有效减轻网络通信的负担 避免资源的无谓浪费  广播也是一种实现一对多数据通信的模式 但广播与多播在实现方式上有所不同 广播是将数据从一个工作站发出 局域网内的其他所有工作站都能收到它 这一特征适用于无连接协议 因为LAN上的所有机器都可获得并处理广播消息 使用广播消息的不利之处是每台机器都必须对该消息进行处理 多播通信则不同 数据从一个工作站发出后 如果在其它LAN上的机器上面运行的进程表示对这些数据 有兴趣 多播数据才会制给它们  本实例由Sender和Receiver两个程序组成 Sender用户从控制台上输入多播发送数据 Receiver端都要求加入同一个多播组 完成接收Sender发送的多播数据

一 实现方法

协议支持

并不是所有的协议都支持多播通信 对Win 平台而言 仅两种可从WinSock内访问的协议(IP/ATM)才提供了对多播通信的支持 因通常通信应用都建立在TCP/IP协议之上的 所以本文只针对IP协议来探讨多播通信技术

支持多播通信的平台包括Windows CE Windows Windows Windows NT Windows 和WindowsXP 自 版开始 Windows CE才开始实现对IP多播的支持 本文实例建立在WindowsXP专业版平台上

多播地址

IP采用D类地址来支持多播 每个D类地址代表一组主机 共有 位可用来标识小组 所以可以同时有多达 亿个小组 当一个进程向一个D类地址发送分组时 会尽最大的努力将它送给小组的所有成员 但不能保证全部送到 有些成员可能收不到这个分组 举个例子来说 假定五个节点都想通过I P多播 实现彼此间的通信 它们便可加入同一个组地址 全部加入之后 由一个节点发出的任何数据均会一模一样地复制一份 发给组内的每个成员 甚至包括始发数据的那个节点 D类I P地址范围在 到 之间 它分为两类 永久地址和临时地址 永久地址是为特殊用途而保留的 比如 根本没有使用(也不能使用) 代表子网内的所有系统(租配主机) 而 代表子网内的所有路由器 在RFC 文件中 提供了所有保留地址的一个详细清单 该文件是为特殊用途保留的所有资源的一个列表 大家可以找来作为参考 Internet分配数字专家组 (I A N A)负责著这个列表的维护 在表 中 我们总结了目前标定为 保留 的一些地址 临时组地址在使用前必须先创建 一个进程可以要求其主机加入特定的组 它也能要求其主机脱离该组 当主机上的最后一个进程脱离某个组后 该组地址就不再在这台主机中出现 每个主机都要记录它的进程当前属于哪个组 表 部分永久地址说明

地 址 说 明 基本地址(保留) 子网上的所有系统 子网上的所有路由器 子网上所有OSPF路由器 子网上所有指定的OSPF路由器 RIP第 版本组地址 网络时间协议 WINS服务器组地址

多播路由器

多播由特殊的多播路由器来实现 多播路由器同时也可以是普通路由器 各个多播路由器每分钟发送一个硬件多播信息给子网上的主机(目的地址为 ) 要求它们报告其进程当前所属的是哪一组 各饥羡主机将它感兴趣的D类地址返回 这些询问和响应分组使用IGMP(Internet group management protocol) 它大致类似于ICMP 它只有两种分组 询问和响应 都有一个简单的固定格式 其中有效载荷字段的第一个字段是一些控制信息 第二字段是一个D类地址 在RFC 中有详细说明

多播路由器的选择是通过生成树实现的 每个多播路由器采用修改过的距离矢量协议和其邻居交换信息 以便向每个路由器为每一组构造一个覆盖所有组员的生成树 在修剪生成树及删除无关路由器和网络时 用到了很多优化方法

库支持

WinSock提供了实现多播通信的API函数调用 针对IP多播 WinSock提供了两种不同的实现方法 具体取决于使用的是哪个版本的WinSock 第一种方法是WinSock 提供的 要求通过套接字选项来加入一个组 另一种方法是WinSock 提供的 它是引入一个新函数 专门负责多播组的加入 这个函数便是WSAJoinLeaf 它是基层协议是无关的 本文将通过一个多播通信的实例的实现过程 来讲叙多播实现的主要步骤 因为Window 以后版本都安装了Winsock 以上版本 所以本文实例在WinSock 平台上开发的 但在其中对WinSock 实现不同的地方加以说明

二 编程步骤

启动Visual C++ 创建一个控制台项目工程MultiCase 在此项目工程中添加Sender和Receiver两个项目 Receiver项目实现步骤

( )创建一个SOCK_DGRAM类型的Socket

( )将此Socket绑定到本地的一个端口上 为了接收服务器端发送的多播数据

( )加入多播组

①WinSock 中引入一个WSAJoinLeaf 此函数原型如下

SOCKET WSAJoinLeaf( SOCKET s const struct sockaddr FAR *name int namelen LPWSABUF lpCallerData LPWSABUF lpCalleeData LPQOS lpSQOS LPQOS lpGQOS DWORD dwFlags )

其中 第一个参数s代表一个套接字句柄 是自WSASocket返回的 传递进来的这个套接字必须使用恰当的多播标志进行创建 否则的话WSAJoinLeaf就会失败 并返回错误WSAEINVAL 第二个参数是SOCKADDR(套接字地址)结构 具体内容由当前采用的协议决定 对于IP协议来说 这个地址指定的是主机打算加入的那个多播组 第三个参数namelen(名字长度)是用于指定name参数的长度 以字节为单位 第四个参数lpCallerData(呼叫者数据)的作用是在会话建立之后 将一个数据缓冲区传输给自己通信的对方 第五个参数lpCalleeData(被叫者数据)用于初始化一个缓冲区 在会话建好之后

接收来自对方的数据 注意在当前的Windows平台上 lpCallerData和lpCalleeData这两个参数并未真正实现 所以均应设为NULL LpSQOS和lpGQOS这两个参数是有关Qos(服务质量)的设置 通常也设为NULL 有关Qos内容请参阅MSDN或有关书籍 最后一个参数dwFlags指出该主机是发送数据 接收数据或收发兼并 该参数可选值分别是 JL_SENDER_ONLY JL_RECEIVER_ONLY或者JL_BOTH

②在WinSock 平台上加入多播组需要调用setsockopt函数 同时设置IP_ADD_MEMBERSHIP选项 指定想加入的那个组的地址结构 具体实现代码将在下面代码注释列出

( )接收多播数据

Sender实现步骤

( )创建一个SOCK_DGRAM类型的Socket ( )加入多播组 ( )发送多播数据

编译两个项目 在局域网中按如下步骤测试

( )将Sender exe拷贝到发送多播数据的pc上 ( )将Receiver exe拷贝到多个要求接收多播数据的pc上 ( )各自运行相应的程序   ( )在Sender PC上输入多播数据后 你就可以在Receiver PC上看到输入的多播数据

三 程序代码

Receiver c程序代码

#include <winsock h>#include <ws tcpip h>#include <stdio h>#include <stdlib h>#define MCASTADDR //本例使用的多播组地址 #define MCASTPORT //绑定的本地端口号 #define BUFSIZE //接收数据缓冲大小 int main( int argc char ** argv){ WSADATA wsd struct sockaddr_in local remote from SOCKET sock sockM TCHAR recvbuf[BUFSIZE] /*struct ip_mreq mcast// Winsock */

int len = sizeof( struct sockaddr_in) int ret //初始化WinSock  if( WSAStartup( MAKEWORD( ) &wsd) != ) {printf( WSAStartup() failed\n )return  } /* 创建一个SOCK_DGRAM类型的SOCKET 其中 WSA_FLAG_MULTIPOINT_C_LEAF表示IP多播在控制面层上属于 无根 类型 WSA_FLAG_MULTIPOINT_D_LEAF表示IP多播在数据面层上属于 无根 有关控制面层和 数据面层有关概念请参阅MSDN说明  */ if((sock=WSASocket(AF_INET SOCK_DGRAM NULL WSA_FLAG_MULTIPOINT_C_LEAF|WSA_FLAG_MULTIPOINT_D_LEAF|WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) {printf( socket failed with:%d\n WSAGetLastError())WSACleanup()return  } //将sock绑定到本机某端口上  local sin_family = AF_INET local sin_port = htons(MCASTPORT) local sin_addr s_addr = INADDR_ANY if( bind(sock (struct sockaddr*)&local sizeof(local)) == SOCKET_ERROR ) {printf( bind failed with:%d \n WSAGetLastError())closesocket(sock)WSACleanup()return  } //加入多播组 remote sin_family = AF_INET remote sin_port = htons(MCASTPORT) remote sin_addr s_addr = inet_addr( MCASTADDR ) /* Winsock */ /* mcast imr_multiaddr s_addr = inet_addr(MCASTADDR) mcast imr_interface s_addr = INADDR_ANY if( setsockopt(sockM IPPROTO_IP IP_ADD_MEMBERSHIP

(char*)&mcast sizeof(mcast)) == SOCKET_ERROR) {printf( setsockopt(IP_ADD_MEMBERSHIP) failed:%d\n WSAGetLastError())closesocket(sockM)WSACleanup()return  } */ /* Winsock */ if(( sockM = WSAJoinLeaf(sock (SOCKADDR*)&remote sizeof(remote)

NULL NULL NULL NULL JL_BOTH)) == INVALID_SOCKET) {printf( WSAJoinLeaf() failed:%d\n WSAGetLastError())closesocket(sock)WSACleanup()return  } //接收多播数据 当接收到的数据为 QUIT 时退出  while( ) {if(( ret = recvfrom(sock recvbuf BUFSIZE

(struct sockaddr*)&from &len)) == SOCKET_ERROR){ printf( recvfrom failed with:%d\n WSAGetLastError()) closesocket(sockM) closesocket(sock) WSACleanup() return }if( strcmp(recvbuf QUIT ) == ) breakelse { recvbuf[ret] = \  printf( RECV: %s FROM <%s>\n recvbuf inet_ntoa(from sin_addr))} }

closesocket(sockM) closesocket(sock) WSACleanup() return }

Sender c程序

代码

#include <winsock h>#include <ws tcpip h>#include <stdio h>#include <stdlib h>#define MCASTADDR //本例使用的多播组地址 #define MCASTPORT //本地端口号 #define BUFSIZE //发送数据缓冲大小 int main( int argc char ** argv){ WSADATA wsd struct sockaddr_in remote SOCKET sock sockM TCHAR sendbuf[BUFSIZE] int len = sizeof( struct sockaddr_in) //初始化WinSock  if( WSAStartup( MAKEWORD( ) &wsd) != ) {printf( WSAStartup() failed\n )return  } if((sock=WSASocket(AF_INET SOCK_DGRAM NULL WSA_FLAG_MULTIPOINT_C_LEAF|WSA_FLAG_MULTIPOINT_D_LEAF|WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) {printf( socket failed with:%d\n WSAGetLastError())WSACleanup()return  } //加入多播组 remote sin_family = AF_INET remote sin_port = htons(MCASTPORT) remote sin_addr s_addr = inet_addr( MCASTADDR ) if(( sockM = WSAJoinLeaf(sock (SOCKADDR*)&remote sizeof(remote) NULL NULL NULL NULL JL_BOTH)) == INVALID_SOCKET) {printf( WSAJoinLeaf() failed:%d\n WSAGetLastError())closesocket(sock)WSACleanup()return  }

//发送多播数据 当用户在控制台输入 QUIT 时退出  while( ) {printf( SEND : )scanf( %s sendbuf)if( sendto(sockM (char*)sendbuf strlen(sendbuf)

(struct sockaddr*)&remote sizeof(remote))==SOCKET_ERROR){ printf( sendto failed with: %d\n WSAGetLastError()) closesocket(sockM) closesocket(sock) WSACleanup() return }if(strcmp(sendbuf QUIT )== ) breakSleep( ) }

closesocket(sockM) closesocket(sock) WSACleanup() return }

四 小结

lishixinzhi/Article/program/net/201311/11975

原文地址: http://www.cnblogs.com/gaoxing/archive/2012/02/19/2358484.html

除地址类别外,还可根据传输的消息特征将IP地址分为单播、广播或多播。主机使用IP地址进行一对一(单播)、一对多(多播)或一对所有(广播)的通信。

单播地址是IP网络中最常见的。包含单播目标地址的分组发送给特定主机,一个这样的例子是,IP地址为192.168.1.5(源地址)的主机向IP地址为192.168.1.200(目标地址)的服务器请求网页,如图5.8所示。

要发送和接收单播分组,IP分组报头中必须有一个目标IP地址,而以太网帧报头中必须有相应的目标MAC地址。IP地址和MAC地址一起将数据传输到特定的目盯铅洞标主机。

如果目标IP地址属于另一个网络,则在帧中使用的目标MAC地址将为与源IP地址位于同一个网络中的路由器接口的MAC地址。

广播分组的目标IP地址的主机部分全为1,这意味着本地网络(广播域)中的所有主机都将接收并查看该分组。诸如ARP和DHCP等很多网络协议都使用广播。

C类网络192.168.1.0的默认子网掩码为255.255.255.0,其广播地址为192.168.1.255,其主机部分为十进制激灶数255或二进制数11111111(全为1);

B类网络172.16.0.0的默认子网掩码为255.255.0.0,其广播地址为172.16.255.255;

A类网络10.0.0.0的默认子网掩码为255.0.0.0,其广播地址为10.255.255.255。

在以太网帧中,必须包含与广播IP地址对应的广播MAC地址。在以太网中,广播MAC地址长48位,其十六进制表示为FF-FF-FF-FF-FF-FF。图5.9所示的是一个广播IP分组。

多播地址让源设备能够将分组发送给一组设备。属于多播组的设备将被分配一个多播组IP地址,多播地址范围为224.0.0.0~239.255.255.255。由于多播地址表示一组设备(有时被称为主机组),因此只能用作分组的目标地址。源地址总是为单播地址。

远程游戏就是一个使用多播地址的例子,很多玩家通过远程连接玩同一个游戏;另一例子是通过视频会议进行远程教学,其中很多学生连接到同一个教室。还有一个例子是硬盘映像应用程序,这种程序用于同时恢复众多硬盘的内容。

同单播地址和广播地址一样,多播IP地址也需要相应的多播MAC地址在本地网络中实际传送帧。多播MAC地址以十六进制值01-00-5E打头,余 下的6个十六进制位是根据IP多播组地址凯枯的最后23位转换得到的。一个MAC多播地址是01-00-5E-0F-64-C5,如图5.10所示。每个十六 进制位相对于4个二进制位。

A和B均可以使用VLC media player,设置A播发组播地址为1,B接收组播地址1的组播,同时设置将接收到的组播流以组播地址2播发蚂御历(在组播接收界面下选闷搜中stream out并点击其后的设置项,在随后出现的对话框中设置地址2),这样就不会形成自环了。

以上意见希拆并望能提供帮助。


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/yw/12217202.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-21
下一篇2023-05-21

发表评论

登录后才能评论

评论列表(0条)

    保存