Ruby TCPServer:接受已知套接字

时间:2012-09-04 22:39:09

标签: ruby sockets ruby-1.9

我的情况是我的服务器可能会关闭TCPServer并重新启动,将所有用户保存到文件中,然后立即重新加载它们;他们的联系不会切断。

问题是我似乎无法重新初始化他们的流。

当我们重新启动(并尝试维护连接)时,我重新初始化TCPServer,并加载我的连接用户数组 - 由于这些用户都有一个现有的套接字地址,存储为<TCPSocket:0x00000000000000>,我可以重新初始化这些TCPServer的地址?

通常,每个用户都会连接并被接受:

$nCS = TCPServer.new(HOST, PORT)

begin
  while socket = $nCS.accept
    Thread.new( socket ) do |sock|
      begin
        d = User.new(sock)
        while sock.gets
          szIn = $_.chomp
          DBG( "Received '" + szIn + "' from Client " + sock.to_s )
          d.parseInput( szIn )
        end
      rescue => e
        $stdout.puts "ERROR: Caught error in Client Thread: #{e} \r\n #{e.backtrace.to_s.gsub(",", ",\r\n")}"
        sock.write("Sorry, an error has occurred, and you have been disconnected."+EOL+"Please try again later."+EOL)
        d.closeConnection
      end
    end
  end
rescue => e
  $stdout.puts "ERROR: Caught error in Server Thread: #{e} \r\n #{e.backtrace.to_s.gsub(",", ",\r\n")}"
  exit
end

为了给它一个热重启的命令,我们使用exec('./main --copyover')标记正在发生复制。

如果$connected包含所有用户的数组,并且每个用户都有一个套接字,如何重新初始化重启之前打开的套接字(假设另一端仍然连接)?

我怀疑使用exec("./main", "--copyover", *$nCS, *$connected)让我更接近,因为这只是取代了进程,并且应该维护文件(不要关闭它们)。

2 个答案:

答案 0 :(得分:0)

你做不到。套接字仅在进程的生命周期内有效:当进程退出时,它将由OS关闭。这反过来使连接无效,因此另一端仍未连接。

答案 1 :(得分:0)

如何在Ruby中热重启TCPServer

热重启(又称Copyover)是管理员可以重新加载应用程序的过程(以及自上次启动以来所做的任何新更改),而不会丢失客户端连接。这对于管理客户期望很有用,因为应用程序在使用时不需要遭受严重的停机和中断。

我在下面提出的建议可能不是最佳做法,但它的功能可能会引导其他人采用类似的解决方案。

命令

我使用特定的编码风格,利用命令表来查找函数及其可访问性。所有命令函数都以cmd为前缀。我会清理杂项以提高可读性:

def cmdCopyover
  #$nCS is the TCPServer object
  #$connected holds an array of all users sockets
  #--copyover flags that this is a hot reboot.
  connected_args = $connected.map do |sock|
    sock.close_on_exec = false if sock.respond_to?(:close_on_exec=)
    sock.fileno.to_s
  end.join(",")
  exec('./main.rb', '--copyover', $nCS.fileno.to_s, connected_args)
end

我们传递的是弦乐; $nCS.fileno.to_s为我们提供了主TCPServer对象的文件描述符,而connected_args是每个连接用户的逗号描述的文件描述符列表。当我们重新启动时,ARGV将是一个包含每个参数的数组:

  • ARGV[0] == "--copyover"
  • ARGV[1] == "5"(或者无论TCPServer的文件描述符是什么)
  • ARGV[2] == "6,7,8,9"(示例,假设有4个已连接的用户)

您期待什么(复制品)

在正常情况下,我们可能会有一个基本服务器(main.rb中看起来像这样:

puts "Starting Server"
$connected = Array.new
$nCS = TCPServer.new("127.0.0.1",9999)

begin
  while socket = $nCS.accept
    # NB: Move this loop to its own function, threadLoop()
    Thread.new( socket ) do |sock|
      begin
        while sock.gets
          szIn = $_.chomp
          #do something with input.
        end
      rescue => e
        puts "ERROR: Caught error in Client Thread: #{e}"
        puts #{e.backtrace.to_s.gsub(",", ",\r\n")}"
        sock.write("Sorry, an error has occurred, and you have been disconnected."+EOL+"Please try again later."+EOL)
        sock.close
      end
    end
  end
rescue => e
  puts "Error: Caught Error in Server Thread: #{e}"
  puts "#{e.backtrace.to_s.gsub(",", ",\r\n")}"
  exit
end

我们希望将主循环移动到它自己的函数以使其可访问 - 我们的重新连接用户需要重新插入循环中。

让我们准备main.rb接受热重启:

def threadLoop( socket )
  Thread.new( socket ) do |sock|
    begin
      while sock.gets
        szIn = $_.chomp
        #do something with input.
      end
    rescue => e
      puts "ERROR: Caught error in Client Thread: #{e}"
      puts #{e.backtrace.to_s.gsub(",", ",\r\n")}"
      sock.write("Sorry, an error has occurred, and you have been disconnected."+EOL+"Please try again later."+EOL)
      sock.close
    end
  end
end

puts "Starting Server"
$connected = Array.new
if ARGV[0] == '--copyover'
  $nCS = TCPServer.for_fd( ARGV[1].to_i )
  $nCS.close_on_exec = false if $nCS.respond_to?(:close_on_exec=)
  connected_args = ARGV[2]
  connected_args.split(/,/).map do |sockfd|
  $connected << sockfd

  $connected.each {|c| threadLoop( c ) }
else
  $nCS = TCPServer.new("127.0.0.1",9999)
  $nCS.close_on_exec = false if $nCS.respond_to?(:close_on_exec=)
end

begin
  while socket = $nCS.accept
    threadLoop( socket )
  end
rescue => e
  puts "Error: Caught Error in Server Thread: #{e}"
  puts "#{e.backtrace.to_s.gsub(",", ",\r\n")}"
  exit
end

买者

我的实际使用情况要复杂得多,所以我尽力去除所有的垃圾;然而,当我在这里结束时,我意识到你可能没有$connected(这对我来说是一个更大的系统的一部分)。可能存在一些错误,所以如果您找到它们请发表评论并且我会更正。

希望这有助于任何找到它的人。