現在、市場に出回っているほとんどの C/S モード取引システムには、一般に 2 つの対話方式があります。

1 つ目は、クライアントとサーバー間の通信モジュールとして JAVA Socket または JAVA NIO Socket を使用し、メッセージ構造を自分で編成してトランザクション情報の送受信を完了する方法です。この開発モードは複雑であり、開発者がシステム自体の通信モジュール、メッセージのデータ構造、暗号化モジュールなど。

2 つ目は、JAVA で最も一般的に使用される Tomcat である http サーバーを使用することです。クライアントとサーバーは、http メッセージを通じて通信します。開発者は、サーバーとクライアント間のリンクの詳細を気にする必要はありません。 http メッセージ構造のデータを保持し、標準に従ってメッセージの送受信を処理する必要があります。 ただし、このモデルでは、開発者が追加のミドルウェアである Tomcat を保守する必要があります。

仕事中に、http リクエストとレスポンス メッセージを自分で構築して解析する方法を説明した記事をたまたま目にしたので、Java NIO ベースの http サーバーを完成させるために、自分の NIO サーバー上で http メッセージを構築して解析することにしました。通信モジュールの実装内容を習得し、業務モジュールの開発を簡素化できる httpリクエストとレスポンスを処理するだけで済むと同時に、サービスを開始するmainメソッドを実装できるため、依存性とメンテナンスが軽減されますTomcatで。 実際には、Tomcat の簡易版を実装することになりますが、このサービスの実用性とセキュリティは当然ながら Tomcat とは比較にならないため、このモデルは興味のある学生との学習とコミュニケーションにのみ使用されます。

まず、HTTP リクエスト ヘッダー情報をカプセル化し、それを解析してオブジェクトに保存します:

package niosrv;

import java.io.BufferedReader;
import java.io.IOException;

/**
 * Parse and save HttpHead details
 */
public class NioHttpHead {

    public static final String ACCEPT = "Accept";
    public static final String ACCEPT_LANGUAGE = "Accept-Language";
    public static final String USER_AGENT = "User-Agent";
    public static final String ACCEPT_ENCODING = "Accept-Encoding";
    public static final String HOST = "Host";
    public static final String DNT = "DNT";
    public static final String CONNECTION = "Connection";
    public static final String COOKIE = "Cookie";
    private BufferedReader br;

    private String method;
    private String requestURL;
    private String protocol;
    private String agent;
    private String host;
    private int port;
    private String encoding;
    private String language;
    private String accept;
    private String dnt;
    private String connection;
    private String cookie;

    private String headStr;
    private String contentStr = "";

    public NioHttpHead(BufferedReader br) {
        this.br = br;
    }

    public void parseHead() {
        String s = null;
        String content = "";
        try {
            s = br.readLine();
            String[] firstLine = s.split(" ");
            if (firstLine.length == 3) {
                this.method = firstLine[0].trim();
                this.requestURL = firstLine[1].trim();
                this.protocol = firstLine[2].trim();
            }
            StringBuffer headBuffer = new StringBuffer(s + "\r\n");
            s = br.readLine();
            while (s != null && !s.equals("")) {
                String[] split = s.split(":");
                switch (split[0].trim()) {
                    case ACCEPT: {
                        this.accept = split[1].trim();
                    }
                    case ACCEPT_LANGUAGE: {
                        this.language = split[1].trim();
                        break;
                    }
                    case USER_AGENT: {
                        this.agent = split[1].trim();
                        break;
                    }
                    case ACCEPT_ENCODING: {
                        this.encoding = split[1].trim();
                        break;
                    }
                    case HOST: {
                        this.host = split[1].trim();
                        break;
                    }
                    case DNT: {
                        this.dnt = split[1].trim();
                        break;
                    }
                    case CONNECTION: {
                        this.connection = split[1].trim();
                        break;
                    }
                    case COOKIE: {
                        this.cookie = split[1].trim();
                        break;
                    }
                }
                s = br.readLine();
                headBuffer.append(s + "\r\n");
            }
            this.headStr = headBuffer.toString();
            //content parse, put request content into content attribute
            content = br.readLine();
            if (content != null && !content.equals("")) {
                StringBuffer contentBuffer = new StringBuffer(content);
                while (content != null && !content.equals("")) {
                    content = br.readLine();
                    if (content != null && !content.equals("")) {
                        contentBuffer.append(content);
                    }
                }
                this.contentStr = contentBuffer.toString();
            }
            System.out.println("Request HEAD message format:" + h eadBuffer.toString());
            System.out.println("Request BODY message format:" + contentStr.toString());
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Request HEAD message parse error:", e);
            System.out.println("Request BODY message parse error:", e);
        }
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getRequestURL() {
        return requestURL;
    }

    public void setRequestURL(String requestURL) {
        this.requestURL = requestURL;
    }

    public String getProtocol() {
        return protocol;
    }

    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }

