2014-02-21 06:09:47 +00:00
|
|
|
package org.servalproject.servaldna;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.net.Inet4Address;
|
|
|
|
import java.net.InetAddress;
|
|
|
|
import java.net.InetSocketAddress;
|
|
|
|
import java.net.UnknownHostException;
|
|
|
|
import java.nio.channels.DatagramChannel;
|
|
|
|
import java.nio.channels.SelectableChannel;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Created by jeremy on 17/02/14.
|
|
|
|
*/
|
|
|
|
public class MdpSocket{
|
2014-02-24 06:20:44 +00:00
|
|
|
private DatagramChannel channel = null;
|
|
|
|
private SubscriberId sid = null;
|
2014-02-21 06:09:47 +00:00
|
|
|
private int port;
|
|
|
|
|
|
|
|
private static final InetAddress loopback;
|
2014-06-20 01:25:09 +00:00
|
|
|
private final int loopbackMdpPort;
|
2014-02-21 06:09:47 +00:00
|
|
|
static {
|
|
|
|
InetAddress local=null;
|
|
|
|
try {
|
2014-06-20 01:25:09 +00:00
|
|
|
// can't trust Inet4Address.getLocalHost() as some implementations can fail to resolve the name "loopback"
|
2014-02-21 06:09:47 +00:00
|
|
|
local = Inet4Address.getByAddress(new byte[]{127, 0, 0, 1});
|
|
|
|
} catch (UnknownHostException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
loopback = local;
|
|
|
|
}
|
|
|
|
|
2014-02-24 06:20:44 +00:00
|
|
|
/* Create an unbound socket, may be used for other information requests before binding */
|
2014-06-20 01:25:09 +00:00
|
|
|
public MdpSocket(int loopbackMdpPort) throws IOException {
|
|
|
|
this.loopbackMdpPort = loopbackMdpPort;
|
2014-02-21 06:09:47 +00:00
|
|
|
}
|
2014-06-20 01:25:09 +00:00
|
|
|
public MdpSocket(int loopbackMdpPort, int port) throws IOException {
|
|
|
|
this(loopbackMdpPort);
|
2014-02-24 06:20:44 +00:00
|
|
|
bind(SubscriberId.ANY, port);
|
2014-02-21 06:09:47 +00:00
|
|
|
}
|
2014-06-20 01:25:09 +00:00
|
|
|
public MdpSocket(int loopbackMdpPort, SubscriberId sid, int port) throws IOException {
|
|
|
|
this(loopbackMdpPort);
|
2014-02-24 06:20:44 +00:00
|
|
|
bind(sid, port);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void bind() throws IOException {
|
|
|
|
bind(SubscriberId.ANY, 0);
|
|
|
|
}
|
|
|
|
public void bind(int port) throws IOException {
|
|
|
|
bind(SubscriberId.ANY, port);
|
|
|
|
}
|
|
|
|
public synchronized void bind(SubscriberId sid, int port) throws IOException {
|
2014-02-21 06:09:47 +00:00
|
|
|
if (loopbackMdpPort==0)
|
|
|
|
throw new IOException("Loopback MDP port has not been set");
|
2014-09-16 05:11:59 +00:00
|
|
|
if (sid==null)
|
|
|
|
throw new NullPointerException();
|
2014-02-24 06:20:44 +00:00
|
|
|
if (sid.equals(this.sid) && this.port == port)
|
|
|
|
return;
|
|
|
|
if (this.sid!=null)
|
|
|
|
throw new IOException("Socket is already bound");
|
|
|
|
getChannel();
|
|
|
|
if (!channel.isBlocking())
|
|
|
|
throw new IOException("Cannot bind a non-blocking socket");
|
2014-02-21 06:09:47 +00:00
|
|
|
MdpPacket packet = new MdpPacket();
|
|
|
|
packet.setLocalSid(sid);
|
|
|
|
packet.setLocalPort(port);
|
|
|
|
packet.setFlags(MdpPacket.MDP_FLAG_BIND);
|
|
|
|
packet.payload.flip();
|
|
|
|
packet.send(channel);
|
2014-02-25 02:12:36 +00:00
|
|
|
channel.socket().setSoTimeout(5000);
|
2014-02-24 06:20:44 +00:00
|
|
|
// should throw MdpError on bind failures
|
2014-02-21 06:09:47 +00:00
|
|
|
receive(packet);
|
2014-09-16 01:52:26 +00:00
|
|
|
if (sid.isBroadcast()){
|
|
|
|
this.sid = sid;
|
|
|
|
}else{
|
|
|
|
try {
|
|
|
|
this.sid = packet.getLocalSid();
|
|
|
|
} catch (AbstractId.InvalidBinaryException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
throw new MdpError(e);
|
|
|
|
}
|
2014-02-21 06:09:47 +00:00
|
|
|
}
|
|
|
|
this.port = packet.getLocalPort();
|
|
|
|
}
|
|
|
|
|
2014-09-16 05:11:59 +00:00
|
|
|
public void rebind() throws IOException{
|
|
|
|
if (this.sid==null)
|
|
|
|
return;
|
|
|
|
SubscriberId sid = this.sid;
|
|
|
|
this.sid=null;
|
|
|
|
bind(sid, this.port);
|
|
|
|
}
|
|
|
|
|
2014-02-24 06:20:44 +00:00
|
|
|
public SelectableChannel getChannel() throws IOException {
|
|
|
|
if (channel == null){
|
|
|
|
channel = DatagramChannel.open();
|
|
|
|
channel.connect(new InetSocketAddress(loopback, loopbackMdpPort));
|
|
|
|
}
|
2014-02-21 06:09:47 +00:00
|
|
|
return channel;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void send(MdpPacket packet) throws IOException {
|
2014-02-24 06:20:44 +00:00
|
|
|
if (sid==null)
|
|
|
|
bind(SubscriberId.ANY, 0);
|
2014-09-16 01:52:26 +00:00
|
|
|
if (!this.sid.isBroadcast())
|
|
|
|
packet.setLocalSid(this.sid);
|
2014-02-21 06:09:47 +00:00
|
|
|
packet.setLocalPort(this.port);
|
|
|
|
packet.send(channel);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void receive(MdpPacket packet) throws IOException {
|
|
|
|
packet.receive(channel);
|
|
|
|
if ((packet.getFlags() & MdpPacket.MDP_FLAG_ERROR)!=0)
|
|
|
|
throw new MdpError("Unspecified error reported by server");
|
|
|
|
}
|
|
|
|
|
|
|
|
public void close() {
|
2014-02-24 06:20:44 +00:00
|
|
|
if (sid!=null){
|
|
|
|
try {
|
|
|
|
MdpPacket p = new MdpPacket();
|
|
|
|
p.payload.flip();
|
|
|
|
p.setFlags(MdpPacket.MDP_FLAG_CLOSE);
|
|
|
|
send(p);
|
|
|
|
} catch (IOException e) {
|
|
|
|
// ignore errors due to servald stopping.
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2014-09-16 05:11:59 +00:00
|
|
|
sid = null;
|
2014-02-21 06:09:47 +00:00
|
|
|
}
|
2014-02-24 06:20:44 +00:00
|
|
|
if (channel!=null){
|
|
|
|
try {
|
|
|
|
channel.close();
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2014-02-21 06:09:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class MdpError extends IOException{
|
|
|
|
public MdpError(String msg){
|
|
|
|
super(msg);
|
|
|
|
}
|
|
|
|
public MdpError(String msg, Throwable cause){
|
|
|
|
super(msg);
|
|
|
|
this.initCause(cause);
|
|
|
|
}
|
|
|
|
public MdpError(Throwable cause){
|
|
|
|
super();
|
|
|
|
this.initCause(cause);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|