如何在指定超时后退出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
相关产品推荐
相关产品推荐

