如何使用 Java 套接字进行多播

本文介绍Java中使用Socket API实现多播通信的方法。包括如何利用MulticastSocket类进行多播消息的发送与接收,以及通过DatagramChannel进行多播的具体操作。

Java Socket API 支持客户端-服务器范式中远程主机之间的网络通信。可以通过三种方式建立通信:一对一通信(客户端-服务器)、一对多通信(广播)和一对多通信(组播)。本文详细阐述了套接字的总体概念,特别是多播,并展示了如何使用 Java 套接字来实现它。

概述:套接字和 IP 地址

套接字基本上是指用于发送和接收数据的目的的网络中的机器之间的指定虚拟端点。网络中的机器由 IP 地址唯一标识。有一种特定的格式和 IP 类别定义了分配给它的网络类型。随着互联网的发展,将更多设备容纳到网络中的需求增加了。结果,使用 32 位寻址并可以支持大约 43 亿台设备的 IPv4 突然显得不那么适应了。因此,需要一个称为 IPv6 的改进版本;它使用64位寻址,可以支持大约3.4e10 4个设备!然而,IPv4 仍在使用并蓬勃发展;因此,我们将只讨论 IPv4。

IPv4 地址格式示例:192.168.17.14(C 类)

第1个字节范围第2个字节范围第3个字节范围第4个字节范围
范围(0-255)(0-255)(0-255)(0-255)

现在,IPv4 支持五类 IP 范围:A 类、B 类、C 类、D 类和 E 类。其中,常用的只有 A、B 和 C。其他有特殊用途。分类的原因是我们可以将我们在网络中大约 43 亿台机器(最坏情况)的大海捞针中搜索机器,限制在特定地址类定义的机器的可管理过滤部分。

第一个八位组范围网络 (N), 主机 (H)子网掩码网络数量每个网络的主机
A1-126NHHH255.0.0.012616777214
B128-191NNHH255.255.0.01638265534
C192-223NNNH255.255.255.02097150254
D224-239用于多播。
E240-254实验性的;保留用于研究目的。

IP 地址 127.xxx 保留为环回地址,称为localhost。IP 地址 255.255.255.255 用于向局域网中的所有主机广播。而且,还有一些私有 IP 地址(在我们讨论的时候不相关)。

客户端和服务器套接字

想象一台服务器正在一台机器上运行。运行本质上意味着服务器进入监听模式;换句话说,循环检查是否有数据到达。现在,数据通过网络通道到达,对吧?但是,数据的类型是由它所遵循的协议定义的。协议定义了数据打包遵循的规范。一些流行的协议是 HTTP、FTP、SMTP 等。(它就像一个包装器,决定了它的公民身份以及服务器如何处理数据。)回想一下,服务器循环地监听套接字(记住,这是一个虚拟的东西)。服务器可能会监听许多套接字。每个套接字由有助于隔离网络中正确机器的 IP 地址和确定服务器专门侦听的机器的套接字或正确端点的端口号唯一标识。

了解差异

  • 单播:网络中两台机器之间的消息发送。
  • 广播:消息发送到网络中的所有机器。
  • 多播:消息发送到网络中的一台或多台机器。

TCP 和 UDP 协议

有两种类型的套接字:面向连接的套接字和无连接的套接字。面向连接的套接字也称为流套接字。在流套接字中,在数据传输之前,通过称为握手的技术建立虚拟的一对一连接,并且数据流在整个虚拟通道中不间断地发送。在无连接套接字中,一次发送一个数据包,并且不建立专用连接。它也称为数据报套接字传输控制协议 (TCP)是传输层协议,是面向连接的套接字使用最广泛的协议。用户数据报协议 (UDP)也是一种传输层协议,但广泛用于无连接套接字。

简而言之,流套接字是:

  • 网络中两台主机之间的点对点专用通道
  • 高度可靠的通信
  • 以相似顺序发送和接收的数据包
  • 即使在传输暂停之间,通道仍然被占用
  • 传输中丢失的数据恢复时间长
  • 使用 TCP 协议

