IceInternal.UdpMulticastServerTransceiver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ice-compat Show documentation
Show all versions of ice-compat Show documentation
Ice is a comprehensive RPC framework that helps you build distributed applications with minimal effort using familiar object-oriented idioms
//
// Copyright (c) ZeroC, Inc. All rights reserved.
//
package IceInternal;
import java.net.DatagramPacket;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
//
// This class is only used on Android, where the java.nio.channels.MulticastChannel interface is not supported.
//
// NOTE: Most of the important methods on java.net.MulticastSocket are synchronized.
//
final class UdpMulticastServerTransceiver implements Transceiver
{
@Override
public java.nio.channels.SelectableChannel fd()
{
//
// Android doesn't provide non-blocking APIs for UDP multicast.
//
return null;
}
@Override
public void setReadyCallback(ReadyCallback callback)
{
assert(_readyCallback == null && callback != null);
_readyCallback = callback;
//
// Start the thread only once the ready callback is set or otherwise the thread
// might start receiving datagrams but wouldn't be able to notify the thread pool.
//
_thread.start();
}
@Override
public int initialize(Buffer readBuffer, Buffer writeBuffer)
{
//
// Nothing to do.
//
return SocketOperation.None;
}
@Override
public int closing(boolean initiator, Ice.LocalException ex)
{
//
// Nothing to do.
//
return SocketOperation.None;
}
@Override
public void close()
{
Thread thread;
synchronized(this)
{
//
// Close the socket first in order to interrupt the helper thread.
//
if(_socket != null)
{
_socket.close();
_socket = null;
}
thread = _thread;
_thread = null;
}
if(thread != null)
{
try
{
thread.join();
}
catch(InterruptedException ex)
{
// Ignore.
}
}
}
@Override
public EndpointI bind()
{
//
// The constructor binds the socket so there's not much left to do.
//
_endpoint = _endpoint.endpoint(this);
return _endpoint;
}
@Override
public int write(Buffer buf)
{
//
// This transceiver can only read.
//
throw new Ice.SocketException();
}
@Override
public synchronized int read(Buffer buf)
{
if(_exception != null)
{
throw _exception;
//throw (Ice.LocalException) _exception.fillInStackTrace();
}
assert(buf.b.position() == 0);
if(!_buffers.isEmpty())
{
Buffer rb = _buffers.removeFirst();
buf.swap(rb);
buf.position(buf.b.limit());
buf.resize(buf.b.limit(), true);
if(rb.b.hasArray())
{
rb.b.clear();
_recycle.add(rb);
}
//
// The read thread will temporarily stop reading if we exceed our threshold. Wake it up if
// we've transitioned below the limit.
//
if(_buffers.size() == _threshold - 1)
{
notifyAll();
}
//
// Update our Read state to indicate whether we still have more data waiting to be read.
//
_readyCallback.ready(SocketOperation.Read, !_buffers.isEmpty());
}
return SocketOperation.None;
}
@Override
public String protocol()
{
return _instance.protocol();
}
@Override
public synchronized String toString()
{
if(_socket == null)
{
return "";
}
return "multicast address = " + Network.addrToString(_addr);
}
@Override
public String toDetailedString()
{
StringBuilder s = new StringBuilder(toString());
java.util.List intfs = Network.getInterfacesForMulticast(_mcastInterface,
Network.getProtocolSupport(_addr));
if(!intfs.isEmpty())
{
s.append("\nlocal interfaces = ");
s.append(IceUtilInternal.StringUtil.joinString(intfs, ", "));
}
return s.toString();
}
@Override
public synchronized Ice.ConnectionInfo getInfo()
{
Ice.UDPConnectionInfo info = new Ice.UDPConnectionInfo();
if(_socket != null)
{
info.localAddress = _addr.getAddress().getHostAddress();
info.localPort = _addr.getPort();
info.rcvSize = _size;
info.mcastAddress = _addr.getAddress().getHostAddress();
info.mcastPort = _addr.getPort();
}
return info;
}
@Override
public void checkSendSize(Buffer buf)
{
//
// Nothing to do.
//
}
@Override
public synchronized void setBufferSize(int rcvSize, int sndSize)
{
setBufSize(rcvSize);
}
public final int effectivePort()
{
return _addr.getPort();
}
//
// Only for use by UdpEndpointI
//
UdpMulticastServerTransceiver(UdpEndpointI endpoint, ProtocolInstance instance, InetSocketAddress addr,
String mcastInterface)
{
_endpoint = endpoint;
_instance = instance;
_mcastInterface = mcastInterface;
_addr = addr;
try
{
//
// The MulticastSocket constructor binds the socket and calls setReuseAddress(true).
//
_socket = new MulticastSocket(_addr);
//
// Obtain the local socket address (in case a system-assigned port was requested).
//
_addr = (InetSocketAddress)_socket.getLocalSocketAddress();
//
// Set the multicast group.
//
Network.setMcastGroup(_socket, _addr, _mcastInterface);
//
// Configure the receive buffer size.
//
_size = _socket.getReceiveBufferSize();
_newSize = -1;
setBufSize(-1);
if(_newSize != -1)
{
updateBufSize();
}
_thread = new Thread()
{
public void run()
{
setName("IceUDPMulticast.ReadThread");
runReadThread();
}
};
}
catch(Exception ex)
{
if(_socket != null)
{
_socket.close();
}
_socket = null;
if(ex instanceof Ice.LocalException)
{
throw (Ice.LocalException)ex;
}
else
{
throw new Ice.SocketException(ex);
}
}
}
private synchronized void exception(Ice.LocalException ex)
{
if(_exception == null)
{
_exception = ex;
}
}
private void setBufSize(int sz)
{
assert(_socket != null);
//
// Get property for buffer size if size not passed in.
//
if(sz == -1)
{
sz = _instance.properties().getPropertyAsIntWithDefault("Ice.UDP.RcvSize", _size);
}
//
// Check for sanity.
//
if(sz < (_udpOverhead + Protocol.headerSize))
{
_instance.logger().warning("Invalid Ice.UDP.RcvSize value of " + sz + " adjusted to " + _size);
}
else if(sz != _size)
{
_newSize = sz;
}
//
// Defer the actual modification of the buffer size to the helper thread.
//
}
private void updateBufSize()
{
//
// Must be called without any other threads holding the lock to the MulticastSocket!
//
try
{
//
// Try to set the buffer size. The kernel will silently adjust the size to an acceptable value.
// Then read the size back to get the size that was actually set.
//
_socket.setReceiveBufferSize(_newSize);
_size = _socket.getReceiveBufferSize();
//
// Warn if the size that was set is less than the requested size and we have not already warned.
//
if(_size < _newSize)
{
BufSizeWarnInfo winfo = _instance.getBufSizeWarn(Ice.UDPEndpointType.value);
if(!winfo.rcvWarn || winfo.rcvSize != _newSize)
{
_instance.logger().warning("UDP receive buffer size: requested size of " + _newSize +
" adjusted to " + _size);
_instance.setRcvBufSizeWarn(Ice.UDPEndpointType.value, _newSize);
}
}
}
catch(java.io.IOException ex)
{
if(_socket != null)
{
_socket.close();
}
_socket = null;
throw new Ice.SocketException(ex);
}
}
@SuppressWarnings("deprecation")
@Override
protected synchronized void finalize()
throws Throwable
{
try
{
IceUtilInternal.Assert.FinalizerAssert(_socket == null);
}
catch(java.lang.Exception ex)
{
}
finally
{
super.finalize();
}
}
private void runReadThread()
{
try
{
DatagramPacket p = null;
while(true)
{
MulticastSocket socket;
Buffer buf = null;
synchronized(this)
{
//
// If we've read too much data, wait until the application consumes some before we read again.
//
while(_socket != null && _exception == null && _buffers.size() >= _threshold)
{
try
{
wait();
}
catch(InterruptedException ex)
{
break;
}
}
if(_socket == null || _exception != null)
{
break;
}
if(_newSize != -1)
{
//
// Application must have called setBufferSize.
//
updateBufSize();
_newSize = -1;
}
socket = _socket;
if(!_recycle.isEmpty())
{
buf = _recycle.removeFirst();
}
else
{
buf = new Buffer(false);
}
buf.resize(_size, false);
}
assert(buf.b.hasArray());
if(p == null)
{
p = new DatagramPacket(buf.b.array(), buf.b.arrayOffset(), buf.b.capacity());
}
else
{
p.setData(buf.b.array(), buf.b.arrayOffset(), buf.b.capacity());
}
socket.receive(p);
if(p.getLength() > 0)
{
buf.b.limit(p.getLength());
synchronized(this)
{
_buffers.add(buf);
_readyCallback.ready(SocketOperation.Read, true);
}
}
}
}
catch(java.io.IOException ex)
{
exception(new Ice.SocketException(ex));
//
// Mark as ready for reading so that the Ice run time will invoke read() and we can report the exception.
//
_readyCallback.ready(SocketOperation.Read, true);
}
catch(Ice.LocalException ex)
{
exception(ex);
//
// Mark as ready for reading so that the Ice run time will invoke read() and we can report the exception.
//
_readyCallback.ready(SocketOperation.Read, true);
}
}
private UdpEndpointI _endpoint = null;
private ProtocolInstance _instance;
private int _size;
private int _newSize;
private MulticastSocket _socket;
private InetSocketAddress _addr;
private String _mcastInterface;
//
// The maximum IP datagram size is 65535. Subtract 20 bytes for the IP header and 8 bytes for the UDP header
// to get the maximum payload.
//
private final static int _udpOverhead = 20 + 8;
//
// The maximum number of packets that we'll queue before the read thread temporarily stops reading.
//
private final static int _threshold = 10;
private Thread _thread;
private java.util.LinkedList _buffers = new java.util.LinkedList<>();
private java.util.LinkedList _recycle = new java.util.LinkedList<>();
private Ice.LocalException _exception;
private ReadyCallback _readyCallback;
}