天天看點

自己動手實作HTTP協定

package designpattern.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;

public class NIOServer {

    public static void main(String[] args) throws IOException {
        // 建立ServerSocketChannel,監聽8080端口
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress());

        // 設定為非阻塞模式
        serverSocketChannel.configureBlocking(false);

        // 為serverSocketChannel注冊選擇器
        Selector selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 建立處理器
        while (true) {
            // 等待請求,每次等待阻塞3s,超過3s後線程繼續向下運作,如果傳入0或者不傳入參數将一直阻塞
            if (selector.select() == ) {
                System.out.println("等待請求逾時....");
                continue;
            }
            System.out.println("處理請求....");

            // 擷取待處理的SelectionKey
            Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
            while (keyIter.hasNext()) {
                SelectionKey key = keyIter.next();
                // 啟動新線程處理SelectionKey
                new Thread(new HttpHandler(key)).run();
                // 處理完畢,從待處理的SelectionKey疊代器中移除目前所使用的key
                keyIter.remove();
            }
        }
    }
}

package designpattern.nio;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;

import org.apache.commons.lang3.StringUtils;

public class HttpHandler implements Runnable {

    private int bufferSize = ;
    private String localCharset = "UTF-8";
    private SelectionKey key;

    public HttpHandler() {

    }

    public HttpHandler(SelectionKey key) {
        this.key = key;
    }

    public void handlerAccept() throws IOException {
        SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
        clientChannel.configureBlocking(false);
        clientChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize));
    }

    public void handleRead() throws IOException {
        // 擷取channel
        SocketChannel socketChannel = (SocketChannel) key.channel();

        // 擷取buffer并重置
        ByteBuffer buffer = (ByteBuffer) key.attachment();
        buffer.clear();

        // 沒有讀到内容則關閉
        if (socketChannel.read(buffer) == -) {
            socketChannel.close();
        } else {
            // 接收請求資料
            buffer.flip();
            String receivedString = Charset.forName(localCharset).newDecoder().decode(buffer).toString();

            // 控制台列印請求封包頭
            String[] requestMessage = receivedString.split("\r\n");
            for (String s : requestMessage) {
                System.out.println(s);

                // 遇到空行說明封包頭已經列印完
                if (StringUtils.isEmpty(s)) {
                    break;
                }
            }

            // 控制台列印首行資訊
            String[] firstLine = requestMessage[].split(" ");
            System.out.println();
            System.out.println("Method:\t" + firstLine[]);
            System.out.println("url:\t" + firstLine[]);
            System.out.println("HTTP Version:\t" + firstLine[]);
            System.out.println();

            // 傳回用戶端
            StringBuilder sendString = new StringBuilder();
            sendString.append("HTTP/1.1 200 OK\r\n");// 響應封包首行,200表示處理成功
            sendString.append("Content-Type:text/html;charset=" + localCharset + "\r\n");
            sendString.append("\r\n");// 封包頭結束後加一個空行
            sendString.append("<html><head><title>顯示封包</title></head></body>");
            sendString.append("接收到請求封包是:<br/>");
            for (String s : requestMessage) {
                sendString.append(s + "<br/>");
            }
            sendString.append("</body></html>");

            buffer = ByteBuffer.wrap(sendString.toString().getBytes(localCharset));
            socketChannel.write(buffer);
            socketChannel.close();
        }

    }

    @Override
    public void run() {
        try {
            // 接收到連接配接請求時
            if (key.isAcceptable()) {
                handlerAccept();
            }

            // 讀資料
            if (key.isReadable()) {
                handleRead();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
           
自己動手實作HTTP協定