The solution I found:
public class RemoteConnection extends Observable {
private static Logger log = Logger.getLogger(RemoteConnection.class.getName());
private final Socket socket;
private final BufferedInputStream in;
private final BufferedOutputStream out;
private final static String UTF8 = "UTF-8";
public RemoteConnection(String host, int port) throws UnknownHostException, IOException {
socket = new Socket(host, port);
in = new BufferedInputStream(socket.getInputStream());
out = new BufferedOutputStream(socket.getOutputStream());
}
public void write(Deque<String> commands) throws IOException {
String command;
while (!commands.isEmpty()) {
command = commands.pop();
out.write(command.concat("\r\n").getBytes(Charset.forName(UTF8)));
log.info(command);
}
out.flush();
}
void read() { //probably should use BufferedStream to better effect..?
Thread readRemote = new Thread() {
@Override
public void run() {
StringBuilder sb = new StringBuilder();
char ch;
int i;
while (true) {
try {
i = in.read();
ch = (char) i;
sb.append(ch);
System.out.print(ch);
if (i == 13) {
setChanged();
notifyObservers(sb.toString());
log.fine(sb.toString());
sb = new StringBuilder();
}
} catch (IOException ioe) {
log.fine(ioe.toString());
}
}
}
};
readRemote.start();
}
}
By reorganizing the threading, this approximates a poor-mans telnet, with asynchronous threads for i/o. I think reading from the console was blocking...something...
I really don't know why this works but the other approaches didn't. I would've preferred to have the main class start and handle threads, and pass references between the threads, but that just didn't work despite using the various solutions offered here.
LocalConnection has a similar threading approach.