手写一个民用Tomcat (06)

2024-04-19 12:20
文章标签 tomcat 手写 06 民用

本文主要是介绍手写一个民用Tomcat (06),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

我们这次是引入获取参数,比如你的GET 请求 或者post 请求 如何吧请求参数进行封装 成map 集合 。

先看下erquest。请求类里边改造

private void parseRequestLine()  这个方法 改造成 依据 ?进行分割处理因为
http://localhost:8080/servlet/com.yixin.HelloWorldServlet?name=jxd&age=18请求 要把参数拿出来 name=jxd&age=18

新增了protected void parseParameters()  解析参数方方法

新增了 private static void putMapEntry 存入参数的方法

这里post 的解析只给出application/x-www-form-urlencoded 格式的请求,至于我们常用的json 请求 后期找机会给出,因为相对简单,就是解析 json 格式 的数据 。

具体的实现逻辑如下(给出关键性代码):

public class JxdRequest implements HttpServletRequest {private InputStream input;private SocketInputStream sis;private String uri;InetAddress address;int port;protected HashMap<String, String> headers = new HashMap<>();protected Map<String, String[]> parameters = new ConcurrentHashMap<>();HttpRequestLine requestLine = new HttpRequestLine();private boolean parsed = false;private String queryString;public void parse(Socket socket) {try {input = socket.getInputStream();this.sis = new SocketInputStream(this.input, 2048);parseConnection(socket);this.sis.readRequestLine(requestLine);parseRequestLine();//解析数据parseHeaders();} catch (IOException e) {e.printStackTrace();} catch (ServletException e) {e.printStackTrace();}}private void parseRequestLine() {int question = requestLine.indexOf("?");if (question >= 0) {queryString=new String(requestLine.uri, question + 1, requestLine.uriEnd - question - 1);uri = new String(requestLine.uri, 0, question);} else {queryString = null;uri = new String(requestLine.uri, 0, requestLine.uriEnd);}}private void parseConnection(Socket socket) {address = socket.getInetAddress();port = socket.getPort();}private void parseHeaders() throws IOException, ServletException {while (true) {HttpHeader header = new HttpHeader();sis.readHeader(header);//表示读取完毕if (header.nameEnd == 0) {if (header.valueEnd == 0) {return;} else {throw new ServletException("httpProcessor.parseHeaders.colon");}}String name = new String(header.name, 0, header.nameEnd);String value = new String(header.value, 0, header.valueEnd);// 设置相应的请求头if (name.equals(DefaultHeaders.ACCEPT_LANGUAGE_NAME)) {headers.put(name, value);} else if (name.equals(DefaultHeaders.CONTENT_LENGTH_NAME)) {headers.put(name, value);} else if (name.equals(DefaultHeaders.CONTENT_TYPE_NAME)) {headers.put(name, value);} else if (name.equals(DefaultHeaders.HOST_NAME)) {headers.put(name, value);} else if (name.equals(DefaultHeaders.CONNECTION_NAME)) {headers.put(name, value);} else if (name.equals(DefaultHeaders.TRANSFER_ENCODING_NAME)) {headers.put(name, value);} else {headers.put(name, value);}}}protected void parseParameters() {String encoding = getCharacterEncoding();System.out.println(encoding);if (encoding == null) {encoding = "ISO-8859-1";}String qString = getQueryString();System.out.println("getQueryString:"+qString);if (qString != null) {byte[] bytes ;try {bytes = qString.getBytes(encoding);parseParameters(this.parameters, bytes, encoding);} catch (UnsupportedEncodingException e) {e.printStackTrace();;}}String contentType = getContentType();if (contentType == null)contentType = "";int semicolon = contentType.indexOf(';');if (semicolon >= 0) {contentType = contentType.substring(0, semicolon).trim();}else {contentType = contentType.trim();}if ("POST".equals(getMethod()) && (getContentLength() > 0) && "application/x-www-form-urlencoded".equals(contentType)) {try {int max = getContentLength();int len = 0;byte buf[] = new byte[getContentLength()];ServletInputStream is = getInputStream();while (len < max) {int next = is.read(buf, len, max - len);if (next < 0) {break;}len += next;}is.close();if (len < max) {throw new RuntimeException("Content length mismatch");}parseParameters(this.parameters, buf, encoding);}catch (UnsupportedEncodingException ue) {}catch (IOException e) {throw new RuntimeException("Content read fail");}}}/**** parseParameters 这个方法 举例 例如data name=jxd&age=18* 他会从0位置遍历到最后一位 同时设置两个指针一个ix 一个ox* ix 就是从char 数组0位开始遍历到最后* ox 是一个查找指针 ,当遇到 = 或者&时候,进行取舍,=前边表示key ,& 前边表示value 如果没有& 表示结尾* 要注意一个细节 当遇到 =或者& 时候会把ox 赋值0 ,但是为啥要    default: data[ox++] = c;* ,当我们遇到第一个=的时候 下一个是value=jxd 那么之前那个key(name) 就没有用了,因为已经赋值到map里边了 ,所以读取* jxd 时候覆盖掉前边的nam,然后 ox指针因为是从0开始 等遇到jxd后变边的&时候* 照样能把value=jxd 取出来 这样 一个数组 就能完成了,虽然data 原数组被改变 这样看似不太好但是,但是节省空间 不然你就要2个数组才能完成,一个取值一个* 放值 不得不说设计的很巧妙 。*/public void parseParameters(Map<String,String[]> map, byte[] data, String encoding)throws UnsupportedEncodingException {if (parsed)return;System.out.println(data);if (data != null && data.length > 0) {int    pos = 0;int    ix = 0;int    ox = 0;String key = null;String value = null;while (ix < data.length) {byte c = data[ix++];switch ((char) c) {case '&':value = new String(data, 0, ox, encoding);if (key != null) {putMapEntry(map,key, value);key = null;}ox = 0;break;case '=':key = new String(data, 0, ox, encoding);ox = 0;break;case '+':data[ox++] = (byte)' ';break;case '%':data[ox++] = (byte)((convertHexDigit(data[ix++]) << 4)+ convertHexDigit(data[ix++]));break;default:data[ox++] = c;}}//The last value does not end in '&'.  So save it now.//最后一个参数没有&结尾if (key != null) {value = new String(data, 0, ox, encoding);putMapEntry(map,key, value);}}parsed = true;}private byte convertHexDigit(byte b) {if ((b >= '0') && (b <= '9')) return (byte)(b - '0');if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);return 0;}/**** 这个方式是 存入map集合 因为有的value值对应多个key 所以是数组形式存储value*/private static void putMapEntry( Map<String,String[]> map, String name, String value) {String[] newValues = null;String[] oldValues = (String[]) map.get(name);if (oldValues == null) {newValues = new String[1];newValues[0] = value;} else {newValues = new String[oldValues.length + 1];System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);newValues[oldValues.length] = value;}map.put(name, newValues);}public String getUri() {return uri;}@Overridepublic String getMethod() {return new String(this.requestLine.method, 0, this.requestLine.methodEnd);}@Overridepublic Collection<Part> getParts() throws IOException, ServletException {return null;}@Overridepublic Part getPart(String s) throws IOException, ServletException {return null;}@Overridepublic <T extends HttpUpgradeHandler> T upgrade(Class<T> aClass) throws IOException, ServletException {return null;}@Overridepublic Object getAttribute(String s) {return null;}@Overridepublic Enumeration<String> getAttributeNames() {return null;}@Overridepublic String getCharacterEncoding() {return null;}@Overridepublic void setCharacterEncoding(String s) throws UnsupportedEncodingException {}@Overridepublic int getContentLength() {return Integer.parseInt(headers.get(DefaultHeaders.CONTENT_LENGTH_NAME));}@Overridepublic long getContentLengthLong() {return 0;}@Overridepublic String getContentType() {return headers.get(DefaultHeaders.CONTENT_TYPE_NAME);}@Overridepublic ServletInputStream getInputStream() throws IOException {return this.sis;}@Overridepublic String getParameter(String name) {parseParameters();String values[] = parameters.get(name);if (values != null)return (values[0]);elsereturn (null);}@Overridepublic Enumeration<String> getParameterNames() {parseParameters();return (Collections.enumeration(parameters.keySet()));}@Overridepublic String[] getParameterValues(String name) {parseParameters();String values[] = (String[]) parameters.get(name);if (values != null)return (values);elsereturn null;}}

这个类里边我们增加了

//这段代码是测试用,可以获取的 请求参数 支持get 和post
Map<String, String[]> map = requestFacade.getParameterMap();

这段代码 可以运行 测试的main 方法进行测试 看一下 map 里边的数据。

public class JxdServletProcessor {public void process(JxdRequest request, JxdResponse response) {
//首先根据uri最后一个/号来定位,后面的字符串认为是servlet名字String uri = request.getUri();String servletName = uri.substring(uri.lastIndexOf("/") + 1);URLClassLoader loader = null;try {
// create a URLClassLoaderURL[] urls = new URL[1];URLStreamHandler streamHandler = null;File classPath = new File(JxdHttpServer.WEB_ROOT);String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString();urls[0] = new URL(null, repository, streamHandler);loader = new URLClassLoader(urls);} catch (IOException e) {System.out.println(e.toString());}//由上面的URLClassLoader加载这个servletClass<?> servletClass = null;Servlet servlet = null;try {HttpRequestFacade requestFacade = new HttpRequestFacade(request);HttpResponseFacade responseFacade = new HttpResponseFacade(response);servletClass = loader.loadClass(servletName);response.setCharacterEncoding("UTF-8");response.addHeader(DefaultHeaders.CONTENT_TYPE_NAME,"text/html;charset=UTF-8");response.sendHeaders();//发送响应头//这段代码是测试用,可以获取的 请求参数 支持get 和postMap<String, String[]> map = requestFacade.getParameterMap();servlet = (Servlet) servletClass.newInstance();servlet.service(requestFacade, responseFacade);} catch (ClassNotFoundException | IOException e) {System.out.println(e.toString());} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (ServletException e) {e.printStackTrace();}}
}

这篇关于手写一个民用Tomcat (06)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/917526

相关文章

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

详解Tomcat 7的七大新特性和新增功能(1)

http://developer.51cto.com/art/201009/228537.htm http://tomcat.apache.org/tomcat-7.0-doc/index.html  Apache发布首个Tomcat 7版本已经发布了有一段时间了,Tomcat 7引入了许多新功能,并对现有功能进行了增强。很多文章列出了Tomcat 7的新功能,但大多数并没有详细解释它们

Tomcat性能参数设置

转自:http://blog.csdn.net/chinadeng/article/details/6591542 Tomcat性能参数设置 2010 - 12 - 27 Tomcat性能参数设置 博客分类: Java Linux Tomcat 网络应用 多线程 Socket 默认参数不适合生产环境使用,因此需要修改一些参数   1、修改启动时内存参数、并指定J

TL-Tomcat中长连接的底层源码原理实现

长连接:浏览器告诉tomcat不要将请求关掉。  如果不是长连接,tomcat响应后会告诉浏览器把这个连接关掉。    tomcat中有一个缓冲区  如果发送大批量数据后 又不处理  那么会堆积缓冲区 后面的请求会越来越慢。

前端-06-eslint9大变样后,如何生成旧版本的.eslintrc.cjs配置文件

目录 问题解决办法 问题 最近在写一个vue3+ts的项目,看了尚硅谷的视频,到了配置eslintrc.cjs的时候我犯了难,因为eslint从9.0之后重大更新,跟以前完全不一样,但是我还是想用和老师一样的eslintrc.cjs文件,该怎么做呢? 视频链接:尚硅谷Vue项目实战硅谷甄选,vue3项目+TypeScript前端项目一套通关 解决办法 首先 eslint 要

Tomcat下载压缩包解压后应有如下文件结构

1、bin:存放启动和关闭Tomcat的命令的路径。 2、conf:存放Tomcat的配置,所有的Tomcat的配置都在该路径下设置。 3、lib:存放Tomcat服务器的核心类库(JAR文件),如果需要扩展Tomcat功能,也可将第三方类库复制到该路径下。 4、logs:这是一个空路径,该路径用于保存Tomcat每次运行后产生的日志。 5、temp:保存Web应用运行过程中生成的临时文件

docker学习系列(四)制作基础的base项目镜像--jdk+tomcat

前面已经完成了docker的安装以及使用,现在我们要将自己的javaweb项目与docker结合 1.1准备jdk+tomcat软件 ​​我下载了apache-tomcat-7.0.68.tar.gz和jdk-7u79-linux-x64.tar.gz,存储于Linux机器的本地目录/usr/ect/wt/下(利用xshell上传)。利用linux命令 tar -zxvf apache-tom

stl的sort和手写快排的运行效率哪个比较高?

STL的sort必然要比你自己写的快排要快,因为你自己手写一个这么复杂的sort,那就太闲了。STL的sort是尽量让复杂度维持在O(N log N)的,因此就有了各种的Hybrid sort algorithm。 题主你提到的先quicksort到一定深度之后就转为heapsort,这种是introsort。 每种STL实现使用的算法各有不同,GNU Standard C++ Lib

Tomcat服务详解

一、部署Tomcat服务器 JDK安装官方网址:https://www.oracle.com/cn/java Tomcat安装官方网址:Apache Tomcat® - Welcome! 安装JDK 1.获取安装包wget https://download.oracle.com/otn/java/jdk/8u411-b09/43d62d619be4e416215729597d70b8ac

C++入门(06)安装QT并快速测试体验一个简单的C++GUI项目

文章目录 1. 清华镜像源下载2. 安装3. 开始菜单上的 QT 工具4. 打开 Qt Creator5. 简单的 GUI C++ 项目5.1 打开 Qt Creator 并创建新项目5.2 设计界面5.3 添加按钮的点击事件5.4 编译并运行项目 6. 信号和槽(Signals and Slots) 这里用到了C++类与对象的很多概念 1. 清华镜像源下载 https://