com.oracle.coherence.common.net.Sockets Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of coherence Show documentation
Show all versions of coherence Show documentation
Oracle Coherence Community Edition
/*
* Copyright (c) 2000, 2020, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
package com.oracle.coherence.common.net;
import com.oracle.coherence.common.internal.net.InterruptibleChannels;
import java.io.IOException;
import java.net.SocketException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.DatagramSocket;
import java.net.MulticastSocket;
import java.net.SocketOptions;
import java.nio.channels.SelectableChannel;
import java.util.logging.Logger;
import java.util.logging.Level;
/**
* Sockets provides static helper methods related to sockets.
*
* @author mf 2010.12.14
*/
public final class Sockets
{
/**
* Apply the specified options to a socket.
*
* @param socket the socket to configure
* @param options the options to apply, or null for none
*
* @throws SocketException if an I/O error occurs
*/
public static void configure(ServerSocket socket, SocketOptions options)
throws SocketException
{
if (options == null || socket.isClosed())
{
return;
}
try
{
Object oOption;
if (!socket.isBound() &&
(oOption = options.getOption(SocketOptions.SO_REUSEADDR)) != null)
{
socket.setReuseAddress((Boolean) oOption);
}
if ((oOption = options.getOption(SocketOptions.SO_RCVBUF)) != null)
{
int cb = (Integer) oOption;
socket.setReceiveBufferSize(cb);
int cbReal = socket.getReceiveBufferSize();
if (cbReal < cb)
{
warnBufferSize(socket, "receive", cb, cbReal);
}
}
if ((oOption = options.getOption(SocketOptions.SO_TIMEOUT)) != null)
{
socket.setSoTimeout((Integer) oOption);
}
}
catch (SocketException e)
{
if (socket.isClosed())
{
return;
}
throw e;
}
}
/**
* Apply the specified options to a socket.
*
* @param socket the socket to configure
* @param options the options to apply, or null for none
*
* @throws SocketException if an I/O error occurs
*/
public static void configure(Socket socket, SocketOptions options)
throws SocketException
{
if (options == null || socket.isClosed())
{
return;
}
try
{
Object oOption;
if (!socket.isBound() &&
(oOption = options.getOption(SocketOptions.SO_REUSEADDR)) != null)
{
boolean fReuse = (Boolean) oOption;
if (fReuse != socket.getReuseAddress())
{
warnReuseAddr(fReuse);
}
socket.setReuseAddress((Boolean) oOption);
}
if ((oOption = options.getOption(SocketOptions.SO_RCVBUF)) != null)
{
int cb = (Integer) oOption;
socket.setReceiveBufferSize(cb);
int cbReal = socket.getReceiveBufferSize();
if (cbReal < cb)
{
warnBufferSize(socket, "receive", cb, cbReal);
}
}
if ((oOption = options.getOption(SocketOptions.SO_SNDBUF)) != null)
{
int cb = (Integer) oOption;
socket.setSendBufferSize(cb);
int cbReal = socket.getSendBufferSize();
if (cbReal < cb)
{
warnBufferSize(socket, "send", cb, cbReal);
}
}
if ((oOption = options.getOption(SocketOptions.SO_TIMEOUT)) != null)
{
socket.setSoTimeout((Integer) oOption);
}
if ((oOption = options.getOption(SocketOptions.SO_LINGER)) != null)
{
socket.setSoLinger(true, (Integer) oOption);
}
if ((oOption = options.getOption(SocketOptions.SO_KEEPALIVE)) != null)
{
socket.setKeepAlive((Boolean) oOption);
}
if ((oOption = options.getOption(SocketOptions.TCP_NODELAY)) != null)
{
socket.setTcpNoDelay((Boolean) oOption);
}
if ((oOption = options.getOption(SocketOptions.IP_TOS)) != null)
{
socket.setTrafficClass((Integer) oOption);
}
}
catch (SocketException e)
{
if (socket.isClosed())
{
return;
}
try
{
// on some OSs (OSX) you can't set options if the remote end
// has closed the socket. This isn't an easily detectable state
// but we try by attempting to simply set some random option to its
// current value.
socket.setKeepAlive(socket.getKeepAlive());
}
catch (SocketException e2)
{
return;
}
throw e;
}
}
/**
* Apply the specified options to a socket.
*
* @param socket the socket to configure
* @param options the options to apply, or null for none
*
* @throws SocketException if an I/O error occurs
*/
public static void configure(DatagramSocket socket, SocketOptions options)
throws SocketException
{
if (options == null || socket.isClosed())
{
return;
}
try
{
Object oOption;
if (!socket.isBound() &&
(oOption = options.getOption(SocketOptions.SO_REUSEADDR)) != null)
{
boolean fReuse = (Boolean) oOption;
if (fReuse != socket.getReuseAddress())
{
warnReuseAddr(fReuse);
}
socket.setReuseAddress((Boolean) oOption);
}
if ((oOption = options.getOption(SocketOptions.SO_RCVBUF)) != null)
{
int cb = (Integer) oOption;
socket.setReceiveBufferSize(cb);
int cbReal = socket.getReceiveBufferSize();
if (cbReal < cb)
{
warnBufferSize(socket, "receive", cb, cbReal);
}
}
if ((oOption = options.getOption(SocketOptions.SO_SNDBUF)) != null)
{
int cb = (Integer) oOption;
socket.setSendBufferSize(cb);
int cbReal = socket.getSendBufferSize();
if (cbReal < cb)
{
warnBufferSize(socket, "send", cb, cbReal);
}
}
if ((oOption = options.getOption(SocketOptions.SO_TIMEOUT)) != null)
{
socket.setSoTimeout((Integer) oOption);
}
}
catch (SocketException e)
{
if (socket.isClosed())
{
return;
}
throw e;
}
}
/**
* Apply the specified options to a socket.
*
* @param socket the socket to configure
* @param options the options to apply, or null for none
*
* @throws SocketException if an I/O error occurs
*/
public void configure(MulticastSocket socket, SocketOptions options)
throws SocketException
{
configure((DatagramSocket) socket, options);
}
/**
* Update the Socket's blocking (and possibly interruptible mode).
*
* When set via this method a non-blocking socket will also be made non-interruptible if possible, and
* interruptible when set to blocking if possible.
*
* Changing the interrupt mode on a socket may not be possible in all JVMs, especially if Java
* security has been enabled. In such cases the Socket will retain its default behavior of always
* being interruptible.
*
* The benefit of making a non-blocking Socket non-interruptible is that it prevents the socket from
* being arbitrarily closed when accessed from a interrupted thread.
*
* Note: This is a work around for JDK-6908931
* Note: As of Java 9 this work around is no longer functional.
*
* @param chan the channel
* @param fBlocking the blocking/interrutible mode
*
* @return true if both modes were set, false if only the blocking mode was set
*
* @throws java.io.IOException if an IO error occurs
*/
public static boolean configureBlocking(SelectableChannel chan, boolean fBlocking)
throws IOException
{
synchronized (chan.blockingLock())
{
chan.configureBlocking(fBlocking);
return InterruptibleChannels.setInterruptible(chan, fBlocking);
}
}
/**
* Return the MTU for the local NIC associated this socket.
*
* @param socket the socket
* @return the MTU
*/
public static int getMTU(Socket socket)
{
int nMtu = 0;
try
{
nMtu = InetAddresses.getLocalMTU(socket.getLocalAddress());
}
catch (Throwable e)
{
// at least on OSX we've seen that calling getLocalAddress on a socket which is
// concurrently being closed can yield this Error. As getLocalAddress is not
// allowed to throw SocketException, it is surfaced as an Error, which is in
// and of itself wrong.
// fall through
}
return nMtu == 0 ? InetAddresses.getLocalMTU() : nMtu;
}
// ----- helpers --------------------------------------------------------
/**
* Issue a warning regarding an undersized socket buffer.
*
* @param socket the socket
* @param sBuffer the buffer description
* @param cb the requested size
* @param cbReal the actual size
*/
protected static void warnBufferSize(Object socket, String sBuffer,
int cb, int cbReal)
{
LOGGER.log(Level.INFO,
"Failed to set the " + sBuffer + " buffer size on " + socket +
" to " + cb + " bytes; actual size is " + cbReal + " bytes." +
" Consult your OS documentation regarding increasing the" +
" maximum socket buffer size. Proceeding with the actual value" +
" may cause sub-optimal performance.");
}
/**
* Issue a warning regarding overrideing SO_REUSEADDR
*
* @param fReuse the specified setting
*/
protected static void warnReuseAddr(boolean fReuse)
{
if (s_fWarnReuseAddr)
{
s_fWarnReuseAddr = false; // only log this once per JVM
LOGGER.log(Level.WARNING,
"The value of SO_REUSEADDR is being overriden to " +
fReuse + " from the system default; this setting is not " +
"portable and may result in differeing behavior across " +
"environments.");
}
}
// ----- static data members --------------------------------------------
/**
* Tracks if the JVM has already warned about explicitly setting re-use
* addr.
*/
private static boolean s_fWarnReuseAddr = true;
// ----- constants ------------------------------------------------------
/**
* The Sockets class logger.
*/
private static Logger LOGGER = Logger.getLogger(Sockets.class.getName());
}