1

for normal io , I can do it like this:

s1=new DatagramSocket(1234)
s2=new DatagramSocket(1234)

bytes=xxxxxx
packet = DatagramPacket(bytes, bytes.size,InetSocketAddress("localhost",1234))
new DatagramSocket().send(packet)
//  and then the s1 and s2 can receive the packet.

but in nio. how to do this? I tried like this:

first:

s1=DatagramChannel.open()
s1.bind(new Inetsocketaddress("localhost",1234)

s2=DatagramChannel.open()
s2.bind(new Inetsocketaddress("localhost",1234)// will throw an exception

second:

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.
Tony
  • 520
  • 4
  • 13

2 Answers2

2

If you really need the message to be processed by all the clients, you need a multi-cast / broad-cast protocol. In this sense, as stated in a previous question (Does Java NIO support broadcast or multicast?), NIO2 Supports multi-cast and broad-cast but not using the DatagramChannel class. Instead you have the MultiCastChannel interface (you can find official documentation in http://javanio.info/filearea/nioserver/WhatsNewNIO2.pdf and an example in: How do I implement a multicast client in NIO.2?). Regarding your code, it should look like:

   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);

Be aware that multi-cast / broad-cast must be supported by your underlying hardware stack. Otherwise, the join method will throw an exception.

If you don't need the message to be processed by all the clients but rather you need any of them to process it without blocking the others, you can set the non-blocking socket option. Considering the code you provided, I add below a solution with non-blocking:

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);
    }

}

Which output is:

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
Cristian Ramon-Cortes
  • 1,838
  • 1
  • 19
  • 32
  • the reader2 receive 0 bytes. If i define reader2 first, then reader2 will receive the data,but reader1 receive 0 bytes. – Tony Jul 06 '17 at 01:43
  • @Tony Please consider [accepting the answer](https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work) if it was useful. – Cristian Ramon-Cortes Aug 08 '17 at 09:24
0

I know the reason.

The Unicast can only be received by one client,if several clients listen the same port, only the first client will receive the data.

If you want several clients receive the data, you should use Multicast or Broadcast.

If you use the Broadcast, the packet should be sent to 255.255.255:port,so all the clients listening the port will receive the data. and the clients bind address could not use localhost or 127.0.0.1. you should use 0.0.0.0 or InetSocketAddress(port) or your local address like 192.168.100.123

If any other application bind a port without 'reuseAddress', then you can not bind the port too.

the kotlin code:

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")
            }
        }
    }
}
Tony
  • 520
  • 4
  • 13