    public String getAgent() {
        return agent;
    }

    public void setAgent(String agent) {
        this.agent = agent;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getEncoding() {
        return encoding;
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        this.language = language;
    }

    public String getAccept() {
        return accept;
    }

    public void setAccept(String accept) {
        this.accept = accept;
    }

    public String getDnt() {
        return dnt;
    }

    public void setDnt(String dnt) {
        this.dnt = dnt;
    }

    public String getConnection() {
        return connection;
    }

    public void setConnection(String connection) {
        this.connection = connection;
    }

    public String getCookie() {
        return cookie;
    }

    public void setCookie(String cookie) {
        this.cookie = cookie;
    }

    public String getHeadStr() {
        return headStr;
    }

    public void setHeadStr(String headStr) {
        this.headStr = headStr;
    }

    public String getContentStr() {
        return contentStr;
    }

    public void setContentStr(String contentStr) {
        this.contentStr = contentStr;
    }
}

Java で Writer クラスを書き換えて、NIO Socket経由で応答情報をクライアントに送り返します:

package niosrv;

import java.io.IOException;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;


public class NioHttpOutWriter extends Writer {

    //because of the pictures size are always too big, so the buffer size here is big
    ByteBuffer buffer = ByteBuffer.allocate(1024000);
    private SocketChannel channel;
    private Selector selector;
    private SelectionKey key;
    private String successHead;

    public NioHttpOutWriter(String successHeader, SelectionKey key, SocketChannel channel, Selector selector) {
        this.successHead = successHeader;
        this.key = key;
        this.channel = channel;
        this.selector = selector;
    }

    @Override
    public void write(char[] contentChar, int off, int len) throws IOException {
        System.out.println("Write Back By write(1,2,3)!");
        String content = new String(contentChar);
        StringBuffer respStr = new StringBuffer(this.successHead.toString());
        respStr.append(content);
        byte[] bytesBody = respStr.toString().getBytes();
        buffer.put(bytesBody);
        buffer.flip();
        channel.write(buffer);
        channel.register(selector, SelectionKey.OP_WRITE, this);
        key.cancel();
        channel.shutdownOutput();
        channel.close();
    }

    @Override

    public void flush() throws IOException {

    }

    @Override
    public void close() throws IOException {

    }

    public ByteBuffer getBuffer() {
        return buffer;
    }

    public void setBuffer(ByteBuffer buffer) {
        this.buffer = buffer;
    }

}

Java で PrintWriter クラスを書き換えて、応答情報を JSON や暗号化された暗号文などのテキスト形式でクライアントに送り返します:

package niosrv;

import java.io.PrintWriter;
import java.io.Writer;


public class NioHttpPrtWriter extends PrintWriter {

    public NioHttpPrtWriter(Writer write) {
        super(write);
    }

    public void writeAsString(String content) throws Exception {
        super.write(content.toCharArray(), 0, content.length());
    }

