package org.jgroups.protocols;
import org.jgroups.Event;
import org.jgroups.Global;
import org.jgroups.Message;
import org.jgroups.annotations.Experimental;
import org.jgroups.annotations.Property;
import org.jgroups.util.Buffer;
import org.jgroups.util.ExposedByteArrayInputStream;
import org.jgroups.util.ExposedByteArrayOutputStream;
import org.jgroups.util.Util;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
* Broadcast PING. Uses UDP broadcasts to discover initial membership. This protocol is useless in IPv6 environments, as
* IPv6 has no notion of broadcast addresses. Use IP multicasts instead (e.g. PING or MPING) when running in IPv6.
* @author Bela Ban
* @since 2.12
*/
@Experimental
public class BPING extends PING implements Runnable {
/* ----------------------------------------- Properties -------------------------------------------------- */
@Property(description="Target address for broadcasts. This should be restricted to the local subnet, e.g. 192.168.1.255")
protected String dest="255.255.255.255";
@Property(description="Port for discovery packets", systemProperty=Global.BPING_BIND_PORT)
protected int bind_port=8555;
@Property(description="Sends discovery packets to ports 8555 to (8555+port_range)")
protected int port_range=5;
/* --------------------------------------------- Fields ------------------------------------------------------ */
protected DatagramSocket sock=null;
protected volatile Thread receiver=null;
protected InetAddress dest_addr;
public BPING() {
}
public int getBindPort() {
return bind_port;
}
public void setBindPort(int bind_port) {
this.bind_port=bind_port;
}
public void init() throws Exception {
super.init();
dest_addr=InetAddress.getByName(dest);
if(log.isDebugEnabled())
log.debug("listening on " + bind_port);
}
public void start() throws Exception {
for(int i=bind_port; i < bind_port+port_range; i++) {
try {
sock=getSocketFactory().createDatagramSocket(Global.BPING_SOCK, i);
break;
}
catch(Throwable t) {
if(i >= bind_port+port_range-1)
throw new RuntimeException("failed to open a port in range [" + bind_port + " - " + (bind_port+port_range) + "]", t);
}
}
sock.setBroadcast(true);
startReceiver();
super.start();
}
private void startReceiver() {
if(receiver == null || !receiver.isAlive()) {
receiver=new Thread(Util.getGlobalThreadGroup(), this, "ReceiverThread");
receiver.setDaemon(true);
receiver.start();
if(log.isTraceEnabled())
log.trace("receiver thread started");
}
}
public void stop() {
Util.close(sock);
sock=null;
receiver=null;
super.stop();
}
void sendMcastDiscoveryRequest(Message msg) {
DataOutputStream out=null;
try {
if(msg.getSrc() == null)
msg.setSrc(local_addr);
ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(128);
out=new DataOutputStream(out_stream);
msg.writeTo(out);
out.flush(); // flushes contents to out_stream
Buffer buf=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size());
discovery_reception.reset();
for(int i=bind_port; i < bind_port+port_range; i++) {
DatagramPacket packet=new DatagramPacket(buf.getBuf(), buf.getOffset(), buf.getLength(), dest_addr, i);
sock.send(packet);
}
waitForDiscoveryRequestReception();
}
catch(IOException ex) {
log.error("failed sending discovery request", ex);
}
finally {
Util.close(out);
}
}
public void run() {
final byte[] receive_buf=new byte[65535];
DatagramPacket packet=new DatagramPacket(receive_buf, receive_buf.length);
byte[] data;
ByteArrayInputStream inp_stream;
DataInputStream inp=null;
Message msg;
while(sock != null && receiver != null && Thread.currentThread().equals(receiver)) {
packet.setData(receive_buf, 0, receive_buf.length);
try {
sock.receive(packet);
data=packet.getData();
inp_stream=new ExposedByteArrayInputStream(data, 0, data.length);
inp=new DataInputStream(inp_stream);
msg=new Message();
msg.readFrom(inp);
up(new Event(Event.MSG, msg));
}
catch(SocketException socketEx) {
break;
}
catch(Throwable ex) {
log.error("failed receiving packet (from " + packet.getSocketAddress() + ")", ex);
}
finally {
Util.close(inp);
}
}
if(log.isTraceEnabled())
log.trace("receiver thread terminated");
}
}