org.reunionemu.jreunion.server.Network.java Source code

Java tutorial

Introduction

Here is the source code for org.reunionemu.jreunion.server.Network.java

Source

package org.reunionemu.jreunion.server;

import java.io.IOException;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.reunionemu.jreunion.events.Event;
import org.reunionemu.jreunion.events.EventDispatcher;
import org.reunionemu.jreunion.events.EventListener;
import org.reunionemu.jreunion.events.network.NetworkAcceptEvent;
import org.reunionemu.jreunion.events.network.NetworkDataEvent;
import org.reunionemu.jreunion.events.network.NetworkDisconnectEvent;
import org.reunionemu.jreunion.events.network.NetworkSendEvent;
import org.reunionemu.jreunion.events.server.ServerEvent;
import org.reunionemu.jreunion.events.server.ServerStartEvent;
import org.reunionemu.jreunion.events.server.ServerStopEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

/**
 * @author Aidamina
 * @license http://reunion.googlecode.com/svn/trunk/license.txt
 */
@Service

public class Network extends EventDispatcher implements Runnable, EventListener {

    private static Logger logger = LoggerFactory.getLogger(Network.class);

    private final ByteBuffer buffer = ByteBuffer.allocate(1024 * 64);

    private Selector selector;

    private Thread thread;

    public Network() throws Exception {
        super();
        selector = Selector.open();

    }

    @Autowired
    Server server;

    @PostConstruct
    public void init() {

        server.addEventListener(ServerEvent.class, this);
        thread = new Thread(this);
        thread.setDaemon(true);
        thread.setName("network");
        //thread.start();

    }

