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

Ruby客户端-服务端Socket数据传输截断问题求助

问题根源与解决方案

核心问题

你的代码在处理二进制数据时,误用了文本分隔符和行读取方法,直接导致数据截断:

  • Marshal.dump生成的二进制数据中,必然包含逗号(,)和换行符(\n)——这两个字符正好是你用来拼接请求、判断数据结束的标记。
  • 客户端用,拼接action与二进制数据,当二进制里的逗号被服务端split(',')识别为分隔符时,数据会被拆成多个片段,服务端只拿到第一个片段。
  • 客户端用socket.puts发送数据(自动追加换行符),而二进制数据本身可能包含换行符,服务端socket.gets读到第一个换行就停止读取,导致只拿到部分数据。

解决方案(推荐两种)

方案1:使用长度前缀传输二进制数据(最可靠)

二进制数据不能用字符做分隔,标准做法是先发送数据长度,再发送实际内容,让服务端按长度读取完整数据:

客户端修改代码:

require 'marshal'

def send_request(socket, action, data = {})
  # 将整个请求序列化
  payload = Marshal.dump({ action: action, data: data })
  # 先发送payload的字节长度(用8字节网络字节序编码)
  socket.write([payload.size].pack('Q'))
  # 发送实际序列化内容
  socket.write(payload)
end

player = {
  type: :FISHERMAN,
  name: "Andrew",
  party: $Trainer.party,
  speech: "Good game."
}

send_request(@server, 'player_data', { data: Marshal.dump(player) })

服务端修改代码:

require 'marshal'

begin
  loop do
    # 先读取8字节的长度标记
    len_data = socket.read(8)
    break if len_data.nil? || len_data.size < 8
    
    payload_size = len_data.unpack('Q')[0]
    # 按长度读取完整的payload
    payload = socket.read(payload_size)
    break if payload.nil? || payload.size < payload_size
    
    request = Marshal.load(payload)
    action = request[:action]
    data = request[:data]

    case action
    when 'player_data'
      player_data = data[:data]
      # 处理完整的Marshal数据
      player = Marshal.load(player_data)
      # ...后续逻辑
    end
  end
rescue => e
  # 处理连接中断、序列化异常等情况
  puts "Error: #{e.message}"
end

方案2:对二进制数据做Base64编码(兼容文本传输)

如果一定要用文本分隔的方式,可将二进制数据转为Base64字符串(无特殊字符),避免分隔符冲突:

客户端修改代码:

require 'marshal'
require 'base64'

def send_request(socket, action, data = {})
  # 对二进制数据做Base64编码
  encoded_data = data.transform_values { |v| Base64.strict_encode64(v) }
  request_data = [action] + encoded_data.map { |k, v| "#{k}:#{v}" }
  socket.puts(request_data.join(','))
end

player = {
  type: :FISHERMAN,
  name: "Andrew",
  party: $Trainer.party,
  speech: "Good game."
}

send_request(@server, 'player_data', { data: Marshal.dump(player) })

服务端修改代码:

require 'marshal'
require 'base64'

begin
  loop do
    data = socket.gets
    break if data.nil?
    
    request_data = data.chomp.split(',')
    action = request_data[0]
    request = {}
    request_data[1..].each do |item|
      key, value = item.split(':', 2)
      request[key] = value
    end

    case action
    when 'player_data'
      # 解码Base64数据
      encoded_data = request['data']
      player_data = Base64.strict_decode64(encoded_data)
      # 还原Marshal对象
      player = Marshal.load(player_data)
      # ...后续逻辑
    end
  end
end

内容的提问来源于stack exchange,提问作者Miquel Ibáñez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 13:35:57