/** * Copyright 2008 - CommonCrawl Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * **/ package org.commoncrawl.io; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.nio.channels.spi.AbstractSelectableChannel; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * * NIOTCPSocket - TCP version of NIOSocket * * @author rana * */ public class NIOClientTCPSocket extends NIOClientSocket { enum SocketType { /** outgoing socket, initiated by the client **/ Outgoing, /** incoming socket, accepted by the server **/ Incoming } public static final Log LOG = LogFactory.getLog(NIOClientTCPSocket.class); private static void probeAndSetSize(boolean sendSize, int targetSize, int minSize, SocketChannel channel) throws IOException { if (sendSize && channel.socket().getSendBufferSize() >= targetSize) { // System.out.println("SendSize is Already:" + // channel.socket().getSendBufferSize()); return; } else if (!sendSize && channel.socket().getReceiveBufferSize() >= targetSize) { // System.out.println("RcvSize is Already:" + // channel.socket().getReceiveBufferSize()); return; } do { int sizeOut = 0; if (sendSize) { channel.socket().setSendBufferSize(targetSize); sizeOut = channel.socket().getSendBufferSize(); } else { channel.socket().setReceiveBufferSize(targetSize); sizeOut = channel.socket().getReceiveBufferSize(); } if (sizeOut == targetSize) break; targetSize >>= 1; } while (targetSize > minSize); } private static void setClientSocketOptions(SocketChannel channel) throws IOException { channel.socket().setPerformancePreferences(0, 1, 3); channel.socket().setTcpNoDelay(true); channel.socket().setSoLinger(false, 0); channel.socket().setKeepAlive(true); probeAndSetSize(false, 2 << 16, 2 << 10, channel); probeAndSetSize(true, 2 << 15, 2 << 10, channel); channel.configureBlocking(false); } int _socketId = NIOSocket._idFactory.getNextId(); SocketType _socketType; SocketChannel _channel; NIOClientSocketListener _listener; InetSocketAddress _localAddress; InetSocketAddress _address; public NIOClientTCPSocket(InetSocketAddress localAddress, NIOClientSocketListener socketListener) throws IOException { _socketType = SocketType.Outgoing; _channel = SocketChannel.open(); if (localAddress != null) { InetSocketAddress modifiedLocalAddress = new InetSocketAddress(localAddress.getAddress(), 0); // System.out.println("Binding To Local Address:" + // modifiedLocalAddress.getAddress()); // LOG.info(this + "Binding To Local Address:" + // modifiedLocalAddress.getAddress()); _channel.socket().bind(modifiedLocalAddress); } _localAddress = (InetSocketAddress) _channel.socket().getLocalSocketAddress(); setClientSocketOptions(_channel); _listener = socketListener; } /** * internal constructor used to create NIOSocket objects for incoming client * connections **/ NIOClientTCPSocket(SocketChannel channel) throws IOException { _socketType = SocketType.Incoming; _channel = channel; _address = new InetSocketAddress(channel.socket().getInetAddress(), channel.socket().getPort()); } // @Override public void close() { if (_channel != null) { try { _channel.close(); } catch (IOException e) { System.out.println(e); } _channel = null; } } public void reconnect() throws IOException { if (_channel != null) { _channel.close(); connect(_address); } else { throw new IOException("Invalid State!"); } } // @Override public void connect(InetSocketAddress address) throws IOException { // System.out.println("Connecting to:"+address.getHostAddress()+" at port:"+Integer.toString(port)); if (_socketType == SocketType.Incoming) { throw new IOException("Invalid State-Connect called on an Incoming (server) Socket"); } _address = address; if (_channel == null) { throw new IOException("Invalid State- Channel Not Open during connect call"); } // LOG.info(this + "Connecting to: " + address.getAddress().getHostAddress() // + ":" + address.getPort() + " via Interface:" + // _channel.socket().getLocalAddress().getHostAddress()); _channel.connect(address); } // @Override public boolean finishConnect() throws IOException { if (_socketType == SocketType.Incoming) { throw new IOException("Invalid State-Connect called on an Incoming (server) Socket"); } if (_channel == null) { throw new IOException("Invalid State - finishConnect called on closed channel"); } try { if (_channel.finishConnect()) { _channel.socket().setKeepAlive(true); // LOG.info(this + "Connected to: " + // _address.getAddress().getHostAddress() + ":" + _address.getPort() + // " via Interface:" + // _channel.socket().getLocalAddress().getHostAddress()); return true; } } catch (IOException e) { // LOG.error("channel.finishConnect to address:" + // _address.getAddress().getHostAddress() +" port: " + _address.getPort() // + " threw exception:" + e.toString()); throw e; } return false; } // @Override public AbstractSelectableChannel getChannel() { return _channel; } // @Override public NIOSocketListener getListener() { return _listener; } public InetSocketAddress getLocalSocketAddress() throws IOException { if (_channel != null && _channel.isOpen()) { return (InetSocketAddress) _channel.socket().getLocalSocketAddress(); } return _localAddress; } // @Override public InetSocketAddress getSocketAddress() throws IOException { return _address; } // @Override public int getSocketId() { return _socketId; } // @Override public boolean isOpen() { return _channel != null; } // @Override public int read(ByteBuffer dst) throws IOException { if (_channel == null) { throw new IOException("Invalid State - read called on closed channel"); } return _channel.read(dst); } /** set the socket listener for this object */ public void setListener(NIOSocketListener listener) { _listener = (NIOClientSocketListener) listener; } // @Override public int write(ByteBuffer src) throws IOException { if (_channel == null) { throw new IOException("Invalid State - read called on closed channel"); } return _channel.write(src); } }