0

I've this Ruby server that uses a Unix Socket:

require 'socket'

server = UNIXServer.new('/tmp/ciccio.sock')
loop do
  sock = server.accept
  loop do
    begin
      data = sock.recv(1024)
      data = "DO SOMETHING -> #{data}"
      sock.write(data)
      sock.flush
    rescue Errno::EPIPE, Errno::ENOTCONN
      break
    end
  end
end

And I've this client in JavaScript that uses node.js net api:

Net = require('net');

var client = Net.connect({path: '/tmp/ciccio.sock'}, () => {
  console.log('write data');
  client.write('hello world!');
});

client.on('data', (data) => {
  console.log(data.toString());
  client.end();
});

client.on('end', () => {
  console.log('end');
});

client.on('error', (error) => {
  console.log(error.toString());
});

The problem is that at the first iteration of server loop, recv receives the right string and the server replies to the client with the string data. But at the second iteration recv receives empty data and so a infinite loop begins... server continues to receive empty data and recv does not block...

If I use a ruby client like this all works fine...

require 'socket'

sock = UNIXSocket.new('/tmp/ciccio.sock')
loop do
  sock.write('hello world!')
  data = sock.recv(1024)
  puts data
  sleep(10)
end
sock.close
stecb
  • 14,478
  • 2
  • 50
  • 68
Pioz
  • 6,051
  • 4
  • 48
  • 67
  • Why do you call `client.end();` on `data`? Should not it be called on `end` instead? – Aleksei Matiushkin Nov 25 '15 at 12:57
  • @mudasobwa I've used the example of the doc https://nodejs.org/api/net.html#net_net_connect_options_connectlistener – Pioz Nov 25 '15 at 13:00
  • The problem is that recv doesn't block, so it can only return what's available at that instant, which is sometimes nothing. W. Richard Stevens is the authority on this. Read his book – John La Rooy Nov 25 '15 at 13:21
  • 1
    @JohnLaRooy `recv` is a blocking call while `recv_nonblock` is the non-blocking variant. Of course, this assumes the file descriptor was not set with `O_NONBLOCK` prior to calling `recv`. I'm willing to bet that this is not the case and that `recv` is blocking but after the connection finishes, `recv` is being called on a closed socket and therefore will always return an empty string. – Aldehir Nov 25 '15 at 18:54

1 Answers1

2

Your client closes the connection upon connecting, writing "hello world", and then receiving a response. Now your server is calling recv on a socket that has been closed, which will return an empty string. Since you're looping to read on this socket indefinitely, it will never return control back to the outer loop to call accept for the second client. The ruby version of your client works because it never closes the connection to the server.

Breaking out of your inner loop after receiving an empty string should get you what you want,

require 'socket'

server = UNIXServer.new('/tmp/ciccio.sock')
loop do
  sock = server.accept
  loop do
    begin
      data = sock.recv(1024)
      break if data.empty?

      data = "DO SOMETHING -> #{data}"
      sock.write(data)
      sock.flush
    rescue Errno::EPIPE, Errno::ENOTCONN
      break
    end
  end
end

Another potential problem is that your server can only service a single connection to a single client, as the inner loop blocks any subsequent calls to accept until the current connection ends.

Aldehir
  • 2,025
  • 13
  • 10
  • "Now your server is calling recv on a socket that has been closed, which will return an empty string": this was the part I did not understand. Thanks! – Pioz Nov 25 '15 at 23:13