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. 文章导航 Java NIO Socketに基づいて、HTTP リクエストとレスポンスを手動で解析し、Tomcatと同様のカスタマイズされたhttpサーバーを実装します(1) 以Java NIO Socket为基础,实现类似Tomcat的定制化http服务器,自己开发Tomcat(二)