/* * Copyright (c) 2014 tabletoptool.com team. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * rptools.com team - initial implementation * tabletoptool.com team - further development */ package com.t3.clientserver.connection; import java.io.BufferedOutputStream; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; import com.t3.clientserver.Command; import com.t3.clientserver.NetworkSerializer; public class ClientConnection extends AbstractConnection implements Closeable { private final Socket socket; private SendThread send; private ReceiveThread receive; private final String id; public ClientConnection(String host, int port, String id) throws UnknownHostException, IOException { this(new Socket(host, port), id); } public ClientConnection(Socket socket, String id) { this.socket = socket; this.id = id; } public void start() throws IOException { if (sendHandshake(socket)) { this.send = new SendThread(this, socket.getOutputStream()); this.send.start(); this.receive = new ReceiveThread(this, socket.getInputStream()); this.receive.start(); } else { socket.close(); } } /** * @throws IOException could be thrown by subclasses */ public boolean sendHandshake(Socket s) throws IOException { return true; } public String getId() { return id; } public void sendMessage(byte[] message) { sendMessage(null, message); } public void sendMessage(Object channel, byte[] message) { addMessage(channel, message); synchronized (send) { send.notify(); } } public boolean isAlive() { return !socket.isClosed(); } @Override public synchronized void close() throws IOException { if (send.stopRequested) { return; } socket.close(); send.requestStop(); receive.requestStop(); // try { // send.join(); // // receive.join(); <-- This causes a freaky error. whatever. // } catch (InterruptedException e) { // e.printStackTrace(); // } } // ///////////////////////////////////////////////////////////////////////// // send thread // ///////////////////////////////////////////////////////////////////////// private class SendThread extends Thread { private final ClientConnection conn; private final OutputStream out; private boolean stopRequested = false; public SendThread(ClientConnection conn, OutputStream out) { this.conn = conn; this.out = new BufferedOutputStream(out, 1024); } public void requestStop() { this.stopRequested = true; synchronized (this) { this.notify(); } } @Override public void run() { try { while (!stopRequested && conn.isAlive()) { try { while (conn.hasMoreMessages()) { try { byte[] message = conn.nextMessage(); if (message == null) { continue; } conn.writeMessage(out, message); } catch (IndexOutOfBoundsException e) { // just ignore and wait } } synchronized (this) { if (!stopRequested) { this.wait(); } } } catch (InterruptedException e) { // do nothing } } } catch (IOException e) { fireDisconnect(); } } } // ///////////////////////////////////////////////////////////////////////// // receive thread // ///////////////////////////////////////////////////////////////////////// private class ReceiveThread extends Thread { private final ClientConnection conn; private final InputStream in; private boolean stopRequested = false; public ReceiveThread(ClientConnection conn, InputStream in) { this.conn = conn; this.in = in; } public void requestStop() { stopRequested = true; } @Override public void run() { while (!stopRequested && conn.isAlive()) { try { byte[] message = conn.readMessage(in); conn.dispatchMessage(conn.id, message); } catch (IOException e) { fireDisconnect(); break; } catch (Throwable t) { // don't let anything kill this thread via exception t.printStackTrace(); } } } } public void callMethod(Enum<? extends Command> method, Object... parameters) { byte[] message = NetworkSerializer.serialize(method, parameters); sendMessage(message); } }