ruby - 如何从外部停止udp_server_loop

标签 ruby sockets server udp

我用Ruby编写了小UDP服务器:

def listen
  puts "Started UDP server on #{@port}..."

  Socket.udp_server_loop(@port) do |message, message_source|
    puts "Got \"#{message}\" from #{message_source}"

    handle_incoming_message(message)
  end
end

我在一个单独的线程中启动它:

thread = Thread.new { listen }

有没有一种方法可以从线程外部优雅地停止udp_server_loop而不只是杀死它(thread.kill)?我也不想通过接收任何UDP消息从内部停止它。 udp_server_loop可能不是适合我的工具吗?

最佳答案

我认为您无法使用udp_server_loop做到这一点(尽管您也许可以使用它使用的某些方法)。您将不得不在自己的循环中调用 IO::select ,并用某种信号通知它退出,并通过某种方式唤醒线程,这样就不必发送数据包来停止它了。

一种简单的方法是使用select的timeout选项和一个变量来设置,以指示您希望线程结束,例如:

@halt_loop = false

def listen
  puts "Started UDP server on #{@port}..."
  sockets = Socket.udp_server_sockets(@port)

  loop do
    readable, _, _ = IO.select(sockets, nil, nil, 1) # timeout 1 sec
    break if @halt_loop
    next unless readable # select returns nil on timeout

    Socket.udp_server_recv(readable) do |message, message_source|
      puts "Got \"#{message}\" from #{message_source}"

      handle_incoming_message(message)
    end
  end
end

然后,当您要停止线程时,可以将@halt_loop设置为true。

不利的一面是它正在有效地轮询。如果减少超时,则可能在空循环上执行更多工作,而如果增加超时,则在停止线程时必须等待更长的时间。

另一个稍微复杂一点的解决方案是使用 pipe 并使select与套接字一起监听。然后,您可以直接发出信号以完成select并退出线程。
@read, @write = IO.pipe
@halt_loop = false

def listen
  puts "Started UDP server on #{@port}..."
  sockets = Socket.udp_server_sockets(@port)
  sockets << @read

  loop do
    readable, _, _ = IO.select(sockets)
    break if @halt_loop

    readable.delete @read

    Socket.udp_server_recv(readable) do |message, message_source|
      puts "Got \"#{message}\" from #{message_source}"

      handle_incoming_message(message)
    end
  end
end

def end_loop
  @halt_loop = true
  @write.puts "STOP!"
end

要退出线程,您只需要调用end_loop即可,该命令会设置@halt_loop标志,然后将其写入管道,使另一端可读,并使另一个线程从select返回。

您可以让此代码检查可读的IO,如果其中之一是管道的读取端而不是使用变量,则退出,但至少在Linux上存在潜在的错误,其中select调用可能返回文件描述符为实际不可读时可读。我不知道Ruby是否可以解决这个问题,所以安全胜过遗憾。

另外,在将管道传递给readable之前,请确保从udp_server_recv数组中删除管道。它不是套接字,如果不这样做,将导致异常。

这种技术的缺点是管道“在所有平台上都不可用”。

关于ruby - 如何从外部停止udp_server_loop,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57575662/

相关文章:

html - 如何更改 Ruby on Rails 中特定页面的 css

sql - Rails .where() 查询不工作

java - Java TCP文件传输仅在第一次尝试时完成

html - 我可以在 Windows Phone 8 应用程序中使用 Node.js 吗?

ruby - 按键排序散列,在 Ruby 中返回散列

ruby-on-rails - Factory Girl 序列在 autospec 下失败

c - 服务器上的多个客户端

java - Netty IO发送消息给客户端

web - 尊重 cloudflare 中的语言标题

django - 关于 wsgi.py 中 wsgi 模块的 Ubuntu 16.04 服务器错误