数据报套接字是:

  • 没有专用频道
  • 使用 UDP 协议
  • 可能不是 100% 可靠的
  • 数据发送和接收顺序不一样
  • 传输中丢失数据的快速恢复时间

现在,如果您想知道……我们可以使用 TCP 广播或多播吗?? 显然,答案是否定的,因为 TCP 是一种用于在两个端点之间建立连接的协议。创建的虚拟通道是专用的,直到其中一台主机关闭连接。因此,TCP 使用昂贵的连接建立可靠的传输。当一个数据包被发送时,它需要一个确认。确认确定消息已在另一端正确接收;否则,数据包被重传。在整个过程中,通道始终保持一对一的占用。在广播和多播的情况下,没有回复和响应的概念。它创造了一种单向交通。因此,TCP 的数据传输可靠性不能在 UDP 之上实现,顺便说一句,从逻辑上讲是没有意义的。因此,我们使用 UDP 进行多播和广播。

一个简单的例子

java.net 包中定义的MulticastSocket类表示一个多播套接字。一旦MulticastSocket上对象被创建时,joinGroup()方法被调用以使其成员接收的多播消息之一。请注意,组播 IP 地址定义在 224.0.0.0 到 239.255.255.255 的范围内。以下是 IP 多播地址范围和用途。

起始地址结束地址用途
224.0.0.0224.0.0.255为特殊的“知名”多播地址保留
224.0.1.0238.255.255.255全球范围(互联网范围)的多播地址
239.0.0.0239.255.255.255管理范围的(本地)多播地址

