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
问题原因分析
- 同一机器阻塞核心原因:结束标识格式不匹配。服务器用
puts发送结束标识会自动添加换行符,导致实际发送的是__END_OF_FILE__\n,但客户端判断的是chunk == "__END_OF_FILE__",永远无法触发跳出循环的条件,只能等到连接关闭(客户端退出)时read返回nil才结束循环。同一机器的TCP缓冲区行为更“紧凑”,不会像跨机器那样因网络延迟提前暴露问题。 - 文件损坏/为空原因:二进制传输中混用文本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
相关产品推荐
相关产品推荐

