如何阻止读取 Ruby 中的命名管道?

2024-01-06

我正在尝试设置一个 Ruby 脚本,该脚本在循环中从命名管道读取数据,并阻塞直到管道中的输入可用。

我有一个进程定期将调试事件放入命名管道中:

# Open the logging pipe
log = File.open("log_pipe", "w+") #'log_pipe' created in shell using mkfifo
...
# An interesting event happens
log.puts "Interesting event #4291 occurred"
log.flush
...

然后,我想要一个单独的进程,该进程将从该管道读取事件并在事件发生时将其打印到控制台。我尝试过使用这样的代码:

input = File.open("log_pipe", "r+") 
while true
  puts input.gets  #I expect this to block and wait for input
end
# Kill loop with ctrl+c when done

我想要input.gets阻塞,耐心等待,直到新输入到达 fifo;但它立即读取nil并再次循环,滚动到控制台窗口的顶部。

我尝试过的两件事:

  1. 我已经用“r”和“r+”打开了输入fifo——无论哪种方式我都有同样的问题;

  2. 我试图确定我的写入过程是否正在发送 EOF(我听说这会导致读取 fifo 关闭)——据我所知,事实并非如此。

一些背景:

如果有帮助,这里是我正在尝试做的事情的“大局”视图:

我正在开发一款在 RGSS(一种基于 Ruby 的游戏引擎)中运行的游戏。由于它没有良好的集成调试功能,我想在游戏运行时设置实时日志——当游戏中发生事件时,我希望消息显示在侧面的控制台窗口中。我可以使用类似于上面编写器代码的代码将 Ruby 游戏代码中的事件发送到命名管道;我现在正在尝试设置一个单独的进程,该进程将等待事件出现在管道中,并在事件到达时将其显示在控制台上。我什至不确定我是否需要 Ruby 来做到这一点,但这是我能想到的第一个解决方案。

请注意,我正在使用mkfifo来自 cygwin,我碰巧安装了它;我想知道这是否是我麻烦的根源。

如果它对任何人有帮助,这正是我在 irb 中通过“阅读器”流程看到的内容:

irb(main):001:0> input = File.open("mypipe", "r")
=> #<File:mypipe>
irb(main):002:0> x = input.gets
=> nil
irb(main):003:0> x = input.gets
=> nil

我不期望input.gets002 和 003 立即返回——我希望他们会阻止。


我找到了一个解决方案,可以完全避免使用 Cygwin 不可靠的命名管道实现。 Windows 有自己的命名管道工具,甚至还有一个名为win32管道 http://rubygems.org/gems/win32-pipe使用它的。

不幸的是,似乎无法在 RGSS 脚本中使用 Ruby Gems;但通过剖析 win32-pipe gem,我能够将相同的想法融入到 RGSS 游戏中。此代码是将游戏事件实时记录到后台通道所需的最低限度的代码,但它对于深度调试非常有用。

我在“Main”之前添加了一个新的脚本页面,并添加了以下内容:

module PipeLogger
  # -- Change THIS to change the name of the pipe!
  PIPE_NAME = "RGSSPipe"

  # Constant Defines
  PIPE_DEFAULT_MODE        = 0            # Pipe operation mode
  PIPE_ACCESS_DUPLEX       = 0x00000003   # Pipe open mode
  PIPE_UNLIMITED_INSTANCES = 255          # Number of concurrent instances
  PIPE_BUFFER_SIZE         = 1024         # Size of I/O buffer (1K)
  PIPE_TIMEOUT             = 5000         # Wait time for buffer (5 secs)
  INVALID_HANDLE_VALUE     = 0xFFFFFFFF   # Retval for bad pipe handle

  #-----------------------------------------------------------------------
  # make_APIs
  #-----------------------------------------------------------------------
  def self.make_APIs
    $CreateNamedPipe     = Win32API.new('kernel32', 'CreateNamedPipe', 'PLLLLLLL', 'L')
    $FlushFileBuffers    = Win32API.new('kernel32', 'FlushFileBuffers', 'L', 'B')
    $DisconnectNamedPipe = Win32API.new('kernel32', 'DisconnectNamedPipe', 'L', 'B')
    $WriteFile           = Win32API.new('kernel32', 'WriteFile', 'LPLPP', 'B')
    $CloseHandle         = Win32API.new('kernel32', 'CloseHandle', 'L', 'B')
  end

  #-----------------------------------------------------------------------
  # setup_pipe
  #-----------------------------------------------------------------------
  def self.setup_pipe
    make_APIs
    @@name = "\\\\.\\pipe\\" + PIPE_NAME

    @@pipe_mode = PIPE_DEFAULT_MODE
    @@open_mode = PIPE_ACCESS_DUPLEX
    @@pipe         = nil
    @@buffer       = 0.chr * PIPE_BUFFER_SIZE
    @@size         = 0
    @@bytes        = [0].pack('L')

    @@pipe = $CreateNamedPipe.call(
      @@name,
      @@open_mode,
      @@pipe_mode,
      PIPE_UNLIMITED_INSTANCES,
      PIPE_BUFFER_SIZE,
      PIPE_BUFFER_SIZE,
      PIPE_TIMEOUT,
      0
    )

    if @@pipe == INVALID_HANDLE_VALUE
      # If we could not open the pipe, notify the user
      # and proceed quietly
      print "WARNING -- Unable to create named pipe: " + PIPE_NAME
      @@pipe = nil
    else
      # Prompt the user to open the pipe
      print "Please launch the RGSSMonitor.rb script"
    end
  end

  #-----------------------------------------------------------------------
  # write_to_pipe ('msg' must be a string)
  #-----------------------------------------------------------------------
  def self.write_to_pipe(msg)
    if @@pipe
      # Format data
      @@buffer = msg
      @@size   = msg.size

      $WriteFile.call(@@pipe, @@buffer, @@buffer.size, @@bytes, 0)
    end
  end

  #------------------------------------------------------------------------
  # close_pipe
  #------------------------------------------------------------------------
  def self.close_pipe
    if @@pipe
      # Send kill message to RGSSMonitor
      @@buffer = "!!GAMEOVER!!"
      @@size   = @@buffer.size
      $WriteFile.call(@@pipe, @@buffer, @@buffer.size, @@bytes, 0)

      # Close down the pipe
      $FlushFileBuffers.call(@@pipe)
      $DisconnectNamedPipe.call(@@pipe)
      $CloseHandle.call(@@pipe)
      @@pipe = nil
    end
  end
