Search code examples
rubynode.jssocketsclient-serverunix-socket

Ruby UNIXServer with Node.js client


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

Solution

  • 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.