    @Override
    public void print(String s) {
        super.print(s);
    }
}

サーブレットに HttpServletRequest インターフェイスを実装し、独自の NIO Http Request リクエスト処理クラスを実装します。この実装クラスでは 3 つのアクションを実行します:

1,NIO Socket経由で HTTP メッセージを受信します。

2, メッセージ ヘッダーを解析し、メッセージ ヘッダーとメッセージ本文を別々に保存します。

3,メッセージ本文を解析して、メッセージ内のリクエスト パラメーターを取得します。リクエスト パラメーターは、JSON モード、URL アセンブリ、パラメーターなしモードの 3 つのモードをサポートします。

package niosrv;

import com.alibaba.fastjson.JSONObject;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.security.Principal;
import java.util.*;


public class NioHttpRequest implements HttpServletRequest {


    private SocketChannel channel;
    private NioHttpHead head;
    private Map<String, String> params = new HashMap<>();
    private String context;
    public byte[] reqContent;
    private SelectionKey key;

    public NioHttpRequest(SelectionKey key) {
        this.key = key;
        this.channel = (SocketChannel) this.key.channel();
    }

    public boolean parseHead(String projectName) throws Exception {
        String receive = receive(channel);
        if (null == receive || ("").equals(receive)) {
            return false;
        }
        //parse the request head
        BufferedReader br = new BufferedReader(new StringReader(receive));
        this.head = new NioHttpHead(br);
        this.head.parseHead();
        br.close();
        //parse the request content
        String content = this.head.getContentStr();
        boolean flag = parseParams(projectName, content);
        return true && flag;
    }