    @Override
    public void run() {
        logger.info("network thread starting");
        while (true) {
            try {
                // See if we've had any activity -- either an incoming connection,
                // or incoming data on an existing connection
                int num = selector.select();
                if (num == 0) {
                    // we need synchronize here otherwise we might block again before we were able to change the selector
                    synchronized (this) {
                        continue;
                    }
                }

                // If we don't have any activity, loop around and wait again
                // Get the keys corresponding to the activity
                // that has been detected, and process them one by one

                Set<SelectionKey> keys = selector.selectedKeys();
                Iterator<SelectionKey> it = keys.iterator();
                while (it.hasNext()) {
                    // Get a key representing one of bits of I/O activity
                    SelectionKey key = it.next();
                    if (!key.isValid())
                        continue;

                    SelectableChannel selectableChannel = key.channel();
                    // What kind of activity is it?
                    if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {

                        // It's an incoming connection.
                        // Register this socket with the Selector
                        // so we can listen for input on it                  

                        SocketChannel clientSocketChannel = ((ServerSocketChannel) selectableChannel).accept();

                        processAccept(clientSocketChannel);

                    } else {
                        SocketChannel socketChannel = (SocketChannel) selectableChannel;

                        if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {

                            // It's incoming data on a connection, so process it
                            boolean ok = processInput(socketChannel);

                            // If the connection is dead, then remove it
                            // from the selector and close it
                            if (!ok) {
                                LoggerFactory.getLogger(Network.class).info("Client Connection Lost");
                                key.cancel();
                                disconnect(socketChannel);
                            }

                        } else if ((key.readyOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {

                            boolean ok = processOutput(socketChannel);
                            if (ok) {
                                socketChannel.register(selector, SelectionKey.OP_READ);
                            }
                        }
                    }
                }
                // We remove the selected keys, because we've dealt with them.
                keys.clear();

            } catch (Exception e) {
                if (e instanceof ClosedSelectorException || e instanceof InterruptedException)
                    return;
                LoggerFactory.getLogger(Network.class).error("Error in network", e);
            }
        }

    }

    private void processAccept(SocketChannel socketChannel) throws IOException {

        socketChannel.configureBlocking(false);

        fireEvent(NetworkAcceptEvent.class, socketChannel);

        // Register it with the selector, for reading
        selector.wakeup();
        socketChannel.register(selector, SelectionKey.OP_READ);

    }

    private boolean processInput(SocketChannel socketChannel) {

        int result = -1;
        Client client = null;
        buffer.clear();
        try {
            result = socketChannel.read(buffer);
            buffer.flip();
            client = Server.getInstance().getWorld().getClients().get(socketChannel);
        } catch (IOException e) {
            LoggerFactory.getLogger(this.getClass()).error("Exception", e);
        }

        // If no data or client, close the connection
        if (result <= 0 || client == null) {
            return false;
        }

        byte[] data = new byte[result];
        buffer.get(data, 0, result);

        fireEvent(NetworkDataEvent.class, socketChannel, data);

        return true;
    }

    private boolean processOutput(SocketChannel socketChannel) {

        Client client = Server.getInstance().getWorld().getClients().get(socketChannel);

        if (client == null)
            return false;

        if (!socketChannel.isOpen() || !socketChannel.isConnected()) {
            disconnect(socketChannel);
            return false;
        }
        buffer.clear();
        byte[] packetBytes = client.flush();
        if (packetBytes == null)
            return true;
        buffer.put(packetBytes);
        buffer.flip();

        try {
            socketChannel.write(buffer);
        } catch (IOException e) {
            LoggerFactory.getLogger(this.getClass()).error("Exception", e);
            disconnect(socketChannel);
            return false;
        }
        return true;
    }

    public void disconnect(SocketChannel socketChannel) {

        if (socketChannel.isConnected() && socketChannel.isOpen()) {
            processOutput(socketChannel);
        }
        LoggerFactory.getLogger(Network.class)
                .info("Disconnecting {local=" + socketChannel.socket().getLocalSocketAddress() + " remote="
                        + socketChannel.socket().getRemoteSocketAddress() + "}\n");
        fireEvent(NetworkDisconnectEvent.class, socketChannel);

        try {
            socketChannel.close();
        } catch (IOException e) {
            LoggerFactory.getLogger(this.getClass()).warn("Exception", e);
        }
    }

    public void notifySend(SocketChannel socketChannel) {
        try {
            // we synchronize this to make sure we register the key before the selector gets back to sleep again.
            if (socketChannel.isOpen() && selector.isOpen()) {
                selector.wakeup();
                socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);

            }

        } catch (ClosedChannelException e) {
            //Disconnect detected
        } catch (CancelledKeyException e) {

        } catch (Exception e) {

            LoggerFactory.getLogger(this.getClass()).error("Exception", e);

        }
    }

    public void start() {

        thread.start();

    }

    public boolean register(InetSocketAddress address) {
        try {
            ServerSocketChannel serverChannel = ServerSocketChannel.open();
            ServerSocket serverSocket = serverChannel.socket();
            serverSocket.bind(address);
            serverChannel.configureBlocking(false);
            synchronized (this) {
                selector.wakeup();
                serverChannel.register(selector, SelectionKey.OP_ACCEPT);
            }

        } catch (Exception e) {
            if (e instanceof BindException) {
                LoggerFactory.getLogger(Network.class)
                        .error("Port " + address.getPort() + " not available. Is the server already running?", e);
                return false;
            }
        }
        return true;
    }

    public void stop() {
        try {
            LoggerFactory.getLogger(Network.class).info("net stop");
            selector.close();
            thread.interrupt();
        } catch (IOException e) {
            LoggerFactory.getLogger(this.getClass()).warn("Exception", e);
        }
    }

    @Override
    public void handleEvent(Event event) {
        if (event instanceof NetworkSendEvent) {
            NetworkSendEvent networkSendEvent = (NetworkSendEvent) event;
            this.notifySend(networkSendEvent.getSocketChannel());
        } else if (event instanceof ServerStartEvent) {
            start();
        } else if (event instanceof ServerStopEvent) {
            stop();
        }
    }
}