public void receiveLoop() {
		boolean canSend = true;
		try {
			while (true) {
				RootMessage req = receive();
				if (req == null) {
					// Either normal or abnormal closure
					return;
				}

				RootMessage rep = dispatchNominal.handle(req);
				if (rep == null) {
					// Handler did not generate a response
					continue;
				}

				/**
				 * The likely cause of this failing is that the remote end has closed the socket.
				 * However, we don't return, because there may be commands still in the queue, and
				 * we should process them until we reach the end of input. This will ensure clients
				 * that brazenly send a bunch of commands and then disconnect before receiving the
				 * replies will have their commands processed, even if unsuccessfully.
				 */
				if (canSend) {
					canSend = send(rep);
				}
			}
		}
		finally {
			try {
				dispose();
			}
			catch (IOException e) {
				Msg.error(this, "Could not close socket after error", e);
			}
		}
	}
Create a handler

<p>
Note it is common for this to be constructed by a TCP <em>client</em>.

@param plugin the Trace RMI plugin
@param socket the socket to the back-end debugger
@throws IOException if there is an issue with the I/O streams
Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiHandler.java