Index: java/main/src/main/java/com/framsticks/communication/Connection.java
===================================================================
--- java/main/src/main/java/com/framsticks/communication/Connection.java	(revision 90)
+++ java/main/src/main/java/com/framsticks/communication/Connection.java	(revision 96)
@@ -12,6 +12,4 @@
 import java.net.Socket;
 import java.net.SocketTimeoutException;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import com.framsticks.util.dispatching.AbstractJoinable;
@@ -33,6 +31,4 @@
 	protected Socket socket = null;
 
-	protected volatile boolean connected = false;
-
 	public boolean requestIdEnabled = false;
 
@@ -43,35 +39,48 @@
 	}
 	protected final String address;
+	protected final String description;
 
 	protected final Thread<Connection> senderThread = new Thread<>();
 	protected final Thread<Connection> receiverThread = new Thread<>();
-	protected final JoinableCollection<Thread<Connection>> threads = new JoinableCollection<>(true);
+	protected final JoinableCollection<Thread<Connection>> threads = new JoinableCollection<>();
+
+	protected void setUpThreadNames(String name) {
+	}
 
 	/**
 	 *
 	 */
-	public Connection(String address) {
+	public Connection(String address, String description) {
 		this.address = address;
+		this.description = description;
+		threads.setObservableName(address + " connection threads");
 		threads.add(senderThread);
 		threads.add(receiverThread);
-	}
-	public boolean isConnected() {
-		return connected;
-	}
-
-
-	protected static final String ARGUMENT_PATTERN_FRAGMENT = "((?:\\S+)|(?:\"[^\"]*\"))";
-	protected static final Pattern requestIdEnabledPattern = Pattern.compile("^\\s*([0-9]+)(?:\\s+" + ARGUMENT_PATTERN_FRAGMENT + ")?\\n$");
-	protected static final Pattern requestIDisabledPattern = Pattern.compile("^\\s*" + ARGUMENT_PATTERN_FRAGMENT + "?\\n$");
-	protected static final Pattern eventPattern = Pattern.compile("^\\s*(\\S+)\\s*(\\S+)\\n");
-
-
-	protected final Pair<Integer, String> parseRest(String rest) {
-		Matcher matcher = (requestIdEnabled ? requestIdEnabledPattern : requestIDisabledPattern).matcher(rest);
-		if (!matcher.matches()) {
-			log.fatal("unmatched first line of input: " + rest);
-			return null;
-		}
-		return new Pair<Integer, String>(requestIdEnabled ? Integer.parseInt(matcher.group(1)) : null, matcher.group(requestIdEnabled ? 2 : 1));
+
+		senderThread.setName(description + " thread " + address + " sender");
+		receiverThread.setName(description + " thread " + address + " receiver");
+	}
+
+	public synchronized boolean isConnected() {
+		return socket != null && socket.isConnected();
+	}
+
+
+	// protected static final String ARGUMENT_PATTERN_FRAGMENT = "((?:\\S+)|(?:\\\"[^\"]*\\\"))";
+	// protected static final Pattern REQUEST_ID_ENABLED_PATTERN = Pattern.compile("^\\s*([0-9]+)(?:\\s+" + ARGUMENT_PATTERN_FRAGMENT + ")?$");
+	// protected static final Pattern REQUEST_ID_DISABLED_PATTERN = Pattern.compile("^\\s*" + ARGUMENT_PATTERN_FRAGMENT + "?$");
+
+	// // protected final Pair<String, String> breakLine(String line)
+	// protected final Pair<Integer, String> parseRest(String rest) {
+	// 	Matcher matcher = (requestIdEnabled ? REQUEST_ID_ENABLED_PATTERN : REQUEST_ID_DISABLED_PATTERN).matcher(rest);
+	// 	if (!matcher.matches()) {
+	// 		log.fatal("unmatched first line of input: '" + rest + "'");
+	// 		return null;
+	// 	}
+	// 	return new Pair<Integer, String>(requestIdEnabled ? Integer.parseInt(matcher.group(1)) : null, matcher.group(requestIdEnabled ? 2 : 1));
+	// }
+
+	protected final Pair<Integer, CharSequence> takeRequestId(CharSequence line) {
+		return Request.takeRequestId(requestIdEnabled, line);
 	}
 
@@ -81,8 +90,8 @@
 	int iterator = 0;
 	int bufferStart = 0;
-	char[] readBuffer = new char[BUFFER_LENGTH];
+	final char[] readBuffer = new char[BUFFER_LENGTH];
 
 	protected String getLine() {
-		StringBuilder lineBuffer = new StringBuilder();
+		final StringBuilder lineBuffer = new StringBuilder();
 		try {
 			while (!Thread.interrupted()) {
@@ -92,10 +101,16 @@
 						continue;
 					}
-					lineBuffer.append(readBuffer, bufferStart, iterator - bufferStart + 1);
+					/** Do not append new line. */
+					lineBuffer.append(readBuffer, bufferStart, iterator - bufferStart);
 					++iterator;
 					bufferStart = iterator;
 					return lineBuffer.toString();
 				}
-				lineBuffer.append(readBuffer, bufferStart, readChars - bufferStart);
+				final int length = readChars - bufferStart;
+				if (length > 0) {
+					assert bufferStart >= 0 && bufferStart < BUFFER_LENGTH;
+					assert bufferStart + length <= BUFFER_LENGTH;
+					lineBuffer.append(readBuffer, bufferStart, length);
+				}
 
 				readChars = 0;
@@ -118,28 +133,15 @@
 	protected abstract void receiverThreadRoutine();
 
-	protected void setUpThread(Thread<Connection> thread, String name) {
-		thread.setName("connection thread " + address + " " + name);
-	}
-
-	protected void runThreads() {
+
+	protected void setupStreams() {
 		try {
 			output = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), Encoding.getFramsticksCharset()), true);
 			input = new BufferedReader(new InputStreamReader(socket.getInputStream(), Encoding.getFramsticksCharset()));
+			synchronized (this) {
+				this.notifyAll();
+			}
 		} catch (IOException e) {
-			log.error("buffer creation failure");
-			return;
+			throw new FramsticksException().msg("failed to setup streams").cause(e).arg("connection", this);
 		}
-
-		setUpThread(senderThread, "sender");
-		setUpThread(receiverThread, "receiver");
-		Dispatching.use(threads, this);
-
-		receiverThread.dispatch(new RunAt<Connection>() {
-			@Override
-			public void run() {
-				receiverThreadRoutine();
-			}
-		});
-
 	}
 
@@ -153,13 +155,4 @@
 	}
 
-	@Override
-	protected void joinableInterrupt() {
-		protocolVersion = -1;
-
-		connected = false;
-		Dispatching.drop(threads, this);
-
-		// finish();
-	}
 
 	@Override
@@ -193,7 +186,42 @@
 
 	@Override
+	public String getName() {
+		return description + " " + address;
+	}
+
+	@Override
+	protected void joinableStart() {
+		Dispatching.use(threads, this);
+
+		senderThread.dispatch(new RunAt<Connection>() {
+			@Override
+			public void run() {
+				synchronized (Connection.this) {
+					while (state.equals(JoinableState.RUNNING) && output == null) {
+						Dispatching.wait(Connection.this, 500);
+					}
+				}
+			}
+		});
+
+		receiverThread.dispatch(new RunAt<Connection>() {
+			@Override
+			public void run() {
+				receiverThreadRoutine();
+			}
+		});
+	}
+
+	@Override
+	protected void joinableInterrupt() {
+		protocolVersion = -1;
+		Dispatching.drop(threads, this);
+		finish();
+	}
+
+
+	@Override
 	protected void joinableJoin() throws InterruptedException {
 		Dispatching.join(threads);
-
 	}
 
