|
|
require 'socket'
require 'ServerDyn.rb'
include ServerDyn
class UDPServer
###############################################################################
# Init
def initialize(port = 24242, packetSize = 64, debug = true)
@port = port
@packetSize = packetSize
@server = UDPSocket.open
@server.bind("0.0.0.0", @port)
@debug = debug
@clients = []
@clientsName = []
@clientsPort = []
@clientsTime = []
@idleTime = 60
@time = Time.new
@dynamicCode = true
end
###############################################################################
# Add a client to the list
def addClient(ip, port)
# First send all connected client to the new client
clientID = 0
@clients.each(){ |i|
if @clientsName[clientID] != "NoName" then
@server.send("0:#{@clientsName[clientID]}", 0, ip, port)
end
clientID += 1
}
# Then register our new client
puts "Add client: #{ip}:#{port}"
@clients += [ ip ]
@clientsName += [ "NoName" ]
@clientsPort += [ port ]
@clientsTime += [ Time.new ]
end
###############################################################################
# Remove a client
def removeClient(slot)
puts "Del client: #{@clients[slot]}:#{@clientsPort[slot]} (#{slot})"
@clients.delete_at(slot)
@clientsName.delete_at(slot)
@clientsPort.delete_at(slot)
@clientsTime.delete_at(slot)
end
###############################################################################
# Get client ID by IP/PORT
def getClientIDByIPPort(ip, port)
clientID = 0
@clients.each(){ |i|
if port == "0" then port = @clientsPort[clientID]; end
if (i == ip) and
(@clientsPort[clientID] == port) then
return clientID
break
end
clientID += 1
}
return -1
end
###############################################################################
# Remove a client that does use a specific ip/port
def removeClientByIPPort(ip, port)
if (clientID = getClientIDByIPPort(ip, port)) != -1 then
removeClient(clientID)
end
end
###############################################################################
# Register a new client if needed
def registerClient(ip, port)
registred = false
if (clientID = getClientIDByIPPort(ip, port)) != -1 then
registred = true;
# update IDLE time
@clientsTime[clientID] = Time.now
return
end
# register the client
if not registred then
addClient(ip, port)
end
end
###############################################################################
# Remove IDLEing clients
def removeIDLEClients()
clientID = 0
t = Time.now
idleingClients = []
# Searching idleing clients
@clientsTime.each(){ |i|
if t - i > @idleTime then
idleingClients += [ clientID ]
end
clientID += 1
}
# Removing idleing client
0.upto(idleingClients.size - 1){ |i|
clientID = idleingClients[i]
begin
broadcastFrom("1:#{@clientsName[clientID]}", "SERVER", 0)
@server.send("Disconnected:IDLEing too much\n", 0,
@clients[clientID],
@clientsPort[clientID])
rescue
puts "removeIDLEClients():broadcast/send error..."
end
removeClient(clientID)
}
end
###############################################################################
# Broadcast
def broadcastFrom(msg, ip, port, echoes = false)
clientID = 0
@clients.each(){ |i|
if ((i != ip) or (@clientsPort[clientID] != port)) or (echoes) then
#@server.send("BROADCAST FROM(#{ip}:#{port}): "+msg, 0, i, @clientsPort[clientID])
@server.send(msg, 0, i, @clientsPort[clientID])
#puts "sending to: #{i} : #{@clientsPort[clientID]}"
end
clientID += 1
}
end
###############################################################################
# Set client Name
def setClientName(ip, port, name)
if (clientID = getClientIDByIPPort(ip, port)) != -1 then
@clientsName[clientID] = name
puts "client(#{ip}:#{port}) name(#{@clientsName[clientID]})"
end
end
###############################################################################
# Server loop
def runLoop()
while 1 do
if @debug then
puts "waiting packet(blocking)..."
end
a = @server.recvfrom(@packetSize)
pData, pProto, pSourcePort, pDNS, pIP =
a[0], a[1][0], a[1][1], a[1][2], a[1][3]
if @debug then
puts "Data...: #{pData}" +
"Proto..: #{pProto}\n" +
"SrcPort: #{pSourcePort}\n" +
"DNS....: #{pDNS}\n" +
"IP.....: #{pIP}\n"
end
if pData =~ /^getStats\n/ then
@server.send("total_client: #{@clients.size}\n", 0, pIP, pSourcePort)
clientID = 0
@clients.each(){ |i|
@server.send(" client ID(#{clientID}) NAME(#{@clientsName[clientID]}) " +
"IP(#{i}) SP(#{@clientsPort[clientID]})" +
" IDLE(#{@clientsTime[clientID]})\n", 0, pIP, pSourcePort)
clientID += 1
}
elsif pData =~ /^sync\n/ then
begin
load 'ServerDyn.rb'
@server.send("Synced\n", 0, pIP, pSourcePort)
@dynamicCode = true
rescue
@server.send("ERROR Syncing! dynamic code will not works\n", 0, pIP, pSourcePort)
@dynamicCode = false
end
elsif pData =~ /^del / then
removeIP = pData.gsub(/^del /, '')
removeIP.chomp!
removeClientByIPPort(removeIP, 0)
end
registerClient(pIP, pSourcePort)
broadcastFrom(pData, pIP, pSourcePort)
if @dynamicCode then
begin
ServerDyn.parseData(pData, pIP, pSourcePort)
rescue
@server.send("ERROR Dynamic code section broken\n", 0, pIP, pSourcePort)
@dynamicCode = false
end
end
if pData =~ /^0:/ then
if @debug then puts "-> connect packet"; end
name = pData.gsub(/.*:/, '')
name.chomp!
setClientName(pIP, pSourcePort, name)
elsif pData =~ /^1:/ then
if @debug then puts "-> disconnect packet"; end
removeClientByIPPort(pIP, pSourcePort)
end
end
end
###############################################################################
# Idle loop
def checkIDLELoop()
while 1 do
sleep @idleTime
@time = Time.new
removeIDLEClients()
end
end
end
mserv = UDPServer.new()
# TODO IDLE MUTEX..
# Server thread
serverThread = Thread.new {
mserv.runLoop()
}
# IDLEing clients
checkIDLE = Thread.new {
mserv.checkIDLELoop()
}
checkIDLE.join
serverThread.join
|