    private boolean parseParams(String projectName, String content) {
        String url = this.head.getRequestURL();
        //Block illegal request URLs
        if (!url.substring(1).startsWith(projectName)) {
            return false;
        }
        //The parameters are concatenated with the original URL
        if (url.indexOf("?") > 0) {
            this.context = url.substring(0, url.indexOf("?"));
            String params = url.substring(url.indexOf("?") + 1, url.length());
            String[] split = params.split("&");
            for (String param : split) {
                String[] split2 = param.split("=");
                this.params.put(split2[0], split2[1]);
            }
            reqContent = params.getBytes();
            //The parameter is stream mode, the structure is JSON or original mode x=1&y=2
        } else if (content != null && !("").equals(content)) {
            this.context = url.substring(url.indexOf("/"), url.length());
            try {
                if ((!content.contains("{") && !content.contains("}")) || content.indexOf("&") > 0) {
                    String[] split = content.split("&");
                    for (String param : split) {
                        String[] split2 = param.split("=");
                        this.params.put(split2[0], split2[1]);
                    }
                } else {
                    JSONObject json = JSONObject.parseObject(content);
                    for (String key : json.keySet()) {
                        this.params.put(key.toString(), json.get(key).toString());
                    }
                }
                reqContent = content.getBytes();
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
        //no parameters
        this.context = url.substring(url.indexOf("/"), url.length());
        return true;
    }

    private String receive(SocketChannel socketChannel) throws Exception {
        byte[] bytes = null;
        ByteBuffer receiveBuffer = null;
        ByteBuffer buffer = ByteBuffer.allocate(1024000);
        if (socketChannel.isOpen() && socketChannel.isConnected()) {
            int nBytes = 0;
            buffer.clear();
            try {
                nBytes = socketChannel.read(buffer);
            } catch (IOException e) {
                System.out.println("Socket Channel Can Not Read Buffer From Request,Socket Closed By Client In Exception!");
                //If the client is closed abnormally, it will also be closed here
                key.cancel();
                if (channel.isOpen()) {
                    channel.shutdownOutput();
                    channel.close();
                }
                return null;
            }
            buffer.flip();//open state
            if (nBytes > 0) {
                receiveBuffer = ByteBuffer.allocate(nBytes);
                receiveBuffer.clear();
                buffer.get(receiveBuffer.array());
                receiveBuffer.flip();
            }
            if (receiveBuffer != null) {
                bytes = receiveBuffer.array();
                return new String(bytes);
            } else {
                System.out.println("Socket Channel Can Not Read Buffer From Request,Socket Channel Closed By Client Unexpected!");
                //If the client is closed abnormally, it will also be closed here
                key.channel().close();
                key.cancel();
                if (channel.isOpen()) {
                    channel.shutdownOutput();
                    channel.close();
                }
                return null;
            }
        } else {
            System.out.println("Socket Channel Closed By Client Unexpected,Interrupt The Thread immidiately,!");
            return null;

        }
    }

    public byte[] getReqContent() {
        return reqContent;
    }

    @Override
    public String getParameter(String arg0) {
        return this.params.get(arg0);
    }

    @Override
    public String getContextPath() {
        this.context = context.substring(1, context.length());
        if (this.context.indexOf("/") > 0) {
            return this.context.substring(0, context.indexOf("/"));
        } else {
            return this.context;
        }
    }

    @Override
    public String getServletPath() {
        if (this.context.indexOf("/") == 2) {
            return this.context.substring(context.indexOf("/") + 1, context.lastIndexOf("/"));
        } else {
            return "";
        }
    }


    public String getNioHead() {
        if (null == head) {
            return "[error http head]";
        } else if (null == head.getHeadStr()) {
            return "[error http head]";
        } else {
            return head.getHeadStr();
        }
    }

    @Override
    public String getAuthType() {
        return null;
    }

    @Override
    public Cookie[] getCookies() {
        return new Cookie[0];
    }

    @Override
    public long getDateHeader(String s) {
        return 0;
    }

    @Override
    public String getHeader(String s) {
        return null;
    }

    @Override
    public Enumeration<String> getHeaders(String s) {
        return null;
    }

    @Override
    public Enumeration<String> getHeaderNames() {
        return null;
    }

    @Override
    public int getIntHeader(String s) {
        return 0;
    }

    @Override
    public String getMethod() {
        return this.head.getMethod();
    }

    @Override
    public String getPathInfo() {
        return null;
    }

    @Override
    public String getPathTranslated() {
        return null;
    }


    @Override
    public String getQueryString() {
        return null;
    }

    @Override
    public String getRemoteUser() {
        return null;
    }

    @Override
    public boolean isUserInRole(String s) {
        return false;
    }

    @Override
    public Principal getUserPrincipal() {
        return null;
    }

    @Override
    public String getRequestedSessionId() {
        return null;
    }

    @Override
    public String getRequestURI() {
        String wholeUrl = this.head.getRequestURL();
        if (wholeUrl.contains("?")) {
            return wholeUrl.substring(wholeUrl.lastIndexOf("/") + 1, wholeUrl.indexOf("?"));
        } else {
            return wholeUrl.substring(wholeUrl.lastIndexOf("/") + 1);
        }
    }

    @Override
    public StringBuffer getRequestURL() {
        return new StringBuffer(this.head.getRequestURL());
    }

    @Override
    public HttpSession getSession(boolean b) {
        return null;
    }

    @Override
    public HttpSession getSession() {
        return null;
    }

    @Override
    public String changeSessionId() {
        return null;
    }

    @Override
    public boolean isRequestedSessionIdValid() {
        return false;
    }

    @Override
    public boolean isRequestedSessionIdFromCookie() {
        return false;
    }

    @Override
    public boolean isRequestedSessionIdFromURL() {
        return false;
    }

    @Override
    public boolean isRequestedSessionIdFromUrl() {
        return false;
    }

    @Override
    public boolean authenticate(HttpServletResponse httpServletResponse) throws IOException, ServletException {
        return false;
    }

    @Override
    public void login(String s, String s2) throws ServletException {

    }

    @Override
    public void logout() throws ServletException {

    }

    @Override
    public Collection<Part> getParts() throws IOException, ServletException {
        return null;
    }

    @Override
    public Part getPart(String s) throws IOException, ServletException {
        return null;
    }

    @Override
    public <T extends HttpUpgradeHandler> T upgrade(Class<T> tClass) throws IOException, ServletException {
        return null;
    }

    @Override
    public Object getAttribute(String s) {
        return null;
    }

    @Override
    public Enumeration<String> getAttributeNames() {
        return null;
    }

    @Override
    public String getCharacterEncoding() {
        return null;
    }

    @Override
    public void setCharacterEncoding(String s) throws UnsupportedEncodingException {
    }

    @Override
    public int getContentLength() {
        if (reqContent == null) {
            return 0;
        } else {
            String content = new String(reqContent);
            return content.length();
        }
    }

    @Override
    public long getContentLengthLong() {
        return 0;
    }

    @Override
    public String getContentType() {
        return null;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return null;
    }

    @Override
    public Enumeration<String> getParameterNames() {
        return null;
    }

    @Override
    public String[] getParameterValues(String s) {
        return new String[0];
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        return null;
    }

    @Override
    public String getProtocol() {
        return this.head.getProtocol();
    }

    @Override
    public String getScheme() {
        return null;
    }

    @Override
    public String getServerName() {
        return null;
    }

    @Override
    public int getServerPort() {
        return 0;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return null;
    }

    @Override
    public String getRemoteAddr() {
        return channel.socket().getInetAddress().getHostAddress();
    }

    @Override
    public String getRemoteHost() {
        return channel.socket().getInetAddress().getHostAddress();
    }

    @Override
    public void setAttribute(String s, Object o) {

    }

    @Override
    public void removeAttribute(String s) {

    }

    @Override
    public Locale getLocale() {
        return null;
    }

    @Override
    public Enumeration<Locale> getLocales() {
        return null;
    }

    @Override
    public boolean isSecure() {
        return false;
    }

    @Override
    public RequestDispatcher getRequestDispatcher(String s) {
        return null;
    }

    @Override
    public String getRealPath(String s) {
        return null;
    }

    @Override
    public int getRemotePort() {
        return 0;
    }

    @Override
    public String getLocalName() {
        return null;
    }

    @Override
    public String getLocalAddr() {
        return null;
    }

    @Override
    public int getLocalPort() {
        return 0;
    }

    @Override
    public ServletContext getServletContext() {
        return null;
    }

    @Override
    public AsyncContext startAsync() throws IllegalStateException {
        return null;
    }

    @Override
    public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
        return null;
    }

    @Override
    public boolean isAsyncStarted() {
        return false;
    }

    @Override
    public boolean isAsyncSupported() {
        return false;
    }

    @Override
    public AsyncContext getAsyncContext() {
        return null;
    }

    @Override
    public DispatcherType getDispatcherType() {
        return null;
    }
}

サーブレットに HttpServletResponse インターフェースを実装し、独自の NIO Http Response 応答処理クラスを実装し、主に対応する Http クラスで応答の Http ヘッドを構築し、それが応答コンテンツとともにクライアントに書き戻されるのを待ちます:

package niosrv;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.*;


public class NioHttpResponse implements HttpServletResponse {

