对于普通的io,我可以这样做:
s1=DatagramChannel.open()
s1.bind(new Inetsocketaddress("localhost",1234)
s2=DatagramChannel.open()
s2.bind(new Inetsocketaddress("localhost",1234)// will throw an exception
但是在nio。这该怎么做?我试过这样:
第一
s1=DatagramChannel.open()
s1.setOption(StandardSocketOptions.SO_REUSEADDR, true);
s1.bind(new Inetsocketaddress("localhost",1234)
s2=DatagramChannel.open()
s2.setOption(StandardSocketOptions.SO_REUSEADDR, true);
s2.bind(new Inetsocketaddress("localhost",1234)
//these code will not throw exception. but s2 cannot receive any data.
// when i close s1, then s2 will receive data.
第二
{{1}}
答案 0 :(得分:2)
如果您确实需要所有客户端处理消息,则需要多播/广播协议。从这个意义上讲,如前一个问题(Does Java NIO support broadcast or multicast?)所述,NIO2支持多播和广播但不使用DatagramChannel类。相反,您拥有MultiCastChannel界面(您可以在http://javanio.info/filearea/nioserver/WhatsNewNIO2.pdf中找到官方文档,并在How do I implement a multicast client in NIO.2?中找到示例)。 关于您的代码,它应该如下所示:
NetworkInterface netInterface = NetworkInterface.getByName("em1");
InetAddress group = InetAddress.getByName("localhost");
// Reader 1
System.out.println("Create Reader 1");
DatagramChannel s1 = DatagramChannel.open(StandardProtocolFamily.INET);
s1.setOption(StandardSocketOptions.SO_REUSEADDR, true);
s1.bind(new InetSocketAddress(PORT));
s1.setOption(StandardSocketOptions.IP_MULTICAST_IF, netInterface);
// Reader 2
System.out.println("Create Reader 2");
DatagramChannel s2 = DatagramChannel.open(StandardProtocolFamily.INET);
s2.setOption(StandardSocketOptions.SO_REUSEADDR, true);
s2.bind(new InetSocketAddress(PORT));
s2.setOption(StandardSocketOptions.IP_MULTICAST_IF, netInterface);
请注意,底层硬件堆栈必须支持多播/广播。否则,join
方法将抛出异常。
如果您不需要所有客户端处理消息,而是需要其中任何一个来处理它而不阻塞其他客户端,则可以设置非阻塞套接字选项。考虑到您提供的代码,我在下面添加了一个非阻塞的解决方案:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.charset.StandardCharsets;
public class Main {
private static final String HOST = "localhost";
private static final int PORT = 1234;
private static final String MESSAGE = "HelloWorld";
private static final int MESSAGE_SIZE = MESSAGE.length();
public static void main(String[] args) throws IOException {
// Reader 1
System.out.println("Create Reader 1");
DatagramChannel s1 = DatagramChannel.open();
s1.configureBlocking(false);
s1.setOption(StandardSocketOptions.SO_REUSEADDR, true);
s1.bind(new InetSocketAddress(HOST, PORT));
// Reader 2
System.out.println("Create Reader 2");
DatagramChannel s2 = DatagramChannel.open();
s2.configureBlocking(false);
s2.setOption(StandardSocketOptions.SO_REUSEADDR, true);
s2.bind(new InetSocketAddress(HOST, PORT));
// Writer
System.out.println("Create Writer");
DatagramChannel s3 = DatagramChannel.open();
// Send and receive messages
System.out.println("Send message");
send(s3);
System.out.println("Receive message on Reader 1");
receive(s1);
System.out.println("Receive message on Reader 2");
receive(s2);
// Close
System.out.println("Close");
s1.close();
s2.close();
s3.close();
}
private static void send(DatagramChannel channel) throws IOException {
ByteBuffer buf = ByteBuffer.allocate(MESSAGE_SIZE);
buf.clear();
buf.put(MESSAGE.getBytes());
buf.flip();
int bytesSent = channel.send(buf, new InetSocketAddress(HOST, PORT));
System.out.println("Sent: " + bytesSent);
}
private static void receive(DatagramChannel channel) throws IOException {
ByteBuffer buf = ByteBuffer.allocate(MESSAGE_SIZE);
buf.clear();
channel.receive(buf);
String str = new String(buf.array(), StandardCharsets.UTF_8);
System.out.println("Received: " + str);
}
}
哪个输出是:
Create Reader 1
Create Reader 2
Create Writer
Send message
Sent: 10
Receive message on Reader 1
Received:
Receive message on Reader 2
Received: HelloWorld
Close
答案 1 :(得分:-1)
我知道原因。
单播只能由一个客户端接收,如果多个客户端监听同一个端口,则只有第一个客户端会收到数据。
如果您希望多个客户端接收数据,则应使用多播或广播。
如果使用广播,则应将数据包发送到255.255.255:端口,以便侦听端口的所有客户端都将接收数据。 并且客户端绑定地址无法使用localhost或127.0.0.1。你应该使用0.0.0.0或InetSocketAddress(端口)或你的本地地址,如192.168.100.123
如果任何其他应用程序绑定没有' reuseAddress'的端口,那么您也无法绑定端口。
kotlin代码:
fun main(args: Array<String>) {
val selector = Selector.open()
val reader = DatagramChannel.open()
val sender = DatagramChannel.open()
reader.configureBlocking(false)
reader.socket().reuseAddress = true
// reader.bind(InetSocketAddress("0.0.0.0", 1234))
// reader.bind(InetSocketAddress("192.168.100.37", 1234))
reader.bind(InetSocketAddress(1234))
reader.register(selector, SelectionKey.OP_READ)
sender.socket().broadcast = true
val t = Thread {
while (true) {
val line = readLine()
if (line != null) {
val buffer = ByteBuffer.wrap(line.toByteArray())
sender.send(buffer, InetSocketAddress("255.255.255.255", 1234))
}
}
}
t.isDaemon = true
t.start()
while (selector.isOpen) {
val n = selector.select()
if (n == 0) continue
val keys = selector.selectedKeys().iterator()
while (keys.hasNext()) {
val key = keys.next()
keys.remove()
if (key.isReadable) {
val buffer = ByteBuffer.allocateDirect(1024)
val channel = key.channel() as DatagramChannel
val address = channel.receive(buffer)
buffer.flip()
val s = Charsets.UTF_8.decode(buffer)
println("$address says : $s")
}
}
}
}