首页 > 解决方案 > Ruby unix 套接字客户端和服务器示例

问题描述

我发现了很多关于使用 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 来终止该套接字。

所以我在这里很迷茫。任何人都可以提供我想要的客户端和服务器脚本的工作示例吗?

标签: rubysockets

解决方案


据我所知,两者都不起作用。

这是不正确的;你的客户很好。但是,您的服务器不是。

您的客户端代码正在尝试使用 HTTP 进行通信。您的服务器代码中没有任何内容实现 HTTP 语义。

我相信发生在您身上的是:客户端连接。服务器说“Hello world”并关闭连接,就像客户端开始发送标头一样。客户发现它正在与墙壁通话,并且由于传输标头是强制性的,因此它放弃了一个错误。

这意味着,您至少必须让客户端在关闭连接之前说出 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

虽然这是超级简单的,而且不是很正确(例如,它会在 POST 和许多其他细节上失败)。你可能最好使用 Rack,它已经知道如何正确地使用 HTTP。Host如果您在其参数中提供路径,Rack 可以使用 UNIX 套接字。例如:

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

推荐阅读