/** * Copyright (C) 2009-2014 Cars and Tracks Development Project (CTDP). * * 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 2 * 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package net.ctdp.rfdynhud.plugins.datasender; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; /** * Connects via a socket using TCP and sends/receives data (server side). * * @author Marvin Froehlich (CTDP) */ public abstract class AbstractTCPServerCommunicator extends AbstractServerCommunicator implements Runnable { private final int port; private ServerSocket serverSocket = null; private volatile boolean running = false; private volatile boolean connected = false; private volatile boolean restart = true; private volatile boolean closeRequested = false; private volatile boolean waitingForConnection = false; private boolean commandInProgress = false; private short currentCommand = 0; private boolean commandEnded = false; @Override public final boolean isRunning() { return ( running ); } @Override protected void startCommandImpl( short code ) { synchronized ( eventsBuffer ) { if ( commandInProgress ) throw new IllegalStateException( "Another command (" + currentCommand + ") has been started, but not ended." ); currentCommand = code; commandInProgress = true; try { eventsBuffer.writeShort( code ); } catch ( IOException e ) { log( e ); } } } @Override protected void endCommandImpl() { synchronized ( eventsBuffer ) { if ( !commandInProgress ) throw new IllegalStateException( "No command had been started." ); currentCommand = 0; commandInProgress = false; commandEnded = true; } } @Override public void run() { running = true; closeRequested = false; Socket socket = null; DataInputStream in = null; OutputStream out = null; try { if ( serverSocket == null ) { serverSocket = new ServerSocket( port ); serverSocket.setReuseAddress( true ); } waitingForConnection = true; socket = serverSocket.accept(); waitingForConnection = false; in = new DataInputStream( new BufferedInputStream( socket.getInputStream() ) ); out = socket.getOutputStream(); } catch ( Throwable t ) { running = false; log( t ); return; } //connected = true; while ( running && socket.isConnected() && !socket.isClosed() && !socket.isInputShutdown() && !socket.isOutputShutdown() ) { synchronized ( eventsBuffer ) { if ( eventsBuffer0.size() > 0 ) { try { eventsBuffer0.writeTo( out ); if ( commandEnded ) out.flush(); eventsBuffer0.reset(); } catch ( SocketException e ) { if ( !closeRequested ) { log( "Connection closed unexpectedly" ); log( e ); close( true ); } break; } catch ( IOException e ) { log( e ); } } else if ( commandEnded ) { try { out.flush(); } catch ( SocketException e ) { if ( !closeRequested ) { log( "Connection closed unexpectedly" ); log( e ); close( true ); } break; } catch ( IOException e ) { log( e ); } } } if ( closeRequested ) { try { running = false; out.write( ( CONNECTION_CLOSED >>> 8 ) & 0xFF ); out.write( ( CONNECTION_CLOSED >>> 0 ) & 0xFF ); } catch ( SocketException e ) { break; } catch ( IOException e ) { log( e ); } } try { if ( in.available() >= 2 ) { //plugin.debug( "in.available: ", in.available() ); readInput( in ); } } catch ( SocketException e ) { if ( !closeRequested ) { log( "Connection closed unexpectedly" ); log( e ); close( true ); } break; } catch ( IOException e ) { log( e ); } try { Thread.sleep( 10L ); } catch ( InterruptedException e ) { log( e ); } } running = false; connected = false; closeRequested = false; try { socket.close(); socket = null; } catch ( IOException e ) { log( e ); } synchronized ( eventsBuffer ) { eventsBuffer0.reset(); } onConnectionClosed(); if ( restart ) { try { Thread.sleep( 200L ); } catch ( InterruptedException e ) { } new Thread( this ).start(); } } @Override public void connect() { if ( running ) return; new Thread( this ).start(); } protected void beforeClosed() { } protected void closeWaitingSocket() { try { // Create dummy connection to close the waiting socket. Socket socket2 = new Socket( "localhost", port ); socket2.close(); } catch ( IOException e ) { log( e ); } } @Override protected final void close( boolean restart ) { if ( connected ) { beforeClosed(); closeRequested = true; } else { running = false; } this.restart = restart; if ( waitingForConnection ) { closeWaitingSocket(); } } public AbstractTCPServerCommunicator( int port, String password ) { super( password ); this.port = port; } }