package org.red5.server.net.udp;
/*
* RED5 Open Source Flash Server - http://www.osflash.org/red5
*
* Copyright (c) 2006-2009 by respective authors (see below). All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any later
* version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this library; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoHandlerAdapter;
import org.apache.mina.common.IoSession;
import org.red5.io.utils.HexDump;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* => client send to server
* <= server send to client
* << server broadcast
*
* Connecting to the server
*
* => byte(join)
* << byte(join) int(id)
* => byte(list)
* <= byte(list) int(count) int(id) int(id) ...
*
* Sending a message to all
*
* => byte(send) [..anything..]
* << byte(send) [..anything..]
*
* Server ping client to keep alive, every second
*
* <= byte(noop)
* => byte(noop)
*
* Timeouts (after 10s no reply)
*
* << byte(exit) int(id)
*
* @author luke
*/
public class BasicHandler extends IoHandlerAdapter {
protected static Logger log = LoggerFactory.getLogger(BasicHandler.class);
static final int TICK = 1000;
static final int TIMEOUT = 10000;
static final byte NOOP = 0x00;
static final byte JOIN = 0x01;
static final byte LIST = 0x02;
static final byte SEND = 0x03;
static final byte EXIT = 0x04;
final ByteBuffer NOOP_MSG = ByteBuffer.wrap(new byte[]{NOOP}).asReadOnlyBuffer();
protected Timer timer = new Timer("Timer", true);
protected Set<IoSession> sessions = Collections.synchronizedSet(new HashSet<IoSession>());
protected boolean showInfo = false;
public BasicHandler(){
timer.scheduleAtFixedRate(new TimeoutTask(), 0, TICK);
showInfo = log.isInfoEnabled();
}
@Override
public void exceptionCaught(IoSession session, Throwable ex) throws Exception {
if(showInfo) log.info("Exception: "+session.getRemoteAddress().toString(), ex);
sessions.remove(session);
}
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
if(showInfo) log.info("Incomming: "+session.getRemoteAddress().toString());
ByteBuffer data = (ByteBuffer) message;
// IN HEX DUMP
log.info(HexDump.prettyPrintHex(data.asReadOnlyBuffer().array()));
final byte type = data.get();
data.position(0);
switch(type){
case NOOP:
// drop
break;
case JOIN:
if(!sessions.contains(session)){
sessions.add(session);
join(session);
}
break;
case LIST:
list(session);
break;
case SEND:
broadcast(session, data);
break;
case EXIT:
if(sessions.contains(session)){
sessions.remove(session);
session.close();
leave(session);
}
break;
default:
if(showInfo) log.info("Unknown (play echo): "+session.getRemoteAddress().toString());
echo(session, data);
break;
}
}
protected void echo(IoSession session, ByteBuffer data){
session.write(data);
}
protected void broadcast(IoSession exclude, ByteBuffer data){
for(IoSession session : sessions){
if(exclude != null && exclude.equals(session)) continue;
if(showInfo) log.info("Sending: "+session.getRemoteAddress().toString());
data.acquire();
session.write(data);
}
}
protected void list(IoSession to){
final int size = 1 + 4 + (sessions.size()*4);
ByteBuffer msg = ByteBuffer.allocate(size);
msg.put(LIST);
msg.putInt(sessions.size());
for(IoSession session : sessions){
msg.putInt(session.getRemoteAddress().hashCode());
}
msg.flip();
to.write(msg);
}
protected void leave(IoSession session){
final int size = 5;
ByteBuffer msg = ByteBuffer.allocate(size);
msg.put(EXIT);
msg.putInt(session.getRemoteAddress().hashCode());
msg.flip();
broadcast(null, msg);
}
protected void join(IoSession session){
final int size = 5;
ByteBuffer msg = ByteBuffer.allocate(size);
msg.put(JOIN);
msg.putInt(session.getRemoteAddress().hashCode());
msg.flip();
broadcast(null, msg);
}
@Override
public void sessionCreated(IoSession session) throws Exception {
if(showInfo) log.info("Created: {}", session.getRemoteAddress().toString());
}
protected class TimeoutTask extends TimerTask {
public void run(){
long kill = System.currentTimeMillis() - TIMEOUT;
LinkedList<IoSession> remove = new LinkedList<IoSession>();
for(IoSession session : sessions){
if(session.getLastReadTime() < kill){
if(showInfo) log.info("Timout: {}", session.getRemoteAddress().toString());
remove.add(session);
} else {
session.write(NOOP_MSG.asReadOnlyBuffer());
}
}
if(remove.size() == 0) return;
for(IoSession session : remove){
sessions.remove(session);
session.close();
}
for(IoSession session : remove){
leave(session);
}
}
}
}