I'd like to set up a Java application that works as a server, accepting (blocking) queries from a Bash script. The Java part is written using nio
, and has a main loop that looks like this:
ServerSocketChannel ssc = ...; // bound to localhost:8011
Charset charset = ...;
// Waits for connections forever.
while(true) {
SocketChannel sc = ssc.accept();
StringBuffer sb = new StringBuffer();
int read = 0;
// Builds up a string representing the query.
while(true) {
ByteBuffer bb = ByteBuffer.allocate(1024);
read = sc.read(bb);
if(read == -1) break;
bb.flip();
CharBuffer cb = charset.decode(bb);
sb.append(cb.toString());
}
// Do something with the query.
sc.write(charset.encode(CharBuffer.wrap(sb.toString())));
sc.close();
}
The Bash part relies on the /dev/tcp/
magic:
exec 3<> /dev/tcp/localhost/8011
echo "message" 1>&3
I can see that the message sent from Bash does reach the Java part (if I add a System.out.println(cb);
in the inner loop, I can see the parts), but the inner loop doesn't terminate unless I kill the Bash script.
My question is really quite simple: how can the Bash script signal to the Java server that its communication has come to an end? I've tried adding
echo -en "\004" 1>&3
in my Bash script, but that didn't help.
Try closing the file descriptor. This should be seen by Java as a closed stream, and allow the inner loop to terminate.
exec 3>&-
It is possible for a socket can be "half-open" (that is, shut down in one direction but still open in the other). A Socket
instance has methods to detect this state.
I haven't tested whether the pipe-socket hybrid created by bash
supports this or not. If it doesn't, you'll have to design a protocol with some internal length-encoding or delimiting sequences to indicate message boundaries.