    private SocketChannel channel;
    private SelectionKey key;
    private Selector selector;

    public static final int HTTP_STATUS_SUCCESS = 200;

    public static final int HTTP_STATUS_NOT_FOUND = 404;
    //head message
    private final Map<String, String> headers = new HashMap<String, String>();

    private int responseCode = HTTP_STATUS_SUCCESS;
    private String responseInfo = "OK";

    private int responseError = HTTP_STATUS_NOT_FOUND;
    private String responseErrInfo = "SYS_ERROR";

    private String successHeader;


    public NioHttpResponse(SelectionKey key, Selector selector) {
        this.key = key;
        this.channel = (SocketChannel) this.key.channel();
        this.selector = selector;
        setUpHeader();
    }

    private void setUpHeader() {
        headers.put("Server", "HttpServer (Nio HttpServer 1.0)");
        headers.put("User-Agent", "*/*");
        headers.put("Content-Type", getContentType());
        headers.put("Connection", "keep-alive");
        String date = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US).format(new Date());
        headers.put("Date", date);
    }

    private String header2String(int resCode) {
        StringBuffer head = new StringBuffer("HTTP/1.1");
        head.append(" ").append(resCode == 0 ? responseCode : responseError).append(" ").append(resCode == 0 ? responseInfo : responseErrInfo).append("\r\n");
        head.append("Date:").append(" ").append(headers.get("Date")).append("\r\n");
        head.append("Server:").append(" ").append(headers.get("Server")).append("\r\n");
        head.append("User-Agent:").append(" ").append(headers.get("User-Agent")).append("\r\n");
        head.append("Content-Type:").append(" ").append(headers.get("Content-Type")).append("\r\n");
        head.append("Connection:").append(" ").append(headers.get("Connection")).append("\r\n");
        head.append("\r\n");
        return head.toString();
    }