来源:http : //www.tcpipguide.com/free/t_IPMulticastAddressing.htm


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class UDPMulticastClient implements Runnable {

	public static void main(String[] args) {
		Thread t = new Thread(new UDPMulticastClient());
		t.start();
	}

	public void receiveUDPMessage(String ip, int port) throws IOException {
		byte[] buffer = new byte[1024];
		MulticastSocket socket = new MulticastSocket(4321);
		InetAddress group = InetAddress.getByName("230.0.0.0");
		socket.joinGroup(group);
		while (true) {
			System.out.println("Waiting for multicast message...");
			DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
			socket.receive(packet);
			String msg = new String(packet.getData(), packet.getOffset(), packet.getLength());
			System.out.println("[Multicast UDP message received] >> " + msg);
			if ("OK".equals(msg)) {
				System.out.println("No more message. Exiting : " + msg);
				break;
			}
		}
		socket.leaveGroup(group);
		socket.close();
	}

	@Override
	public void run() {
		try {
			receiveUDPMessage("230.0.0.0", 4321);
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}

}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPMulticastServer {

	public static void sendUDPMessage(String message, String ipAddress, int port) throws IOException {
		DatagramSocket socket = new DatagramSocket();
		InetAddress group = InetAddress.getByName(ipAddress);
		byte[] msg = message.getBytes();
		DatagramPacket packet = new DatagramPacket(msg, msg.length, group, port);
		socket.send(packet);
		socket.close();
	}

	public static void main(String[] args) throws IOException {
		sendUDPMessage("This is a multicast messge", "230.0.0.0", 4321);
		sendUDPMessage("This is the second multicast messge", "230.0.0.0", 4321);
		sendUDPMessage("This is the third multicast messge", "230.0.0.0", 4321);
		sendUDPMessage("OK", "230.0.0.0", 4321);
	}

}

通过数据报通道进行组播

Java 通过java.nio.channels包中定义的名为DatagramChannel的类支持通过数据报通道进行多播。想要接收多播消息的数据报通道加入多播组。这样,它就成为接收组播消息的组的成员。建立连接后,数据报通道将保持连接状态,直到断开或关闭。因此,我们可以通过调用isConnected()方法来检查数据报通道的状态,如果连接打开则返回布尔值true,否则返回false

这是一个简单的示例来说明如何在多播中使用数据报通道。

一个简单的例子

使用数据报通道有一些额外的优势,例如我们可以选择仅从选定的源接收多播数据报并阻止其他源。例如,我们可以使用MembershipKey类中的block(InterAddress)方法来阻止多播消息源。有一种方法,称为unblock(InetAddress),可以解锁相同的内容。在这里,我们将实现简单的多播消息发送和接收场景。有关其他 API 信息,请参阅 Java API 文档。


import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.MembershipKey;

public class MulticastReceiver {
	private static final String MULTICAST_INTERFACE = "eth0";
	private static final int MULTICAST_PORT = 4321;
	private static final String MULTICAST_IP = "230.0.0.0";

	private String receiveMessage(String ip, String iface, int port) throws IOException {
		DatagramChannel datagramChannel = DatagramChannel.open(StandardProtocolFamily.INET);
		NetworkInterface networkInterface =NetworkInterface.getByInetAddress(InetAddress.getLocalHost());
		datagramChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
		datagramChannel.bind(new InetSocketAddress(port));
		//datagramChannel.setOption(StandardSocketOptions.IP_MULTICAST_IF, networkInterface);
		InetAddress inetAddress = InetAddress.getByName(ip);
		MembershipKey membershipKey = datagramChannel.join(inetAddress,networkInterface);
		System.out.println("Waiting for the message...");
		ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
		datagramChannel.receive(byteBuffer);
		byteBuffer.flip();
		byte[] bytes = new byte[byteBuffer.limit()];
		byteBuffer.get(bytes, 0, byteBuffer.limit());
		membershipKey.drop();
		return new String(bytes);
	}

	public static void main(String[] args) throws IOException {
		MulticastReceiver mr = new MulticastReceiver();
		System.out
				.println("Message received : " + mr.receiveMessage(MULTICAST_IP, MULTICAST_INTERFACE, MULTICAST_PORT));
	}

}
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;

public class MulticastPublisher {
	private static final String MULTICAST_INTERFACE = "eth0";
	private static final int MULTICAST_PORT = 4321;
	private static final String MULTICAST_IP = "230.0.0.0";

	public void sendMessage(String ip, String iface, int port, String message) throws IOException {
		DatagramChannel datagramChannel = DatagramChannel.open();
		datagramChannel.bind(null);
		NetworkInterface networkInterface = NetworkInterface.getByInetAddress(InetAddress.getLocalHost());
		datagramChannel.setOption(StandardSocketOptions.IP_MULTICAST_IF, networkInterface);
		ByteBuffer byteBuffer = ByteBuffer.wrap(message.getBytes());
		InetSocketAddress inetSocketAddress = new InetSocketAddress(ip, port);
		datagramChannel.send(byteBuffer, inetSocketAddress);
	}

	public static void main(String[] args) throws IOException {
		MulticastPublisher mp = new MulticastPublisher();
		mp.sendMessage(MULTICAST_IP, MULTICAST_INTERFACE, MULTICAST_PORT, "Hi there!");

	}

}

结论

通过 Java 进行多播是 Java 网络编程范式的一部分。两个远程主机之间的通信实际上通过参考模型或 TCP/IP 模型定义的几个层。沟通的基本原理很复杂。但是,从 Java 编程的角度来看,使用网络库提供的 API 变得简单。尽管 UDP 被称为不太可靠的协议,但实际上它也不是那么不可靠。而且,TCP 资源匮乏。因此,在大多数情况下,UDP 似乎是更好的选择。TCP 几乎可以做 UDP 可以做的所有事情——除了多播和广播只能使用 UDP。

去掉了下载分限制对于UDP组播的一些认识 利用UDP组播能在intarnet,internet上也数据报的形式进行数据的组播(在internet上进行组播,要求路由器支持IGMP(internet网关管理协议,这个协议是在IP出现以后,为了支持组播而出现的)).相对于极度消耗网络带宽的广播来说(广播只能在intranet内广播),UDP组播有了很大的优化,只有终端加入到了一个广播组,UDP组播的数据才能被他接受到. UDP组播是采用的无连接,数据报的连接方式,所以是不可靠的.也就是数据能不能到达接受端和数据到达的顺序都是不能保证的.但是由于UDP不用保证数据的可靠性,所有数据的传送速度是很快的.1. 组播的“根” 组播从概念上来讲分为两部分:控制部分和数据部分。控制部分决定着组播的对象的组织方式。而数据部分决定了数据的传输方式。 控制层有“有根”,“无根”两种情况。对于有根的控制层,存在着一个root和若干个leaf. root负责管理这个组播组,只有他能邀请一个leaf加入一个组播组(ATM就是有根控制的一个典型的例子)。对于无根的控制层,没有root,只有若干的leaf. 每一个leaf都能自己加入一个组播组(IP就是无根控制的典型例子) 数据层也有“有根”,“无根”两种情况。对于有根数据层,从root发出的数据能到达每一个leaf,而从leaf发出的数据只能到达root.对于无根数据层,每一个leaf发出的数据能到达组播组中的每一个leaf(甚至包括他自己)。每一个leaf也能接受组播组里的任何数据包。二.IP组播地址 IP组播通信需要一个特殊的组播地址.IP组播地址是一组D类IP地址,范围从224.0.0.0 到 239.255.255.255。其中还有很多地址是为特殊的目的保留的。224.0.0.0到224.0.0.255的地址最好不要用,因为他们大多是为了特殊的目的保持的(比如IGMP协议)三.IGMP协议 IGMP(internet网关管理协议)是IP组播的基础.在IP协议出现以后,为了加入对组播的支持,IGMP产生了。IGMP所做的实际上就是告诉路由器,在这个路由器所在的子网内有人对发送到某一个组播组的数据感兴趣,这样当这个组播组的数据到达后面,路由器就不会抛弃它,而是把他转送给所有感兴趣的客户。假如不同子网内的A,B要进行组播通信,那么,位与A,B之间的所有路由器必须都要支持IGMP协议,否则A,B之间不能进行通信。 当一个应用加入一个组播组后,就会向这个子网的所有路由器发送一个IGMP加入命令,告诉他子网内有人对发送到某一个组播组的数据感兴趣.路由器也会定时向子网内的所有终端发送一条查询消息,用于询问是否还有人对某个组播组的数据感兴趣。如果有的话,终端就会回应一条IGMP消息,路由器则继续转发这个组播组的数据。如果没有人回应这条消息,那么路由器就认为已经没有终端对这个组播组的数据感兴趣,就不会在转发关于这个组播组的数据了。在IGMP第二版中,一个终端推出组播组以后,会向路由器发送一个推出消息,路由器也会通过这个消息来判断是否还要继续转发关于这个组播组的数据了(IGMP第一版中没有这个功能)[这些事情都是底层的系统做的,你只要坐享其成就好了] 四. winsock 1组播 winsock 1的组播主要有以下几个步骤:1. 建立支持数据报的scoket2. 把socket和本地的一个端口绑定(以后会通过这个端口进行数据的收发)3. 通过setsockopt IP_ADD_MEMBERSHIP加入一个组播组4. 然后就能通过sendto / recvfrom进行数据的收法5. 通过 setsockopt IP_DROP_MEMBERSHIP离开一个组播组6. 关闭socket如果你仅仅是想向一个组播组发送数据,而不要接受数据,那么可不用加入组播组,而直接通过sendto向组播组发送数据五.winsock 2组播 winsock 2组播主要是通过WSAJoinLeaf来实现的(WSAJoinLeaf的行为,返回值根据socket的模式,组播的实现构架有很大的关系) winsock 2组播的主要有以下几个步骤1. 建立支持数据报的socket(用WSASocket建立socket,同2. 时设置组播的一些属性)3. 把socket和本地的一个端口绑定(以后会通过这个端口进行数据的收发)4. 通过WSAJoinLeaf加入一个组播组5. 通过sendto / recvfrom进行数据的收发6. 直接关闭socket,7. 退出组播组
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值