Windows 10下Async::WebSocket无法连接,Stream Deck Ruby SDK开发受阻
Windows 10下Ruby Async::WebSocket连接Stream Deck失败的解决方案
问题背景
我正在为Stream Deck开发Ruby SDK——Stream Deck是一款类似高级硬件AutoHotkey的设备,支持为按钮自定义图标和功能,还能通过目录无限扩展管理按钮。它提供语言无关的API,启动插件时会传入-port、-pluginUUID、-registerEvent、-info参数,插件需要在指定端口的localhost上建立WebSocket连接,连接后发送包含指定事件和UUID的JSON字符串完成注册。
当前遇到的问题:
- Ruby 3.0.5环境下,参数解析和控制台输出正常,但WebSocket连接后程序挂起,无法正常通信
- RubyInstaller 3.1.2环境下程序直接崩溃,报错:
0.0s warn: Async::Task [oid=0x280] [ec=0x294] [pid=32436] [2022-12-13 00:36:46 -0500] | Task may have ended with unhandled exception. | Errno::EBADF: Bad file descriptor | → <internal:io> 63 | C:/Ruby31-x64/lib/ruby/gems/3.1.0/gems/io-event-1.1.4/lib/io/event/selector/select.rb 206 - WSL环境下修改为硬编码端口和UUID后,能正常与Stream Deck通信,推测是Windows平台下Async相关模块的兼容性问题
解决方案1:替换为非Async的WebSocket客户端
Async库在Windows平台的IO事件处理存在兼容性问题,改用faye-websocket(基于EventMachine)可以规避该问题,稳定性更好:
安装依赖:
gem install faye-websocket eventmachine替换测试脚本:
#!/usr/bin/env ruby require "json" require "faye/websocket" require "eventmachine" # 解析启动参数 _, port, _, UUID, _, REGISTER_EVENT, _, *info = ARGV PORT = port.to_i INFO = JSON.parse(info.join(" ")) # 调试输出(确认参数正确) p PORT p UUID p REGISTER_EVENT p INFO EventMachine.run do # 建立WebSocket连接 ws = Faye::WebSocket::Client.new("ws://localhost:#{PORT}") ws.on :open do |event| puts "Opened!" # 发送注册消息 ws.send({ event: REGISTER_EVENT, uuid: UUID }.to_json) end ws.on :message do |event| puts "Message:" puts event.data end ws.on :close do |event| puts "Closed: #{event.code} - #{event.reason}" ws = nil EventMachine.stop end ws.on :error do |event| puts "Error: #{event.message}" end end
解决方案2:调整Async库的使用方式(针对Ruby 3.0.5)
如果坚持使用Async库,可以尝试以下调整修复Windows平台的兼容性问题:
强制使用兼容的IO选择器
在代码开头添加环境变量,指定使用poll选择器(避免Windows下select调用的问题):ENV['IO_EVENT_SELECTOR'] = 'poll'若安装了
io-event-win32gem,也可以指定Windows专属选择器:gem install io-event-win32然后设置:
ENV['IO_EVENT_SELECTOR'] = 'win32'修复协议并添加错误处理
显式使用ws://协议(而非http://),并添加异常捕获避免程序崩溃:#!/usr/bin/env ruby require "json" require "async/websocket/client" require "async/http/endpoint" # 指定兼容的IO选择器 ENV['IO_EVENT_SELECTOR'] = 'poll' include Async include Async::HTTP # 解析参数 _, port, _, UUID, _, REGISTER_EVENT, _, *info = ARGV PORT = port.to_i INFO = JSON.parse info.join(" ") p PORT p UUID p REGISTER_EVENT p INFO Async do |task| begin # 显式使用ws协议 WebSocket::Client.connect(Endpoint.parse "ws://localhost:#{PORT}") do |ws| ws.write({ event: REGISTER_EVENT, uuid: UUID }.to_json) ws.flush puts "Opened!" while msg = ws.read puts "Message:" puts msg end end rescue Errno::EBADF => e puts "Connection failed: #{e.message}" # 可添加重连逻辑或退出 end end
解决方案3:使用Ruby 3.1+标准库WebSocket客户端
Ruby 3.1及以上版本标准库内置了websocket-client-simple,无需额外依赖,兼容性更佳:
#!/usr/bin/env ruby require "json" require "websocket-client-simple" # 解析参数 _, port, _, UUID, _, REGISTER_EVENT, _, *info = ARGV PORT = port.to_i INFO = JSON.parse(info.join(" ")) p PORT p UUID p REGISTER_EVENT p INFO # 建立WebSocket连接 ws = WebSocket::Client::Simple.connect "ws://localhost:#{PORT}" ws.on :open do puts "Opened!" ws.send({ event: REGISTER_EVENT, uuid: UUID }.to_json) end ws.on :message do |msg| puts "Message:" puts msg.data end ws.on :close do |e| puts "Closed: #{e.code} #{e.reason}" exit 1 end ws.on :error do |e| puts "Error: #{e.message}" end # 保持程序运行 loop do sleep 1 end
内容的提问来源于stack exchange,提问作者vinnydiehl
相关产品推荐
相关产品推荐

