You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails 5.1.5环境下IoT设备TCP请求转Puma处理报错咨询

解决Puma无法处理IoT TCP请求的问题

嘿,这个问题我之前帮朋友踩过坑!核心原因很直白:Puma是纯HTTP服务器,它只能解析符合HTTP协议格式的请求,而IoT设备发的是原始TCP数据流,这俩完全不在一个频道上,直接转发肯定会报HTTP解析错误。给你几个实用的解决方案,按推荐程度排序:

方案1:编写独立的TCP服务进程(最推荐)

我最推荐这个思路——单独搞个轻量的TCP服务,专门对接IoT设备的连接和数据接收,然后把数据转存到Rails的数据库里。这样既不干扰Rails主应用的运行,又能高效处理TCP长/短连接。

给你写个简单的Ruby示例(用原生socket库,直接复用Rails的数据库模型):

# tcp_server.rb
require 'socket'
require 'active_record'

# 加载Rails生产环境的数据库配置,直接操作模型
ActiveRecord::Base.establish_connection(YAML.load_file('config/database.yml')['production'])
require './app/models/iot_data.rb'

# 监听本地1234端口,接收IoT设备连接
server = TCPServer.new('0.0.0.0', 1234)

loop do
  client = server.accept
  # 读取设备发送的数据,按需调整读取大小(比如1024字节)
  raw_data = client.readpartial(1024)
  # 假设你的IoTData模型有data字段,直接存原始数据
  IoTData.create(data: raw_data.strip)
  client.close
end

然后你可以用systemd或者foreman把这个TCP进程和Puma一起管理,Nginx的stream配置直接把TCP流量转发到这个服务的端口就行:

stream {
  server {
    listen 8888; # IoT设备要连接的端口
    proxy_pass 127.0.0.1:1234; # 转发到我们的TCP服务
  }
}

方案2:把TCP请求包装成HTTP(适合简单数据场景)

如果你的IoT设备发的是简单文本数据,不想额外维护进程,可以试试在Nginx里把原始TCP数据转换成HTTP请求,再转发给Puma。不过这个需要Nginx编译时开启ngx_stream_js_module模块。

举个Nginx配置示例:

stream {
  # 导入自定义JS脚本,用来转换TCP数据为HTTP
  js_import tcp_to_http.js;

  server {
    listen 8888;
    # 提前读取TCP数据,转换成HTTP格式
    js_preread tcp_to_http.convert;
    # 转发给Puma的端口
    proxy_pass 127.0.0.1:3000;
  }
}

对应的tcp_to_http.js脚本:

function convert(s) {
  let rawData = s.buffer;
  // 把原始数据包装成POST请求的格式
  let httpRequest = `POST /iot/save_data HTTP/1.1\r\nHost: your-domain.com\r\nContent-Length: ${rawData.length}\r\n\r\n${rawData}`;
  s.send(httpRequest);
}

然后在Rails里写个对应的控制器动作接收:

# app/controllers/iot_controller.rb
class IotController < ApplicationController
  # IoT设备不需要CSRF验证,跳过这个检查
  skip_before_action :verify_authenticity_token

  def save_data
    # 读取HTTP请求体里的原始数据
    raw_data = request.body.read
    IoTData.create(data: raw_data)
    # 返回200状态码给设备
    head :ok
  end
end

别忘了在routes.rb里加路由:

post '/iot/save_data', to: 'iot#save_data'

这个方案的缺点是如果TCP数据是二进制或者需要长连接,Nginx的JS模块处理起来会很麻烦,不如方案1灵活。

方案3:用Action Cable(适合双向通信场景)

如果你的IoT设备需要和服务器双向通信(比如服务器给设备发指令),可以试试Rails的Action Cable——它基于WebSocket,而WebSocket本身是建立在TCP之上的,Puma可以直接处理。不过这个需要修改IoT设备的通信协议,改成WebSocket。

先在Rails里创建一个Channel:

# app/channels/iot_channel.rb
class IotChannel < ApplicationCable::Channel
  def subscribed
    # 给每个设备开个专属的数据流
    stream_from "iot_device_#{params[:device_id]}"
  end

  def receive(data)
    # 接收设备发过来的数据,存到数据库
    IoTData.create(device_id: params[:device_id], data: data['payload'])
    # 如果需要给设备发响应,直接用transmit就行
    transmit status: 'success'
  end
end

然后设备端需要实现WebSocket客户端,连接到ws://your-domain.com/cable,发送符合Action Cable格式的数据。这个方案适合有双向交互需求的场景,但得改设备的通信逻辑。


总结一下:如果只是单纯接收IoT数据,方案1是最稳定、最灵活的;如果数据格式简单且不想额外维护进程,可以试试方案2;如果需要双向通信,方案3更合适。

内容的提问来源于stack exchange,提问作者darshi sheth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 07:45:31