/**
* This file is part of SecureNIO. Copyright (C) 2014 K. Dermitzakis
* <dermitza@gmail.com>
*
* SecureNIO is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* SecureNIO is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with SecureNIO. If not, see <http://www.gnu.org/licenses/>.
*/
package ch.dermitza.securenio;
import ch.dermitza.securenio.packet.PacketIF;
import ch.dermitza.securenio.packet.worker.AbstractPacketWorker;
import ch.dermitza.securenio.socket.PlainSocket;
import ch.dermitza.securenio.socket.SocketIF;
import ch.dermitza.securenio.socket.secure.SecureSocket;
import ch.dermitza.securenio.util.PropertiesReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.logging.Level;
import javax.net.ssl.SSLEngine;
/**
* A TCP Server implementation of {@link AbstractSelector}. This implementation
* is purely non-blocking and can handle both {@link PlainSocket}s and
* {@link SecureSocket}s.
*
* @author K. Dermitzakis
* @version 0.19
* @since 0.18
*/
public class TCPServer extends AbstractSelector {
private ServerSocketChannel ssc;
/**
* Create a TCPServer instance.
*
* @param address The address to bind to
* @param port The port to listen on
* @param packetWorker The instance of packet worker to use
* @param usingSSL Whether we are using SSL/TLS
* @param needClientAuth Whether we need clients to also authenticate
*/
public TCPServer(InetAddress address, int port,
AbstractPacketWorker packetWorker, boolean usingSSL,
boolean needClientAuth) {
super(address, port, packetWorker, usingSSL, false,
needClientAuth);
}
/**
* Send an {@link PacketIF} over the specified {@link SocketIF}.
*
* @param sc The SocketIF to send the packet through.
* @param packet The PacketIF to send through the associated SocketIF.
*
* @see AbstractSelector#send(ch.dermitza.securenio.socket.SocketIF,
* java.nio.ByteBuffer)
*/
public void send(SocketIF sc, PacketIF packet) {
send(sc, packet.toBytes());
}
/**
* Initialize a server connection. This method initializes a
* {@link ServerSocketChannel}, configures it to non-blocking, binds it to
* the specified (if any) host and port, sets the specified backlog and
* registers it with the underlying {@link java.nio.channels.Selector}
* instance with an OP_ACCEPT {@link SelectionKey}.
*
* @throws IOException Propagates all underlying IOExceptions as thrown, to
* be handled by the application layer.
*
* @see AbstractSelector#run()
*/
@Override
protected void initConnection() throws IOException {
// Create a new non-blocking server socket channel
logger.config("Creating NB ServerSocketChannel");
ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
// Bind the server socket to the specified address and port
logger.log(Level.CONFIG, "Binding ServerSocket to *:{0}", port);
InetSocketAddress isa = new InetSocketAddress(address, port);
//ssc.socket().setReuseAddress(true);
ssc.socket().bind(isa, PropertiesReader.getBacklog());
// Register the server socket channel, indicating an interest in
// accepting new connections
logger.finest("Registering ServerChannel to selector");
ssc.register(selector, SelectionKey.OP_ACCEPT);
}
/**
* Accepts incoming connections and binds new non-blocking {@link SocketIF}
* instances to them. If this server implementation is using SSL/TLS, it
* also sets up the {@link SSLEngine}, to be used.
*
* @param key The selection key with the underlying {@link SocketChannel} to
* be accepted
*
* @see AbstractSelector#run()
*/
@Override
protected void accept(SelectionKey key) {
SocketChannel socketChannel = null;
SocketIF socket = null;
String peerHost = null;
int peerPort = 0;
try {
// Accept the connection and make it non-blocking
socketChannel = ssc.accept();
socketChannel.configureBlocking(false);
socketChannel.setOption(StandardSocketOptions.TCP_NODELAY, PropertiesReader.getTCPNoDelay());
socketChannel.setOption(StandardSocketOptions.SO_SNDBUF, PropertiesReader.getSoSndBuf());
socketChannel.setOption(StandardSocketOptions.SO_RCVBUF, PropertiesReader.getSoRcvBuf());
socketChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, PropertiesReader.getKeepAlive());
socketChannel.setOption(StandardSocketOptions.SO_REUSEADDR, PropertiesReader.getReuseAddress());
socketChannel.setOption(StandardSocketOptions.IP_TOS, PropertiesReader.getIPTos());
// Register the new SocketChannel with our Selector, indicating
// we'd like to be notified when there's data waiting to be read
socketChannel.register(selector, SelectionKey.OP_READ);
// Now wrap it in our container
if (usingSSL) {
peerHost = socketChannel.socket().getInetAddress().getHostAddress();
peerPort = socketChannel.socket().getPort();
SSLEngine engine = setupEngine(peerHost, peerPort);
socket = new SecureSocket(socketChannel, engine, singleThreaded,
taskWorker, toWorker, this, this);
} else {
socket = new PlainSocket(socketChannel);
}
} catch (IOException ioe) {
logger.log(Level.INFO, "IOE accepting the connection", ioe);
// If accepting the connection failed, close the socket and remove
// any references to it
if (socket != null) {
closeSocket(socket);
}
return;
}
// Finally, add the socket to our socket container
container.addSocket(socketChannel, socket);
logger.log(Level.CONFIG, "{0}:{1} connected", new Object[]{peerHost, peerPort});
}
/**
* As this is the server implementation, it is NOT allowed to call this
* method which is only useful for client implementations. This
* implementation will throw a {@link NoSuchMethodError} if it is called and
* do nothing else.
*
* @param key The selection key to be used for connecting.
*
* @see AbstractSelector#run()
*/
@Override
protected void connect(SelectionKey key) {
throw new NoSuchMethodError("connect() is never called in client");
}
}