Ruby unix примеры клиентских и серверных сокетов - PullRequest
0 голосов
/ 06 января 2020

Я нашел много общей информации о работе сервера с использованием сокетов unix, но я не могу заставить его работать. Вот все, что я хочу сделать. Сервер создает сокет unix, а затем прослушивает его. Когда приходит запрос, он обрабатывает запрос и отправляет ответ. На данный момент не имеет значения, когда запрос или какая обработка происходит. «Привет, мир» пока подойдет.

Вот сервер и клиентские скрипты, которые у меня сейчас есть. Насколько я могу судить, ни один из них не работает.

Сервер:

#!/usr/bin/ruby -w
require 'socket'

server = UNIXServer.new('/tmp/socket-simple')
socket = server.accept
socket.write 'Hello world'
socket.close
server.close

Клиент:

#!/usr/bin/ruby -w
require 'net_http_unix'

request = Net::HTTP::Get.new('whatever')
client = NetX::HTTPUnix.new('unix://' + '/tmp/socket-simple')
response = client.request(request)
puts response.body

Я ожидаю, что клиент отправит этот простой запрос возвращает «Hello world» и отображает этот ответ. Вот что на самом деле происходит.

Клиентский скрипт выводит эту ошибку:

Traceback (most recent call last):
    16: from ./socket-client.rb:8:in `<main>'
    15: from /usr/lib/ruby/2.5.0/net/http.rb:1458:in `request'
    14: from /usr/lib/ruby/2.5.0/net/http.rb:910:in `start'
    13: from /usr/lib/ruby/2.5.0/net/http.rb:1460:in `block in request'
    12: from /usr/lib/ruby/2.5.0/net/http.rb:1467:in `request'
    11: from /usr/lib/ruby/2.5.0/net/http.rb:1493:in `transport_request'
    10: from /usr/lib/ruby/2.5.0/net/http.rb:1536:in `begin_transport'
    9: from /var/lib/gems/2.5.0/gems/net_http_unix-0.2.2/lib/net_x/http_unix.rb:24:in `connect'
    8: from /var/lib/gems/2.5.0/gems/net_http_unix-0.2.2/lib/net_x/http_unix.rb:35:in `connect_unix'
    7: from /usr/lib/ruby/2.5.0/timeout.rb:108:in `timeout'
    6: from /usr/lib/ruby/2.5.0/timeout.rb:33:in `catch'
    5: from /usr/lib/ruby/2.5.0/timeout.rb:33:in `catch'
    4: from /usr/lib/ruby/2.5.0/timeout.rb:33:in `block in catch'
    3: from /usr/lib/ruby/2.5.0/timeout.rb:93:in `block in timeout'
    2: from /var/lib/gems/2.5.0/gems/net_http_unix-0.2.2/lib/net_x/http_unix.rb:35:in `block in connect_unix'
    1: from /var/lib/gems/2.5.0/gems/net_http_unix-0.2.2/lib/net_x/http_unix.rb:35:in `open'
/var/lib/gems/2.5.0/gems/net_http_unix-0.2.2/lib/net_x/http_unix.rb:35:in `initialize': Connection refused - connect(2) for /tmp/socket-simple (Errno::ECONNREFUSED)

Сервер работает нормально, за исключением того, что он оставляет файл сокета unix открытым. Я могу убить этот сокет, удалив /tmp/socket-simple.

Так что я довольно растерялся здесь. Кто-нибудь может предоставить мне рабочие примеры клиентских и серверных скриптов, которые делают то, что я хочу?

Ответы [ 2 ]

0 голосов
/ 06 января 2020

Насколько я могу судить, ни один из них не работает.

Это неверно; с твоим клиентом все в порядке. Ваш сервер, однако, не является.

Ваш код клиента пытается связаться по HTTP. Ничто в коде вашего сервера не реализует семантику HTTP.

Я верю, что с вами происходит следующее: клиент подключается. Сервер говорит «Привет, мир» и закрывает соединение, как только Клиент начинает отправлять заголовок. Клиент обнаруживает, что он говорит со стеной, и поскольку передача заголовка является обязательной, он сдается с ошибкой.

Это означает, что вы должны по крайней мере позволить клиенту произнести заголовок HTTP, прежде чем закрыть соединение , Вы также должны отправить заголовок ответа HTTP, иначе клиент HTTP окажется в замешательстве.

Вот минимальный код сервера, который мне подходит:

#!/usr/bin/ruby -w
require 'socket'

begin
  server = UNIXServer.new('/tmp/socket-simple')
  socket = server.accept

  # read the headers, at least
  while socket.gets.chomp != ""
  end

  # send the HTTP response header
  socket.puts "HTTP/1.1 200 OK"
  socket.puts "Content-Type: text/plain"
  socket.puts

  # only now can you send a response body
  socket.puts 'Hello world'
  socket.close
  server.close
ensure
  File.unlink('/tmp/socket-simple')
end

Это очень упрощенно c хотя и не очень корректно (например, при POST произойдет сбой и многие другие детали). Возможно, вам лучше использовать Rack, который уже знает, как правильно говорить по HTTP. Rack может использовать сокет UNIX, если вы указали путь в аргументе Host. Например:

require 'rack'
require 'thin'

class TestApp
  def call(env)
    [200, {"Content-Type" => "text/plain"}, ["Hello World!"]]
  end
end

app = TestApp.new
Rack::Handler.get('thin').run(app, Host: '/tmp/socket-simple')

РЕДАКТИРОВАТЬ: Вот версия без зависимости с использованием WEBrick. WEBrick не поддерживает UNIX сокетов напрямую, поэтому мы должны предотвратить попытки его прослушивания и вставить наш собственный слушатель:

require 'webrick'
require 'socket'

begin
  UNIXServer.open('/tmp/socket-simple') do |ssocket|
    server = WEBrick::HTTPServer.new(DoNotListen: true)
    server.listeners << ssocket

    server.mount_proc '/' do |req, res|
      res.set_content_type('text/plain')
      res.body = "Hello, world!"
    end

    server.start
  end
ensure
  File.unlink('/tmp/socket-simple')
end
0 голосов
/ 06 января 2020

Я не уверен, что такое net_http_unix и зачем вам приносить сторонние решения, когда все для выполнения sh есть задача в ядре.

Socket ruby документация. Вот копия-вставка оттуда.

# echo server
Socket.unix_server_loop("/tmp/sock") do |sock, _client|
  begin      
    IO.copy_stream(sock, sock)          
  ensure          
    sock.close          
  end          
end

# client (another terminal)
Socket.unix("/tmp/sock") do |sock|
  t = Thread.new { IO.copy_stream(sock, STDOUT) }  
  IO.copy_stream(STDIN, sock)  
  t.join  
end

Во втором терминале вы можете напечатать что угодно, и оно будет отображено. Также вы можете поместить выходные данные отладки в блок сервера для отслеживания обмена.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...