/*******************************************************************************
* Copyright 2013-2016 alladin-IT GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package at.alladin.rmbt.qos.testserver.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.text.DateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import at.alladin.rmbt.qos.testserver.ServerPreferences.TestServerServiceEnum;
import at.alladin.rmbt.qos.testserver.servers.AbstractUdpServer;
import at.alladin.rmbt.qos.testserver.TestServer;
import at.alladin.rmbt.qos.testserver.util.TestServerConsole;
import at.alladin.rmbt.util.net.rtp.RealtimeTransportProtocol.RtpVersion;
import at.alladin.rmbt.util.net.rtp.RtpUtil;
/**
*
* @author lb
*
*/
public class NioUdpMultiClientServer extends AbstractUdpServer<DatagramChannel> {
private final static String TAG = NioUdpMultiClientServer.class.getCanonicalName();
private final AtomicBoolean isRunning;
public final static int BUFFER_LENGTH = 1024;
final DatagramChannel channel;
final InetAddress address;
final int port;
private final String name;
private long lastClientTime = 0;
/**
*
* @param port
* @param timeOut
* @throws Exception
*/
public NioUdpMultiClientServer(int port, InetAddress address) throws Exception {
super(DatagramChannel.class);
TestServerConsole.log("Initializing " + TAG + " on " + address + ":" + port, 1, TestServerServiceEnum.TEST_SERVER);
//this.socket = new DatagramSocket(port, TestServer.serverPreferences.getInetAddrBindTo());
this.channel = TestServer.createDatagramChannel(port, address);
this.isRunning = new AtomicBoolean(false);
this.address = address;
this.port = port;
this.name = "NioUdpMultiClientServer [" + address + ":" + port + "]";
}
/*
* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
isRunning.set(true);
try {
TestServerConsole.log("Starting " + TAG + " on address: " + channel.getLocalAddress() + " ...", 2, TestServerServiceEnum.UDP_SERVICE);
channel.configureBlocking(false);
final Selector selector = Selector.open();
channel.register(selector, SelectionKey.OP_READ);
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_LENGTH);
while (isRunning.get()) {
selector.select(5000);
Set<SelectionKey> readyKeys = selector.selectedKeys();
if (!readyKeys.isEmpty()) {
Iterator<SelectionKey> iterator = readyKeys.iterator();
while (iterator.hasNext()) {
// not sure if try does make sense:
// try {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
if (key.isReadable() && key.isValid()) {
buffer.clear();
SocketAddress senderAddr = channel.receive(buffer);
buffer.flip();
final byte[] data = new byte[buffer.remaining()];
buffer.get(data);
if (data == null || data.length == 0) {
continue;
}
final DatagramPacket dp = new DatagramPacket(data, data.length, senderAddr);
final RtpVersion rtpVersion = RtpUtil.getVersion(data[0]);
String clientUuid = null;
//set last client timestamp
lastClientTime = System.currentTimeMillis();
if (!RtpVersion.VER2.equals(rtpVersion)) {
//Non RTP packet:
final int packetNumber = data[1];
String timeStamp = null;
try {
char[] uuid = new char[36];
for (int i = 2; i < 38; i++) {
uuid[i - 2] = (char) data[i];
}
clientUuid = String.valueOf(uuid);
char[] ts = new char[dp.getLength() - 38];
for (int i = 38; i < dp.getLength(); i++) {
ts[i - 38] = (char) data[i];
}
timeStamp = String.valueOf(ts);
}
catch (Exception e) {
TestServerConsole.error(getName(), e, 1, TestServerServiceEnum.UDP_SERVICE);
}
TestServerConsole.log("received UDP from: " + dp.getAddress().toString() + ":" + dp.getPort()
+ " (on local port :" + ((InetSocketAddress) channel.getLocalAddress()).getPort() + ") , #" + packetNumber + " TimeStamp: " + timeStamp + ", containing: " + clientUuid, 1, TestServerServiceEnum.UDP_SERVICE);
}
else {
//RtpPacket received:
clientUuid = "VOIP_" + RtpUtil.getSsrc(data);
}
if (clientUuid != null) {
synchronized (incomingMap) {
final UdpTestCandidate clientData;
final String uuid = clientUuid;
if (!incomingMap.containsKey(clientUuid)) {
clientData = new UdpTestCandidate();
clientData.setNumPackets(Integer.MAX_VALUE);
clientData.setRemotePort(dp.getPort());
incomingMap.put(clientUuid, clientData);
}
else {
clientData = (UdpTestCandidate) incomingMap.get(clientUuid);
if (clientData.isError()) {
continue;
}
}
//if a callback has been provided by the clienthandler run it in the background:
if (clientData.getOnUdpPacketReceivedCallback() != null) {
Runnable onReceiveRunnable = new Runnable() {
@Override
public void run() {
clientData.getOnUdpPacketReceivedCallback().onReceive(dp, uuid, NioUdpMultiClientServer.this);
}
};
TestServer.getCommonThreadPool().submit(onReceiveRunnable);
}
}
}
}
// } catch (final Exception e) {
// TestServerConsole.error(getName(), e, 2, TestServerServiceEnum.UDP_SERVICE);
// }
}
}
}
}
catch (Exception e) {
TestServerConsole.error(getName(), e, 0, TestServerServiceEnum.UDP_SERVICE);
}
finally {
if (channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
TestServerConsole.error(getName(), e, 2, TestServerServiceEnum.UDP_SERVICE);
}
}
}
TestServerConsole.log("NioUdpMultiServer shutdown on address: " + address, 1, TestServerServiceEnum.UDP_SERVICE);
}
/**
*
* @return
*/
public boolean getIsRunning() {
return isRunning.get();
}
/**
*
*/
public void quit() {
isRunning.set(false);
try {
TestServerConsole.log("NioUdpServer received quit command on address: " + channel.getLocalAddress(), 1, TestServerServiceEnum.UDP_SERVICE);
} catch (IOException e) {
TestServerConsole.error(getName(), e, 1, TestServerServiceEnum.UDP_SERVICE);
}
}
@Override
public String toString() {
return "NioUdpMultiClientServer [isRunning=" + isRunning + ", channel="
+ channel + ", address=" + address + ", incomingMap="
+ incomingMap + ", name=" + name + "]";
}
/**
*
* @return
*/
public String toStringExtra() {
return " \n\t Socket additional info [local addr=" + address + ", connected="
+ channel.isConnected() + ", open=" + channel.isOpen() +"]";
}
/**
*
* @return
*/
public String getName() {
return name;
}
/**
*
* @return
*/
public InetAddress getAddress() {
return address;
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.qos.testserver.udp.AbstractCandidateHandler#getSocket()
*/
@Override
public DatagramChannel getSocket() {
return channel;
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.qos.testserver.udp.AbstractUdpServer#getLocalPort()
*/
@Override
public int getLocalPort() {
return port;
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.qos.testserver.udp.AbstractUdpServer#send(java.net.DatagramPacket)
*/
@Override
public void send(DatagramPacket dp) throws IOException {
byte[] data = dp.getData();
final ByteBuffer writeBuffer = ByteBuffer.allocate(data.length);
writeBuffer.clear();
writeBuffer.put(dp.getData());
writeBuffer.flip();
TestServerConsole.log(getName() + " sending datagram: length = "
+ writeBuffer.array().length + ", to: " + dp.getSocketAddress(), 2, TestServerServiceEnum.UDP_SERVICE);
channel.send(writeBuffer, dp.getSocketAddress());
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.qos.testserver.udp.AbstractUdpServer#isHealthy()
*/
@Override
public boolean isHealthy() {
return channel != null && channel.isOpen();
}
/*
* (non-Javadoc)
* @see at.alladin.rmbt.qos.testserver.entity.Observable#getStatusMessage()
*/
@Override
public String getStatusMessage() {
return "Last client timestamp: " + DateFormat.getDateTimeInstance().format(new Date(lastClientTime));
}
}