end

要使用它,您只需要确保调用PipeLogger::setup_pipe在编写事件之前;并打电话PipeLogger::close_pipe游戏退出前。 (我将设置调用放在“Main”的开头,并添加一个ensure调用条款close_pipe.) 之后,您可以添加一个调用PipeLogger::write_to_pipe("msg")在任何脚本中的任何位置,使用任何“msg”字符串并写入管道。

我已经用 RPG Maker XP 测试了这段代码;它还应该与 RPG Maker VX 及更高版本一起使用。

您还需要从管道中读取一些内容。有多种方法可以做到这一点,但一种简单的方法是使用标准 Ruby 安装、win32-pipe Ruby Gem 和以下脚本:

require 'rubygems'
require 'win32/pipe'
include Win32

# -- Change THIS to change the name of the pipe!
PIPE_NAME = "RGSSPipe"

Thread.new { loop { sleep 0.01 } } # Allow Ctrl+C

pipe = Pipe::Client.new(PIPE_NAME)
continue = true

while continue
  msg = pipe.read.to_s
  puts msg

  continue = false if msg.chomp == "!!GAMEOVER!!"
end

I use 适用于 Windows 的 Ruby 1.8.7 http://rubyinstaller.org/win32-管道 gem http://rubygems.org/gems/win32-pipe上面提到过(参见here http://docs.rubygems.org/read/book/1有关安装 gem 的良好参考)。将以上内容保存为“RGSSMonitor.rb”并从命令行调用它ruby RGSSMonitor.rb.

