Crystal: What wrong with unix sockets?

Created on 12 Apr 2017  路  4Comments  路  Source: crystal-lang/crystal

Code from here - https://crystal-lang.org/api/0.21.1/UNIXServer.html

require "socket"

server = UNIXServer.new("/tmp/myapp.sock")
message = server.gets
server.puts message

Error:

laptop% ~/crystal-0.21.1-1/bin/crystal run ./test.cr
Error reading file: Invalid argument (Errno)
0x449c4f: *UNIXServer at /home/sorcus/crystal-0.21.1-1/src/io/file_descriptor.cr 249:9
0x449f46: *UNIXServer at /home/sorcus/crystal-0.21.1-1/src/io/buffered.cr 208:12
0x44a024: *UNIXServer at /home/sorcus/crystal-0.21.1-1/src/io/buffered.cr 84:7
0x44966f: *UNIXServer at /home/sorcus/crystal-0.21.1-1/src/io.cr 700:37
0x44947f: *UNIXServer at /home/sorcus/crystal-0.21.1-1/src/io.cr 674:5
0x449441: *UNIXServer at /home/sorcus/crystal-0.21.1-1/src/io.cr 644:5
0x44941f: *UNIXServer at /home/sorcus/crystal-0.21.1-1/src/io.cr 642:3
0x42af60: ??? at /home/sorcus/test.cr 4:11
0x430d39: main at /home/sorcus/crystal-0.21.1-1/src/main.cr 12:15
0x7f412700c511: __libc_start_main at ??
0x42a87a: _start at ??
0x0: ??? at ??
bug stdlib

Most helpful comment

I also think it's a mistake that TCPServer and UnixServer inherit Socket and that they are IOs, because you can't read/write from them, only accept connections, so the above should simply not even compile.

All 4 comments

Right, the example cannot work, because we can't simply read from a UNIXServer, the server needs to accept a client, then use this client to interact, like:

server = UNIXServer.new("/tmp/myapp.sock")

puts "waiting for client on /tmp/myapp.sock"
client = server.accept

message = client.gets
client.puts message

server.close

Then using another program, you can interact with the UNIXServer with a UNIXSocket:

client = UNIXSocket.new("/tmp/myapp.sock")
client.puts "Hello world"

puts "server replied"
puts client.gets

We'll need to change the documentation (and some other things too, @ysbaddaden )

I also think it's a mistake that TCPServer and UnixServer inherit Socket and that they are IOs, because you can't read/write from them, only accept connections, so the above should simply not even compile.

An experiment based on @bew comment:

require "socket"

server = UNIXServer.new("/tmp/myapp.sock")
CLIENTS = [] of UNIXSocket

Signal::INT.trap {
  puts "closing server..."
  server.close
  exit
}

def on_message(client)
  loop {
    msg = client.gets
    if msg == "quit"
      puts "deleting client..."
      CLIENTS.delete client
      pp CLIENTS.size
      break
    else
      yield msg
    end
  }
end

loop {
  puts "waiting for client on /tmp/myapp.sock"
  new_client = server.accept
  spawn {
    on_message(new_client) {|msg|
      CLIENTS.each {|client|
        client.puts msg unless client == new_client
      }
    }
  }
  CLIENTS << new_client
  pp CLIENTS.size
}
require "socket"

client = UNIXSocket.new("/tmp/myapp.sock")

Signal::INT.trap {
  client.puts "quit"
  exit
}

print "name: "
name = gets.to_s

spawn {
  loop {
    msg = client.gets
    if msg
      puts msg
    else
      puts "server closed"
      exit
    end
  }
}

loop {
  msg = gets.to_s
  client.puts "#{name}: #{msg}"
}
$ crystal socket_server.cr
waiting for client on /tmp/myapp.sock
CLIENTS.size # => 1
waiting for client on /tmp/myapp.sock
CLIENTS.size # => 2
waiting for client on /tmp/myapp.sock
$ crystal socket_client.cr
name: program1
hi, I'm p1
program2: Hi p1!!!
$ crystal socket_client.cr
name: program2
program1: hi, I'm p1
Hi p1!!!

Happy crystalling :smile:

@asterite well, TCP and UNIX servers are TCP and UNIX sockets, though they won't act as IO, but then: UDP sockets are both server/client...

Was this page helpful?
0 / 5 - 0 ratings