我的情况是我的服务器可能会关闭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)
让我更接近,因为这只是取代了进程,并且应该维护文件(不要关闭它们)。
答案 0 :(得分:0)
你做不到。套接字仅在进程的生命周期内有效:当进程退出时,它将由OS关闭。这反过来使连接无效,因此另一端仍未连接。
答案 1 :(得分:0)
热重启(又称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
(这对我来说是一个更大的系统的一部分)。可能存在一些错误,所以如果您找到它们请发表评论并且我会更正。
希望这有助于任何找到它的人。