「春MVCソースコード分析と実用的」音符(B)

ソケット与NioSocket

サーバソケットを開始するように、ソケットとソケットのServerSocketがServerSocketをサーバに対して、二つのカテゴリーに分け、()メソッドは、要求を聞いた後、ソケット、特定のデータ伝送のためのソケットを返し、受け入れ、クライアントを使用することができることにより、要求をリッスンリクエスト

受け入れる()ブロッキング方式であるクライアントソケットスレッドに対応したクライアントのネットワーク遅延、サーバは、サーバを待たなければならない場合は、サーバーモニタへの要求は、新しいスレッドを開くたびに、現在のソケットと通信するための責任がありますJDK1.4の初めから、IOの新しい方法があるので、このために、非常にリソースを消費するものです。NioSocket

人気例えば、レストランをデ食べ類推、クライアントとのメタファーのお客様には、専用のシェフと各顧客に伝統的なソケットと同等の(隠喩は新しいスレッドをServerSocketを)作る、アラカルト(メタファークライアントの準備ができていますデータ)顧客が(比喩クライアントの準備ができたデータである)、シェフが調理し始めたどのくらいの顧客が食べて良いものを望んでいるまで待機する時間のシェフを検討し、NIOはダウンモード、または顧客とクライアントの類推が、このレストランでは、唯一のシェフプラス十分なウェイターを取るとき、顧客が良いものを食べたい、それが(それはイベント駆動型である)ウェイターに通知し、その後、ウェイターが、その後、これらの要求がこのように扱わシェフに与えられる収集それ自体はマルチスレッドシングルスレッドでも解決にはなりました事を修正する必要があります

3つの主要な概念がありますNioSocket:

  • バッファ:サービスとクライアントのデータ間の交換
  • チャンネル:のServerSocketChannelとのSocketChannelは、上記の例では、シェフと対応する顧客を分割します
  • セレクタは、上記の例では、対応ウェイター、セレクタ管理チャネル、および対応するイベントを登録し、それはイベント駆動型モードでは、登録されたイベント準備ができて、イベントを処理するために、適切なスレッドを呼び出すのではなく、ソケットなどのような伝統的なかかりますスレッドとスレッドのオーバーヘッドを削減、持続的な接続を維持するために(注意:セレクタとチャンネルの関係に属している誰もが、彼らは「多くの多くは、」データベースの関係に似ていない、別のウェイターを呼び出すことができ、顧客は食べて自分の食べ物をもたらしました、ウェイターが)別の顧客にサービスを提供することができます

NioSocketシンプルなHTTPプロトコルの例:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;

public class HttpServer {
    public static void main(String[] args) throws Exception {
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.socket().bind(new InetSocketAddress(8080)); // 创建channel 并监听8080端口
        ssc.configureBlocking(false); // 设置为非阻塞模式,只有这样才能注册选择器
        Selector selector = Selector.open();
        ssc.register(selector, SelectionKey.OP_ACCEPT); // 为channel注册选择器
        // 创建处理器
        while (true) {
            // 等待请求,每次等待三秒,超过三秒后程序继续向下执行,如果传入参数0或不传参数将一直阻塞
            if (selector.select(3000) == 0) {
                continue;
            }
            Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                // 启动新线程处理SelectionKey
                new Thread(new HttpHandler(key)).run();
                // 处理完后,从待处理的集合中移除当前key
                keyIterator.remove();
            }
        }
    }

    private static class HttpHandler implements Runnable {
        private int bufferSize = 1024;
        private String localCharset = "UTF-8";
        private SelectionKey key;

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

        public void handleAccept() 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 sc = (SocketChannel)key.channel();
            // 获取buffer并重置
            ByteBuffer buffer = (ByteBuffer)key.attachment();
            buffer.clear();
            // 没有读到内容则关闭
            if (sc.read(buffer) == -1) {
                sc.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 (s.isEmpty()) {
                        break;
                    }
                }

                // 控制台打印首行信息
                String[] firstLine = requestMessage[0].split(" ");
                System.out.println();
                System.out.println("Method:\t" + firstLine[0]);
                System.out.println("url:\t" + firstLine[1]);
                System.out.println("Http Version:\t" + firstLine[2]);
                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));
                sc.write(buffer);
                sc.close();
            }
        }

        @Override
        public void run() {
            try {
                // 接收到连接请求
                if (key.isAcceptable()) {
                    handleAccept();
                }
                // 读数据
                if (key.isReadable()) {
                    handleRead();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

おすすめ

転載: www.cnblogs.com/yaoomoon/p/11716144.html