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

Ruby TCP应用本地文件下载阻塞问题排查求助

Ruby TCP远程控制应用的文件下载阻塞与文件损坏问题

我开发了一个基于TCP连接的Ruby远程控制应用,包含接收客户端指令并执行的服务器端,其中一项功能是从服务器向客户端下载文件。当前遇到以下问题:

  • 跨机器运行时下载功能正常,但同一机器运行时,发送download filename指令后应用无响应,直到关闭客户端,文件才会被保存。
  • 存在文件未正确复制、下载后文件为空的情况,甚至怀疑跨机器运行也可能出现这类问题。

客户端代码

elsif command.start_with?('download')
    client.puts(command)  # 向服务器发送指令以开始下载
    filename = client.gets.chomp  # 从服务器接收文件名
  
    file_path = File.join(cartella_corrente, filename)  # 保存文件的完整路径
    File.open(file_path, 'wb') do |file|
      loop do
        chunk = client.read(8000)
        break if chunk.nil? || chunk.empty? || chunk == "__END_OF_FILE__"
        file.write(chunk)
      end
    end

服务器端代码

elsif command.start_with?('download')
      client.puts("download")  # 向客户端发送指令以开始下载
      filename = command[9..-1]
      client.puts(filename)  # 向客户端发送文件名以开始下载
    
      save_path = File.join(SAVE_DIRECTORY, filename)
      if File.exist?(save_path)
        File.open(save_path, 'rb') do |file|
          loop do
            chunk = file.read(8000)
            break if chunk.nil? || chunk.empty?
            client.write(chunk)
            client.flush
          end
          client.puts("__END_OF_FILE__")  # 发送结束标识
        end
        puts "Download completato. Salvato come #{filename}".colorize(:green)
      else
        client.puts("Errore: Il file #{filename} non esiste.")
      end
    else

问题原因分析

  1. 同一机器阻塞核心原因:结束标识格式不匹配。服务器用puts发送结束标识会自动添加换行符,导致实际发送的是__END_OF_FILE__\n,但客户端判断的是chunk == "__END_OF_FILE__",永远无法触发跳出循环的条件,只能等到连接关闭(客户端退出)时read返回nil才结束循环。同一机器的TCP缓冲区行为更“紧凑”,不会像跨机器那样因网络延迟提前暴露问题。
  2. 文件损坏/为空原因:二进制传输中混用文本IO方法(puts/gets)会干扰数据流;结束标识判断失效会导致客户端要么一直等待,要么把带换行的结束标识写入文件,造成文件不完整或损坏。

解决方案

1. 修正结束标识的传输与判断逻辑

  • 服务器端发送结束标识时,改用write避免自动添加换行:
    client.write("__END_OF_FILE__")
    client.flush
    
  • 客户端要处理结束标识可能出现在块中间的情况,不能直接判断整个块等于标识:
    loop do
      chunk = client.read(8000)
      break if chunk.nil? || chunk.empty?
      if chunk.include?("__END_OF_FILE__")
        # 截取标识前的有效文件数据写入
        file.write(chunk.split("__END_OF_FILE__")[0])
        break
      end
      file.write(chunk)
    end
    

2. 统一IO方法,避免文本/二进制混用

二进制传输全程使用read/write,仅在传输文本类数据(如指令、文件名)时使用puts/gets,且明确处理换行符:

  • 服务器端发送文件名保持puts,客户端用gets.chomp读取,确保文件名正确解析。

3. 增加日志与错误处理

在客户端和服务器端添加关键步骤的日志,比如客户端记录每次读取的块长度,服务器记录每次写入的字节数,方便定位数据流异常。

4. 验证文件完整性

传输完成后对比哈希值确保文件完整:

  • 服务器端计算文件MD5哈希并发送:
    require 'digest'
    file_hash = Digest::MD5.file(save_path).hexdigest
    client.write(file_hash)
    client.flush
    
  • 客户端计算本地文件哈希并对比:
    local_hash = Digest::MD5.file(file_path).hexdigest
    server_hash = client.read(32)
    puts local_hash == server_hash ? "文件验证通过" : "文件损坏,哈希不匹配"
    

内容的提问来源于stack exchange,提问作者Alessandro Altobelli

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 19:17:42