CAVEATS:

  1. 上面列出的 RGSS 代码很脆弱;特别是,它不处理打开命名管道的失败。在您自己的开发机器上,这通常不是问题,但我不建议发布此代码。
  2. 我还没有测试过它,但我怀疑如果您向日志写入很多内容而不运行读取管道的进程(例如RGSSMonitor.rb)。 Windows 命名管道具有固定大小(我在这里将其设置为 1K),默认情况下,一旦管道被填满,写入就会阻塞(因为没有进程通过读取来“缓解压力”)。不幸的是,RPGXP 引擎会杀死停止运行 10 秒的 Ruby 脚本。 (我听说 RPGVX 已经取消了这个看门狗功能——在这种情况下,游戏将挂起而不是突然终止。)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何阻止读取 Ruby 中的命名管道? 的相关文章

  • 使用 String#sum 的 Ruby Anagram

    我已经解决了一个问题 要求您编写一个方法来确定所提供的数组中的哪些单词是字谜词 并将字谜词分组到输出中的子数组中 我已经使用似乎是典型的方式解决了这个问题 即对单词进行排序并根据其排序的字符将它们分组为散列 当我最初开始寻找一种方法来做到这
  • Rails JSON 多重嵌套关联

    我有一个对象 测试列表 其中包含问题和奖励问题 每个问题都有一个主题模型 我试图将它们全部包含在 JSON API 中 但我不断收到奇怪的难以理解的语法错误消息 我可以让它处理问题和主题或奖励问题和主题 但不能同时处理两者 这是我现在所拥有
  • 从delayed_job工作线程中排除队列

    在delayed jobs 中 可以为特定队列运行工作线程 我似乎找不到任何有关如何运行工作人员并排除特定队列的示例 文档 我试图让 1 名工作人员处理超高优先级的事情 1 名工作人员处理其余的事情 通过指定哪些队列与哪些工作人员相关来实现
  • 如何使用 nokogiri 解析 XML 而不丢失 HTML 实体?

    如果您查看后面部分中的输出 ruby 会删除所有 html 实体 如何使用 nokogiri 解析 XML 而不丢失 HTML 实体 BEFORE
  • Rails 5.1 中的 Ruby 2.4 Enumerable#sum 中断

    在使用 Ruby 2 3 5 的 Rails 5 1 4 中 我得到以下行为 gt gt sum gt nil 我想升级到 Ruby 2 4 其中 Enumerable sum 是本机实现的 使用 Ruby 2 4 2 在 IRB 中对此进
  • 如何使用 Logger.new 创建文件夹(如果不存在)?

    我正在尝试注册一个新日志 my logger Logger new Rails root log my log 但是当我尝试生成新文件夹时 将其放入其中 my logger Logger new Rails root log today t
  • 像 String#replace 一样替换 Ruby 中引用的 Integer 值

    我有以下代码 def mymethod a a replace a end mystring b mymethod mystring p mystring gt a 但我想用 Integer 执行相同的操作 那可能吗 简短的回答 不 长答案
  • IPC:在两个程序之间使用 C++ 中的命名管道

    我试图在同一台机器上运行的两个不同程序之间实现IPC 在我的例子中是CentOS7 为了实现一种松散耦合 我决定对 IPC 使用命名管道 因此 我正在使用以下示例并遇到了不同的问题 创建并写入管道 include
  • Ruby on Rails - 无法加载此类文件 - net/ssh

    我已经为此苦苦挣扎了几天了 当我尝试从视图调用助手中的方法来执行 ssh 时 它会抛出该错误 加载以下文件时发生此错误 net ssh 但是当我将代码复制到test rb文件并从提示符处执行ruby test rb它连接完美 可能是什么问题
  • Capistrano RVM 和 Ubuntu RVM 不是一个函数,使用“rvm use ...”选择 rubies 将不起作用

    我第一次尝试在 ubuntu 服务器上部署我的应用程序 我一直遇到这个错误 2013 03 24 15 13 36 executing deploy run migrations executing rvm gemset use vapin
  • 使 diff-lcs 的输出可读

    我正在使用 diff lcs gem 输出两个 html 内容体之间的差异 这是示例内容 版本一 p Paragraph one Sentence one p p Paragraph two Another sentence p p Par
  • 用于将唯一项插入数组的 Ruby 条件

    我知道如果你有一个array并将其引用为array uniq它将返回 没有任何重复项 然而 在本例中 它是一个对象数组 这是正确的 Ruby 语言吗 我希望每个电话都进入 calls数组除非call from与数组中已存在的 call fo
  • ruby Sequel gem - 如何使用 pg_array 扩展查询数组

    我正在使用pg array http sequel jeremyevans net rdoc plugins files lib sequel extensions pg array rb html扩展和续集版本 4 1 1 我添加了这样的
  • Ruby 有哪些图形包/API?

    Similar Perl 有哪些图形包 API https stackoverflow com questions 460325 what graphing packages apis exist for perl 我正在对不同语言的在线图
  • 退出无法在 Heroku 上工作 - 使用 Devise gem 和 Rails 4

    我刚刚对使用 Devise 进行身份验证的 Rails 4 应用程序进行了初始部署到 Heroku 注销可以在本地进行 但 Heroku 返回错误 您正在查找的页面不存在 并且不会注销用户 根据 Heroku 日志和 Google SO 搜
  • Bundler 找不到 gem“rack”的兼容版本:

    我是 Ruby 新手 但实际上如果我不想安装 Redmine 我就不需要它 我正在按照以下说明进行操作http www redmine org projects redmine wiki HowTo install Redmine on C
  • 获取类别和子类别的所有产品(rails、awesome_nested_set)

    正在开发一个电子商务应用程序 我试图解决以下问题 我通过 Awesome nested set 插件实现了我的类别 如果我通过选择一个类别列出我的文章 一切正常 但对于某些链接 我想显示一个类别的所有产品及其子类别的产品 这是仅适用于一种类
  • Ruby 中的图像抓取

    如何使用 Nokogiri 抓取特定 URL 上存在的图像 如果有比 Nokogiri 更好的选择 请提出建议 css图像标签是 profilePic img 如果它只是一个 img 带有网址 PAGE http site com page
  • 如何将上传的二进制文件 (ASCII-8BIT) 嵌入 XML (UTF-8) 中?

    我有一个通过常规上传的文件form for 这给了我一个ActionDispatch Http UploadedFile我可以调用的 params 哈希中的对象 read来获取内容 我现在需要将该文件嵌入到 XML 文档中 我现在使用常规
  • Rails apns 用于向 Apple ipad 推送通知 - 使用哪个 gem?

    我希望从 Rails 3 0 3 应用程序向苹果推送通知 我发现了各种 apns ish 宝石 包括 apns on rails 其中一些似乎有点旧 1 或 2 年 并且非常不清楚 2012 年使用的 当前 是什么 根据我的标准 您能推荐使

随机推荐