【问题标题】:Ruby TCPServer: Accepting a known socketRuby TCPServer:接受已知的套接字
【发布时间】:2012-09-04 22:39:09
【问题描述】:

我的服务器可能会关闭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拥有所有用户的数组,并且每个用户都有一个socket,我该如何重新初始化重启前打开的socket(假设另一端仍然连接)?

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

【问题讨论】:

    标签: ruby sockets ruby-1.9


    【解决方案1】:

    你不能。套接字仅在进程的生命周期内有效:当进程退出时它被操作系统关闭。这反过来又使连接无效,因此另一端仍未连接。

    【讨论】:

    • 如果我理解正确,exec 不会终止进程,而是替换它;因此,套接字连接挂在系统中(如果不处理将超时)。因此,我们应该能够寻址仍然存在的文件描述符并将其重新分配给新创建的TCPServer。在andreasen.org/ftp/copyover-7.tar.gz 用 C 语言完成了类似的操作
    • @stslavik 您的问题中没有关于 exec() 的内容。需要大量澄清。
    • 抱歉,我想既然我试图从一个进程中重新启动一个进程,Kernel 模块会以某种方式被给定。我的错。
    【解决方案2】:

    如何在 Ruby 中热重启 TCPServer

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

    我在下面提出的建议可能不是最佳做法,但它正在发挥作用,也许会指导其他人采用类似的解决方案。

    命令

    我使用一种特殊的编码风格,利用命令表来查找函数及其可访问性。所有命令函数都以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(它对我来说是一个更大系统的一部分)。可能有一些错误,所以如果你发现它们请评论,我会更正。

    希望这对找到它的人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-09
      • 2014-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-26
      相关资源
      最近更新 更多