/******************************************************************************* * Copyright (c) 2007, 2016 Wind River Systems, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.core; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import org.eclipse.tcf.protocol.IPeer; import org.eclipse.tcf.protocol.Protocol; /** * ChannelTCP is a IChannel implementation that works on top of TCP sockets as a transport. */ public class ChannelTCP extends StreamChannel { private Socket socket; private int timeout; private InputStream inp; private OutputStream out; private boolean started; private boolean closed; private static SSLContext ssl_context; public static void setSSLContext(SSLContext ssl_context) { ChannelTCP.ssl_context = ssl_context; } /** * Main constructor of ChannelTCP * @param remote_peer Remote Peer to which we want to connect * @param host Hostname or IP Address of the Remote Peer * @param port Port of the Remote Peer * @param ssl true if the socket needs to be an SSL socket, false otherwise */ public ChannelTCP(IPeer remote_peer, final String host, final int port, final boolean ssl) { super(remote_peer); socket = new Socket(); Protocol.invokeLater(new Runnable() { public void run() { Thread thread = new Thread() { public void run() { try { socket.connect(new InetSocketAddress(host, port), timeout); socket.setTcpNoDelay(true); socket.setKeepAlive(true); if (ssl) { if (ssl_context == null) throw new Exception("SSL context is not set"); socket = ssl_context.getSocketFactory().createSocket(socket, host, port, true); ((SSLSocket)socket).startHandshake(); } inp = new BufferedInputStream(socket.getInputStream()); out = new BufferedOutputStream(socket.getOutputStream()); onSocketConnected(null); } catch (Exception x) { onSocketConnected(x); } } }; thread.setName("TCF Socket Connect"); thread.start(); } }); } /** * Constructs a non-secured ChannelTCP, i.e: the socket underlying the ChannelTCP is non-SSL socket * @param remote_peer Remote Peer to which we want to connect * @param host Hostname or IP Address of the Remote Peer * @param port Port of the Remote Peer */ public ChannelTCP(IPeer remote_peer, String host, int port) { this(remote_peer, host, port, false); } /** * Construct a non-secured ChannelTCP, i.e: the socket underlying the ChannelTCP is non-SSL socket. * @param local_peer local peer * @param remote_peer Remote Peer to which we want to connect * @param socket socket for the underlying tcp connection * @throws IOException */ public ChannelTCP(IPeer local_peer, IPeer remote_peer, Socket socket) throws IOException { super(local_peer, remote_peer); this.socket = socket; socket.setTcpNoDelay(true); socket.setKeepAlive(true); inp = new BufferedInputStream(socket.getInputStream()); out = new BufferedOutputStream(socket.getOutputStream()); onSocketConnected(null); } public void setConnectTimeout(int timeout) { this.timeout = timeout; } public void setReuseAddress(boolean on) throws SocketException { socket.setReuseAddress(on); } public void setReceiveBufferSize(int size) throws SocketException { socket.setReceiveBufferSize(size); } public void setSendBufferSize(int size) throws SocketException { socket.setSendBufferSize(size); } public void setSoLinger(boolean on, int linger) throws SocketException { socket.setSoLinger(on, linger); } public void setTrafficClass(int tc) throws SocketException { socket.setTrafficClass(tc); } public void setPerformancePreferences(int connection_time, int latency, int bandwidth) { socket.setPerformancePreferences(connection_time, latency, bandwidth); } /** * @param x exception object, or null if there was no exception creating the ChannelTCP object */ private void onSocketConnected(final Throwable x) { Protocol.invokeLater(new Runnable() { public void run() { if (x != null) { terminate(x); closed = true; } if (closed) { try { if (socket != null) { socket.close(); if (out != null) out.close(); if (inp != null) inp.close(); } } catch (IOException y) { Protocol.log("Cannot close socket", y); } } else { started = true; start(); } } }); } @Override protected final int get() throws IOException { try { if (closed) return -1; return inp.read(); } catch (IOException x) { if (closed) return -1; throw x; } } @Override protected final int get(byte[] buf) throws IOException { try { if (closed) return -1; return inp.read(buf); } catch (IOException x) { if (closed) return -1; throw x; } } @Override protected final void put(int b) throws IOException { assert b >= 0 && b <= 0xff; if (closed) return; out.write(b); } @Override protected final void put(byte[] buf) throws IOException { if (closed) return; out.write(buf); } @Override protected final void put(byte[] buf, int pos, int len) throws IOException { if (closed) return; out.write(buf, pos, len); } @Override protected final void flush() throws IOException { if (closed) return; out.flush(); } @Override protected void stop() throws IOException { closed = true; if (started) { socket.close(); /* * We should not write anything to the stream here, just close it. * So, don't call out.close(), because it calls out.flush(). * The socket output stream is already closed by socket.close(). */ inp.close(); } } }