网络数据传播按照接收者的数量,可分为以下3种方式。
·单播:提供点对点的通信。发送者每次发送的数据有着唯一的目的地址,只被一个接收者接收。
·广播:发送者每次发送的数据可以被传播范围内的所有接收者接收。电视台就采用广播方式。从电视台发射的信号被发送到传播范围内的每个点。不管电视机有没有打开,信号都能到达每台电视机。IP支持广播,但是由于广播会大大增加网络的数据流量,因此对广播的使用作了严格的限制。路由器可以限制对本地网络或子网的广播,并禁止对整个Internet的广播。此外,应该禁止广播那些占高带宽的数据,如音频和视频数据。试想如果一个实时视频流被广播给上亿的Internet用户,那么将使Internet严重超载,甚至崩溃。
·组播:发送者每次发送的数据可以被小组内的所有接收者接收。组播的接收范围介于单播和广播之间。对特定话题感兴趣的主机加入同一个组播组。向这个组发送的数据会到达组内所有的主机,但不会到达组外的主机。 当发送者要给1000个接收者发送同样的数据,如果采用单播,那么数据在发送方被复制1000份,分别传送给每个接收者,这种传播方式效率很低,因为它对数据进行了不必要的复制,浪费了许多网络带宽。如果采用组播,则可以大大提高传输效率,路由器会动态决定组播数据的路由,只在必要时才复制数据
组播组内的所有主机共享同一个地址,这种地址被称为组播地址。组播地址是范围在224.0.0.0 ~239.255.255.255之间的IP地址。此范围内的所有地址的前4个二进制位都是“1110”。组播地址也被称为D类IP地址,与其他的A类、B类和C类地址相区别。组播组是开放的,主机可以在任何时候进入或离开组。IPv6的组播地址都以十六进制值0xFF开头(对应的二进制值为11111111)
IANA(Internet Assigned Numbers Authority)组织负责分发永久组播地址。到目前为止,它已经分配了几百个地址。大多数已分配的地址以224.0、224.1、224.2或239开头,
8-1永久组播地址
表8-1列出了一些永久组播地址。完整的组播地址分配列表可以从iana的官方网站获得。剩余的2.48亿个D类地址可以被任何需要的人用于临时目的。组播路由器(简称为mrouter)负责确保两个不同的网络系统不会同时使用相同的D类地址。从表8-1可以看出,与其他IP地址一样,组播地址也可以有域名,例如组播地址224.0.1.1的域名为ntp.mcast.net。
大多数组播数据为音频或视频,这些数据一般都很大,即便部分数据在传输途中被丢失,接收方也仍然能识别信号。因此组播数据通过UDP发送,虽然不可靠,但比面向连接的TCP的传输速度快3倍以上。 组播与单播UDP的区别在于,前者必须考虑TTL(Time To Live)值,它用IP数据包的头部的1字节表示。TTL通过限制IP包被丢弃前通过的路由器数目,来决定IP包的生存时间。IP包每通过一个路由器,TTL就减1,当TTL变为0,这个包就被丢弃。TTL的一个作用是防止配置有误的路由器把包在路由器之间无限地来回传递,还有一个作用是限制组播的地理范围。例如,当TTL为16时,包被限制在本地区域内传播,当TTL为255时,包将被发送到整个世界。不过,TTL并不能精确地决定包被传播的地理范围。一般情况下,接收方离得越远,包要经过的路由器就越多,TTL值小的包不会比TTL值大的包传播得更远。表8-2列出了TTL值与包传播的地理范围的粗略对应关系。值得意的是,无论TTL取什么值,如果包被发送给地址为224.0.0.0~224.0.0.255之间的组播组,则包只会在本地子网内传播,而不会被转发到其他网络。
如果要收发本地子网以外的组播数据,那么要求本地网上配置了组播路由器(mrouter)。可以从网络管理员那里了解是否配置了mrouter,也可以运行命令“ping all-routers.mcast.net”,如果有路由器响应,则说明网络上有mrouter。 此外,即使子网上配置了mrouter,还是不能保证收发Internet上每一台主机的组播数据。如果要让包能到达组播组内的任何地址,那么需要在发送主机和接收主机之间存在
。
条件 一个局域网 手机的网络调试精灵软件非常方便,可以在上面查看收到的数据报:
192.168.1.100是手机上自动的端口(不明白为啥跟电脑上ipconfig的ip不一样,明明是一个局域网)
public static void main(String[] args) throws IOException, InterruptedException { Net net = new Net(); MulticastSocket multicastSocket2 =new MulticastSocket(null); SocketAddress socketAddress = new InetSocketAddress(4000); InetAddress group =InetAddress.getByName("192.168.1.100"); int port = 4000; multicastSocket2.bind(socketAddress); String msg = "hello"+new java.util.Date(); byte[]buffer = msg.getBytes(StandardCharsets.UTF_8); DatagramPacket packet = new DatagramPacket(buffer,buffer.length,group,port); while(true) { Thread.sleep(5000); multicastSocket2.send(packet); } } }
组播(multicasting)是一种多点投递的形式,它使用硬件技术,通过使用大量组播地址来通信。当某一组机器需要通信时,选择一个组播地址,并配置好相应的网络接口硬件,识别组播地址,从而收到该组播地址上分组的拷贝。
在网络技术的应用并不是很多,网上视频会议、网上视频观看特别适合采用组播方式。
广播(broadcasting)是多点投递的最普遍的形式,它向每一个目的站投递一个分组的拷贝。它可以通过多个单次分组的投递完成,也可以通过单独的连接传递分组的拷贝,直到每个接收方均收到一个拷贝为止。
在网络中不能长时间出现大量的广播包,否则就会出现所谓的“广播风暴”。广播风暴就是网络长时间被大量的广播数据包所占用,正常的点对点通信无法正常进行,外在表现为网络速度奇慢无比。出现广播风暴的原因有很多,一块有故障的网卡,就可能长时间向网络上发送广播包而导致广播风暴。
广播风暴不能完全杜绝,但是只能在同一子网内传播,就好像喇叭的声音只能在同一会场内传播一样,因此在由几百台甚至上千台电脑构成的大中型局域网中,一般进行子网划分,就像将一个大厅用墙壁隔离成许多小厅一样,以达到隔离广播风暴的目的。
在IP网络中,广播地址用IP地址“255.255.来表示,这个IP地址代表同一子网内所有的IP地址。
IP网络的组播一般通过多播IP地址来实现。多播IP地址就是D类IP地址,即224.0.0.0至239.255.255.255之间的IP地址。Windows 2000中的DHCP管理器支持多播IP地址的自动分配
TCP是面向连接的可靠的传输协议,而UDP是无连接的不可靠的传输协议。DatagramSocket以及DatagramChannel都建立在UDP的基础上,当通过它们发送数据报时,如果数据报未送达目的地,那么发送方不会得到任何通知,程序不会抛出异常;当通过它们接收数据报时,如果用于存放数据报的缓冲区的容量小于接收的数据报的大小,那么多余的数据被丢弃,接收方不会得到任何通知,程序不会抛出异常。 尽管DatagramSocket以及DatagramChannel也有connect()方法,但该方法与TCP Socket的connect()方法有着不同的作用。前者不建立TCP意义上的连接,而是限制当前DatagramSocket或DatagramChannel只对参数指定的远程主机和UDP端口收发数据报。 DatagramSocket与DatagramPacket搭配使用,DatagramPacket用来存放接收或发送的数据。DatagramChannel与ByteBuffer搭配使用,ByteBuffer用来存放接收或发送的数据。DatagramSocket只能工作于阻塞模式,而DatagramChannel支持阻塞和非阻塞两种模式。
MulticastSocket是DatagramSocket的子类,它能发送和接收组播数据报**。MulticastSocket必须加入一个组播组中,才能接收发往该组的数据报**。组播组内的所有MulticastSocket共享同一个组播IP地址。组播IP地址介于224.0.0.0 ~239.255.255.255之间。
效果:
注意ip和端口要相同
public class Test implements Runnable{ private static final String BROADCAST_IP = "230.0.0.1"; // 使用常量作为本程序的多点广播目的的端口 public static final int BROADCAST_PORT = 30000; // 定义每个数据报的最大大小为4K private static final int DATA_LEN = 4096; // 定义本程序的MulticastSocket实例 private MulticastSocket socket = null; private InetAddress broadcastAddress = null; private Scanner scan = null; // 定义接收网络数据的字节数组 byte[] inBuff = new byte[DATA_LEN]; // 以指定字节数组创建准备接受数据的DatagramPacket对象 private DatagramPacket inPacket = new DatagramPacket(inBuff, inBuff.length); // 定义一个用于发送的DatagramPacket对象 private DatagramPacket outPacket = null; public void init() throws IOException { try { // 创建用于发送、接收数据的MulticastSocket对象 // 因为该MulticastSocket对象需要接收,所以有指定端口 socket = new MulticastSocket(BROADCAST_PORT); broadcastAddress = InetAddress.getByName(BROADCAST_IP); // 将该socket加入指定的多点广播地址 socket.joinGroup(broadcastAddress); // 设置本MulticastSocket发送的数据报被回送到自身 socket.setLoopbackMode(false); // 初始化发送用的DatagramSocket,它包含一个长度为0的字节数组 outPacket = new DatagramPacket(new byte[0], 0, broadcastAddress, BROADCAST_PORT); // 启动以本实例的run()方法作为线程体的线程 new Thread(this).start(); // 创建键盘输入流 scan = new Scanner(System.in); // 不断读取键盘输入 while (scan.hasNextLine()) { // 将键盘输入的一行字符串转换字节数组 byte[] buff = scan.nextLine().getBytes(); // 设置发送用的DatagramPacket里的字节数据 outPacket.setData(buff); // 发送数据报 socket.send(outPacket); } } finally { socket.close(); } } public void run() { try { while (true) { // 读取Socket中的数据,读到的数据放在inPacket所封装的字节数组里。 socket.receive(inPacket); // 打印输出从socket中读取的内容 System.out.println("聊天信息:" + new String(inBuff, 0, inPacket.getLength())); } } // 捕捉异常 catch (IOException ex) { ex.printStackTrace(); try { if (socket != null) { // 让该Socket离开该多点IP广播地址 socket.leaveGroup(broadcastAddress); // 关闭该Socket对象 socket.close(); } System.exit(1); } catch (IOException e) { e.printStackTrace(); } } } public static void main(String[] args) throws IOException { new Test().init(); } }