java.nio によるHTTPサーバのサンプル

単純に固定レスポンス返すだけのHTTPサーバのサンプル。やっつけです。

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;

public class NioHttpServer {
    private static final int DEFOULT_PORT = 80;
    private static final int TIMEOUT = 10 * 1000;
    private static final int BUFFERSIZE = 8192;
    
    private ServerSocketChannel serverChannel;
    private final int port;
    
    public NioHttpServer() throws IOException {
        this(DEFOULT_PORT);
    }
    public NioHttpServer(final int port) throws IOException {
        this.port = port;
        this.serverChannel = ServerSocketChannel.open();
        this.serverChannel.configureBlocking(false);
        this.serverChannel.socket().setReuseAddress(true);
        this.serverChannel.socket().bind(new InetSocketAddress(this.port));
    }
    
    public void start() throws IOException {
        Selector selector = Selector.open();
        serverChannel.register(selector, serverChannel.validOps());
        
        while(selector.keys().size() > 0) {
            selector.select(TIMEOUT);
            for(SelectionKey key :selector.selectedKeys()) {
                selector.selectedKeys().remove(key);
                if (!key.isValid()) continue;
                if (key.isAcceptable()) handleAcceptable(key);
                if (key.isReadable())   handleReadable(key);
                if (key.isWritable())   handleWritable(key);
            }
        }
    }

    // OP_ACCEPT の処理
    private void handleAcceptable(SelectionKey key) throws IOException {
        SocketChannel ch = ((ServerSocketChannel)key.channel()).accept();
        ch.configureBlocking(false);
        ByteBuffer buff = ByteBuffer.allocateDirect(BUFFERSIZE);
        ch.register(key.selector(), SelectionKey.OP_READ, buff);
    }
    
    // OP_READ の処理
    private void handleReadable(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel)key.channel();
        ByteBuffer buff = (ByteBuffer)key.attachment();
        channel.read(buff);
        printRequest(buff);
        mkResponse(buff);
        key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
    }

    // OP_WRITE の処理
    private void handleWritable(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel)key.channel();
        ByteBuffer buff = (ByteBuffer)key.attachment();
        buff.flip();
        channel.write(buff);
        buff.compact();
        if (buff.position()>0) {
            key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
        } else {
            channel.close();
        }
    }

    // リクエストデータをコンソールへ出力
    private void printRequest(ByteBuffer buff){
        buff.flip();
        byte[] bytes = new byte[buff.limit()];
        buff.get(bytes);
        buff.compact();
        System.out.println(new String(bytes));
    }
    
    // レスポンスデータ(固定)のBufferへの書き込み
    private void mkResponse(ByteBuffer buff) {
        buff.clear();
        String text = "HTTP/1.1 200 OK\n\n"
            + new Date() + " (" + Thread.currentThread() + ")";
        buff.put(text.getBytes());
    }
    
    public static void main(String[] args) throws IOException {
        new NioHttpServer().start();
    }
}

ExecutorService 利用版は

blog1.mammb.com