At present, most of the trading systems of C/S mode on the market generally have two methods.

One is to use JAVA Socket or JAVA NIO Socket as the communication module between the client and the server, and organize the message structure by itself to complete the sending and receiving of transaction information. This development mode is complicated and requires developers to maintain the communication module of the system by themselves. The data structure of the message, the encryption module, etc.

The second is to use the http server, which is the most commonly used Tomcat in JAVA. The client and the server communicate through http messages. Developers do not need to care about the details of the link between the server and the client, but only need to maintain the data of the http message. structure, and then process sending and receiving messages according to the standard. But this model requires developers to maintain an additional middleware, which is Tomcat.

I happened to see an article at work, which explained how to build and parse http request and response messages by myself, so I decided to build and parse http messages on my own NIO Server, so that I can complete a Java NIO-based http Server. You can master the implementation details of the communication module and simplify the development of business modules. You only need to process http requests and responses. At the same time, you can implement the main method to start the service, reducing the dependence and maintenance on Tomcat.

In fact, it is to implement a simplified version of Tomcat, but the practicability and security of this service are of course incomparable with Tomcat, so this model is only used to learn and communicate with interested students.

First encapsulate the Http request header information, and parse and store it in the object:

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;
    }
}

Rewrite the Writer class in Java to send the response information back to the client through 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;
    }

}

Rewrite the PrintWriter class in Java to send the response information back to the client in the form of text, such as JSON or encrypted cipher text:

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);
    }
}

Implement the HttpServletRequest interface in the Servlet and implement your own NIO Http Request request processing class. There are three actions to be done in this implementation class:

1, Receive Http messages through NIO Socket.

2, Parse the message header, and store the message header and message body separately.

3, Parse the message body to obtain the request parameters in the message. The request parameters support three modes, namely JSON mode, URL assembly, and no-parameter mode.

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;
    }
}

Implement the HttpServletResponse interface in the Servlet, implement your own NIO Http Response response processing class, mainly construct the Http head of the response in the corresponding Http class, and wait for it to be written back to the client together with the response content:

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;
    }
}

The above is the content of the first section. The analysis and construction of the Http request and response implemented with Nio as the bottom layer are completed. The next section continues the following content.


发表回复

Thanks for your support to bet365fans!