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

如何在指定超时后退出Ruby的TCPSocket::gets方法?

Ruby TCP套接字读取添加超时方案

你的问题核心在于TCPSocket#gets是阻塞调用——如果套接字没有数据写入,代码会一直卡在gets这一行,循环里的超时判断根本没机会执行。这里给你两个可行的解决方案:

方案一:用IO.select实现动态超时

IO.select可以监听套接字是否有可读数据,同时设置超时时间,完美避开gets的阻塞问题。我们可以记录开始时间,每次循环计算剩余超时时间,让超时判断精准生效:

require 'socket'

server = 'example.com'
port = 80
timeout_seconds = 60 # 1分钟超时

socket = TCPSocket.open(server, port)
start_time = Time.now

loop do
  remaining_time = timeout_seconds - (Time.now - start_time)
  break if remaining_time <= 0

  # 检查套接字是否有数据可读,超时时间设为剩余时长
  ready = IO.select([socket], nil, nil, remaining_time)
  unless ready
    # 超时无数据,终止循环
    break
  end

  if line = socket.gets
    line = line.chop
    # 在这里处理读取到的数据,比如打印或业务逻辑
    puts "收到数据: #{line}"
  else
    # 套接字被远端关闭,退出循环
    break
  end
end

socket.close

方案二:设置套接字接收超时选项

直接给套接字设置SO_RCVTIMEO选项,让gets调用在超时后抛出异常,我们捕获异常即可终止循环:

require 'socket'

server = 'example.com'
port = 80
timeout_seconds = 60 # 1分钟超时

socket = TCPSocket.open(server, port)

# 设置接收超时,参数是[秒, 微秒],打包成二进制格式
sec = timeout_seconds
usec = 0
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, [sec, usec].pack('l_2'))

begin
  while line = socket.gets
    line = line.chop
    puts "收到数据: #{line}"
  end
rescue Errno::ETIMEDOUT
  # 捕获超时异常,退出循环
  puts "读取超时,程序退出"
ensure
  # 确保套接字被关闭
  socket.close
end

两种方案对比:

  • IO.select更灵活,能动态计算剩余超时时间,适合需要精准控制总时长的场景;
  • SO_RCVTIMEO代码更简洁,每次gets都会等待设定的超时时间,适合对总时长要求不那么严格的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 12:28:20