1

How to make nc client on OS X Mavericks keep socket open and read data until server disconnects the socket?

I want to send data to a server through nc and then use the response. Problem is that nc disconnects the socket after the data has been sent, without waiting for the server's response.

This will just write the data then exit:

$ echo "my instruction here" | nc 127.0.0.1 $MY_PORT

This will write the data and then not exit even though the server has disconnected:

$ cat <(echo "my instruction here") - | nc 127.0.0.1 $MY_PORT

The nc man page on OS X Mavericks have the following example for sending a HTTP GET request and printing the response, but it's not working. It just sends the request then exits without waiting for the server's response. Seems like the man page's information is incorrect?

$ echo -n "GET / HTTP/1.0\r\n\r\n" | nc host.example.com 80
1

The default behavior of nc is to close the connection if stdin is closed. To change you can use the -q option (like -q -1) on Linux, it might be the same on OS X. See the man page for details.

Edit: looks like OS X nc does not support -q switch or anything similar. In this case you need to make sure that stdin gets not closed by doing somehing like this:

( echo "foo"; sleep 100000 ) | nc ...

  • There is no -q option for the nc included with OS X – Niklas Berglund Aug 15 '14 at 20:04
  • see edited response for a workaround – Steffen Ullrich Aug 15 '14 at 20:10
  • Thanks, but still not getting any response. Tried modifying the nc man page example: (echo -n "GET / HTTP/1.0\r\n\r\n"; sleep 1000) | nc example.com 80 – Niklas Berglund Aug 16 '14 at 6:10
  • The example is wrong. To make echo interpret \r\n as CR LF you have to use the -e option for echo, i.e. echo -ne "foo\r\n" – Steffen Ullrich Aug 16 '14 at 6:16
  • Cool. With -e it's working. However I need nc to exit when the server disconnects the socket. – Niklas Berglund Aug 16 '14 at 7:05
1

As soon as the listening nc (server) gets closed, the client nc quits as well.

On Mac OS X you can open -a 'Activity Monitor' to verify that.

The reason why the nc & cat example hangs is not nc but cat that continues to keep its stdin and stdout open for reading and writing.

However, when cat tries to write to the pipe after the listening nc has been closed, SIGPIPE is raised and cat terminates.

# cat & SIGPIPE test
(sleep 1; echo hi) | 
   (trap 'echo "received signal $(($? - 128))" 1>&2' EXIT; cat) | :

kill -l 13
man bash | less -p 'EXIT STATUS'
man bash | less -Ip '128\+n'

As an alternative to the workaround by Niklas Berglund it is possible to trap the exit of the client nc to kill the whole process group including the hanging cat command.

# console 1
# ctrl-c will "kill 0" entire process group sh -c '...'
nc -l 8080  

# console 2
# script command (FreeBSD version) will implicitly run: man 2 setsid
# kill 0 will therefore only kill the entire process group sh -c '...' 
# run in a script kill 0 would otherwise kill the entire script
# cf. also https://github.com/jerrykuch/ersatz-setsid 
script -q /dev/null sh -c '
(echo "my instruction here"; cat) | (trap "kill 0" EXIT; nc 127.0.0.1 8080)
'
0

A workaround is to keep writing something to the socket - once data can't be written nc will exit.

(echo -ne "GET / HTTP/1.0\r\n\r\n"; (while true; do echo -e "\n"; sleep 1; done)) | nc example.com 80

This will send empty line to nc once every second until nc can't write to socket and nc will exit.

Thanks to Steffen Ullrich for his answer and comments that guided me to this solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.