    public void setContentType(String contentType) {
        headers.put("Content-Type", contentType);
    }

    public PrintWriter getWriter(int respCode) throws IOException {
        this.successHeader = header2String(respCode);
        return getWriter();
    }

     @Override
    public PrintWriter getWriter() throws IOException {
        NioHttpPrtWriter printWrite = new NioHttpPrtWriter(new NioHttpOutWriter( this.successHeader, key, channel, selector));
        return printWrite;
    }

    @Override
    public void addCookie(Cookie cookie) {

    }

    @Override
    public boolean containsHeader(String s) {
        return false;
    }

    @Override
    public String encodeURL(String s) {
        return null;
    }

    @Override
    public String encodeRedirectURL(String s) {
        return null;
    }

    @Override
    public String encodeUrl(String s) {
        return null;
    }

    @Override
    public String encodeRedirectUrl(String s) {
        return null;
    }

    @Override
    public void sendError(int i, String s) throws IOException {

    }

    @Override
    public void sendError(int i) throws IOException {

    }

    @Override
    public void sendRedirect(String s) throws IOException {

    }

    @Override
    public void setDateHeader(String s, long l) {

    }

    @Override
    public void addDateHeader(String s, long l) {

    }

    @Override
    public void setHeader(String s, String s2) {

    }

    @Override
    public void addHeader(String s, String s2) {
    }

    @Override
    public void setIntHeader(String s, int i) {

    }

    @Override
    public void addIntHeader(String s, int i) {

    }

    @Override
    public void setStatus(int i) {

    }

    @Override
    public void setStatus(int i, String s) {

    }

    @Override
    public int getStatus() {
        return 0;
    }

    @Override
    public String getHeader(String s) {
        return null;
    }

    @Override
    public Collection<String> getHeaders(String s) {
        return null;
    }

    @Override
    public Collection<String> getHeaderNames() {
        return null;
    }

    @Override
    public String getCharacterEncoding() {
        return null;
    }

    @Override
    public String getContentType() {
        return headers.get("Content-Type");
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return null;
    }

    @Override
    public void setCharacterEncoding(String s) {

    }

    @Override
    public void setContentLength(int i) {

    }

    @Override
    public void setContentLengthLong(long l) {

    }

    @Override
    public void setBufferSize(int i) {

    }

    @Override
    public int getBufferSize() {
        return 0;
    }

    @Override
    public void flushBuffer() throws IOException {

    }

    @Override
    public void resetBuffer() {

    }

    @Override
    public boolean isCommitted() {
        return false;
    }

    @Override
    public void reset() {

    }

    @Override
    public void setLocale(Locale locale) {

    }

    @Override
    public Locale getLocale() {
        return null;
    }
}

以上で第 1 セクションの内容となり、Nio を最下層として実装した Http リクエストとレスポンスの解析と構築が完了し、次のセクションに続きます。


发表回复

Thanks for your support to bet365fans!