tomcat其实就是一个网络请求处理程序,如果需要重写,我们需要通过socket编程进行接受数据与发送数据。
getLocalHost #获取本机InetAddress对象
getByName #根据指定主机名/域名获取ip地址对象
getHostName #获取inetAddress对象的主机名
getHostAddress #获取InetAddress对象的地址
package com.socke.demo;import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddressDemo {
public static void main(String[] args) throws UnknownHostException {
//获取本机的InetAddress对象
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);
System.out.println("*******************************");
//根据主机名/域名获取uo地址对象
InetAddress host1 = InetAddress.getByName("DESKTOP-MSCGOJK");
System.out.println(host1);
System.out.println("*******************************");
InetAddress host2 = InetAddress.getByName("www.baidu.com");
System.out.println(host2);
//获取InetAddress对象的主机名
String hostName = host2.getHostName();
System.out.println(hostName);
System.out.println("*******************************");
//获取InetAddress对象的地址
String hostAddress = host2.getHostAddress();
System.out.println(hostName);
System.out.println("*******************************");
}
}
结果
DESKTOP-MSCGOJK/192.168.0.108
*******************************
DESKTOP-MSCGOJK/192.168.0.108
*******************************
www.baidu.com/39.156.66.14
www.baidu.com
*******************************
www.baidu.com
*******************************Process finished with exit code 0
构造方法
ServerSocket(int port) #创建绑定到特定端口的服务器套接字
ServerSocket(int port, int backlog) #用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号
ServerSocket(int port, int backlog, InetAddress address) #使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。
ServerSocket() #创建非绑定服务器套接字常用方法
getLocalPort() #返回此套接字在其上侦听的端口
accept() #侦听并接受到此套接字的连接
setSoTimeout(int timeout) #通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位
bind(SocketAddress host, int backlog) #将 ServerSocket 绑定到特定地址(IP 地址和端口号)
package com.socke.demo;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketServerTcpDemo {
public static void main(String[] args) throws IOException {
//1.在本机的9999端口监听,
ServerSocket serverSocket = new ServerSocket(9999);
//2.接受数据,如果没有数据接受会进行阻塞
Socket socket = serverSocket.accept();
//3.有链接,则返回Socket对象
System.out.println("socket=" + socket.getClass());
}
}
开启程序,并通过浏览器服务127.0.0.1:9999
可以看到这里控制台返回了数据,证明链接成功,因此我们可以找到浏览器其实就是一个客户端,重写tomcat的第一步我们就已经算是成功了,及与浏览器交互
构造方法
Socket(String host, int port) #创建一个流套接字并将其连接到指定主机上的指定端口号
Socket(InetAddress host, int port) #创建一个流套接字并将其连接到指定 IP 地址的指定端口号
Socket(String host, int port, InetAddress localAddress, int localPort) #创建一个套接字并将其连接到指定远程主机上的指定远程端口
Socket(InetAddress host, int port, InetAddress localAddress, int localPort) #创建一个套接字并将其连接到指定远程地址上的指定远程端口
Socket() #通过系统默认类型的 SocketImpl 创建未连接套接字常用方法
connect(SocketAddress host, int timeout) #将此套接字连接到服务器,并指定一个超时值
getInetAddress() # 返回套接字连接的地址
getPort() #返回此套接字连接到的远程端口
getLocalPort() #返回此套接字绑定到的本地端口
getRemoteSocketAddress() #返回此套接字连接的端点的地址,如果未连接则返回 null
getInputStream() #返回此套接字的输入流
getOutputStream() #返回此套接字的输出流
close() #关闭此套接字
建议的客户端
package com.socke.demo;import java.io.IOException;
import java.net.Socket;
public class SocketClientTcpDemo {
public static void main(String[] args) throws IOException {
//1.连接服务端(ip,端口)
Socket socket = new Socket("127.0.0.1",9999);
//2.返送数据
socket.getOutputStream().write("6666".getBytes());
//3.关闭socket
socket.close();
}
}
带回显内容的服务端
package com.socke.demo;import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketServerTcpDemo {
public static void main(String[] args) throws IOException {
//1.在本机的9999端口监听,
ServerSocket serverSocket = new ServerSocket(9999);
//2.接受数据,如果没有数据接受会进行阻塞
Socket socket = serverSocket.accept();
//3.有链接,则返回Socket对象
System.out.println("socket=" + socket.getClass());
//4.读取数据
InputStream inputStream = socket.getInputStream();
int readLen = 0;
byte[] buf = new byte[1024];
while((readLen = inputStream.read(buf))!=-1){
System.out.println(new String(buf,0,readLen));
}
//5.关闭资源
socket.close();
inputStream.close();
serverSocket.close();
}
}
启动服务,并利用浏览器服务127.0.0.1:9999
可以看到返回的内容为一个http请求头,还请求头是由浏览器生成发送的,但是由于我们并没有,返回数据给浏览器因此,浏览器一直获取不到任何内容,处于加载状态,接下来就是,返回数据至浏览器上
SocketServerTcpDemo
程序入口,只要用于实现多线程
package com.demo;import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class SocketServerTcpDemo {
public final static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(25,100,60,TimeUnit.SECONDS,new LinkedBlockingQueue());
public static void start() throws IOException {
//1.在本机的9999端口监听,
ServerSocket serverSocket = new ServerSocket(9999);
while(true){
//2.接受数据,如果没有数据接受会进行阻塞
Socket socket = serverSocket.accept();
//3.并行,线程池
threadPoolExecutor.execute(new SocketProcessor(socket));
}
}
public static void main(String[] args) throws IOException {
start();
}
}
SocketProcessor
socket进程任务,为socket数据发送与处理
package com.demo;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.Socket;
public class SocketProcessor implements Runnable{
Socket connection;
public SocketProcessor(Socket connection){
this.connection = connection;
}
public void run() {
byte[] requestBody = new byte[1024];
try {
connection.getInputStream().read(requestBody);
} catch (IOException e) {
e.printStackTrace();
}
String requestString = new String(requestBody);
//调用http工厂类,处理http请求
HttpFactory httpFactory = new HttpFactory();
HttpServletRequest httpServletRequest = httpFactory.createRequest(requestString.getBytes());
//获取请求方法和url
System.out.println(httpServletRequest.getServletPath());
System.out.println(httpServletRequest.getMethod());
//发送响应
HttpServletResponse httpServletResponse = httpFactory.createResponse(connection);
try {
String content = "输入的uri为:"+httpServletRequest.getServletPath()+"输入的参数为"+httpServletRequest.getParameter("name");
httpServletResponse.getOutputStream().write("HTTP/1.1 200 OK\r\n".getBytes());
httpServletResponse.getOutputStream().write(("Content-type: text/plain;charset=utf-8\r\n").getBytes());
httpServletResponse.getOutputStream().write(("Content-Length: "+(content.getBytes("utf-8").length)+"\r\n\r\n").getBytes());
httpServletResponse.getOutputStream().write(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
HttpFactory
http工厂类,用于将socket数据进行封装成http数据,方便发送以及处理
package com.demo;import javax.servlet.*;
import javax.servlet.http.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.security.Principal;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
public class HttpFactory{
public HttpServletRequest createRequest(final byte[] requestBody){
return new HttpServletRequest() {
....
//获取请求方法
public String getMethod() {
String requestString = new String(requestBody);
return requestString.split("\r\n")[0].split(" ")[0];
}
....
//获取请求路径
public String getServletPath() {
String requestString = new String(requestBody);
return requestString.split("\r\n")[0].split(" ")[1];
}
....
};
//获取参数内容
public String getParameter(String s) {
String requestString = new String(requestBody);
String uri = requestString.split("\r\n")[0].split(" ")[1];
System.out.println(uri);
String parameters = uri.split("\\?")[1];
String[] keyValues = parameters.split("&");
Map<String,String> map = new HashMap<String, String>();
for(String str : keyValues){
System.out.println(str.split("=")[0]);
map.put(str.split("=")[0],str.split("=")[1]);
}
return map.get(s);
}
}
public HttpServletResponse createResponse(final Socket connect){
return new HttpServletResponse() {
....
public ServletOutputStream getOutputStream() throws IOException {
return new ServletOutputStream(){
public void write(int b) throws IOException {
connect.getOutputStream().write(b);
}
};
}
....
};
}
效果
这里由于需要重写的方法太多,在这里就只重写了,后面经常用到的方法
到目前为止我们的tomcat已经可以完整的获取,处理,发送http数据。
在前面我们已经解决了网络通信的问题,接下来就是对接项目,及通过url访问我们的serverlet
ProjectConfigBean,通过读取xml获取serverlet信息
package com.demo;import jdk.internal.org.xml.sax.helpers.DefaultHandler;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.demo.BootStraper.work_space;
public class ProjectConfigBean extends DefaultHandler {
//Servlet 集合
Map<String,String> servlets = new HashMap<String, String>();
//Servlet 实例化
Map<String,Object> servletInstances = new HashMap<String, Object>();
//servlet 映射
Map<String,String> servletMapping = new HashMap<String, String>();
//项目
private String project;
//xml文件地址
private String xmlDir;
public ProjectConfigBean(String project) throws IOException, JDOMException {
//1.创建SAXBuilder对象
SAXBuilder saxBuilder = new SAXBuilder();
//2.创建输入流
InputStream is = new FileInputStream(new File(work_space+project+"/WEB-INF/web.xml"));
//3.将输入流加载到build中
Document document = saxBuilder.build(is);
//4.获取根节点
Element rootElement = document.getRootElement();
//5.获取子节点
List<Element> children = rootElement.getChildren();
for (Element child : children) {
//获取<servlet>中的标签
List<Element> childrenList = child.getChildren();
//判断servlet以及servlet-mapping
if(child.getName().equals("servlet")){
//将servlet添加至servlets
servlets.put(childrenList.get(0).getValue(),childrenList.get(1).getValue());
}
else if (child.getName().equals("servlet-mapping")){
//将servlet添加至servletMapping
servletMapping.put(childrenList.get(1).getValue(),childrenList.get(0).getValue());
}
}
}
public ProjectConfigBean loadXml() {
return this;
}
}
加载依赖以及class文件
package com.demo;import org.jdom.JDOMException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Map;
public class ProjectLoader {
//项目地址
private String project;
//类加载器
URLClassLoader loader = null;
public ProjectLoader(String project) throws MalformedURLException {
this.project = project;
//创建一个存储需要加载的class和jar包的数组
ArrayList<URL> urls = new ArrayList<URL>();
//读取lib下的jar包列表
File libs = new File(BootStraper.work_space+"/"+project+"WEB-INF/lib");
if(libs.exists()){
for (String lib: libs.list()){
urls.add(new URL("file:"+BootStraper.work_space+"/"+project+"WEB-INF/lib/"+lib));
}
}
//读取classes文件列表
urls.add(new URL("file:"+BootStraper.work_space+"/"+project+"WEB-INF/classes/"));
//加载项目的class和jar包
this.loader = new URLClassLoader(urls.toArray(urls.toArray(new URL[]{})));
}
//加载类
public ProjectLoader load(ProjectConfigBean projectConfigBean) throws IOException, JDOMException, ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException {
//设置所有线程都使用刚创建的加载器
Thread.currentThread().setContextClassLoader(this.loader);
//遍历出所有的类
for (Map.Entry<String,String> entry:projectConfigBean.servlets.entrySet()){
//利用反射加载对象
Class<?> clazz = loader.loadClass(entry.getValue());
//实例化
Servlet servlet = (Servlet) clazz.newInstance();
//servlet初始化
servlet.init(new ServletConfig() {
public String getServletName() {
return null;
}
public ServletContext getServletContext() {
return null;
}
public String getInitParameter(String s) {
return null;
}
public Enumeration<String> getInitParameterNames() {
return null;
}
});
//保存对象,如果不保存在projectConfigBean中,创建的对象就会被销毁掉,前面做的所有都将是徒劳
projectConfigBean.servletInstances.put(entry.getKey(),servlet);
}
return this;
}
}
关联servlet和url
package com.demo;import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.Socket;
public class SocketProcessor implements Runnable{
Socket connection;
public SocketProcessor(Socket connection){
this.connection = connection;
}
public void run() {
byte[] requestBody = new byte[1024];
try {
connection.getInputStream().read(requestBody);
} catch (IOException e) {
e.printStackTrace();
}
String requestString = new String(requestBody);
//调用http工厂类,处理http请求
HttpFactory httpFactory = new HttpFactory();
HttpServletRequest httpServletRequest = httpFactory.createRequest(requestString.getBytes());
//获取请求方法和url
System.out.println(httpServletRequest.getServletPath());
System.out.println(httpServletRequest.getMethod());
//发送响应
HttpServletResponse httpServletResponse = httpFactory.createResponse(connection);
//获取请求路径,匹配servlet
try {
BootStraper bootStraper = new BootStraper();
// 1.处理/
String servletName = bootStraper.projectConfigBeans.get("").servletMapping.get("/");
//2.通过url加载
if(servletName == null){
servletName = bootStraper.projectConfigBeans.get("").servletMapping.get(httpServletRequest.getServletPath());
}
//3.如果没有找到
if (servletName == null){
try {
httpServletResponse.getOutputStream().write("404".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
//通过Servlet名称,获取对应的servlet实例
Servlet servlet = (Servlet)bootStraper.projectConfigBeans.get("").servletInstances.get(servletName);
//将httpServletRequest,httpServletResponse传入servlet
try {
servlet.service(httpServletRequest,httpServletResponse);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.demo;import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class SocketServerTcpDemo {
public final static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(25,100,60,TimeUnit.SECONDS,new LinkedBlockingQueue());
public static void start() throws IOException {
//1.在本机的9999端口监听,
ServerSocket serverSocket = new ServerSocket(9999);
while(true){
//2.接受数据,如果没有数据接受会进行阻塞
Socket socket = serverSocket.accept();
//3.并行,线程池
threadPoolExecutor.execute(new SocketProcessor(socket));
}
}
public static void main(String[] args) throws IOException {
start();
}
}
package com.naihe;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String content = "这里是MyServlet,输入的uri为:"+req.getServletPath()+"输入的参数为"+req.getParameter("name");
resp.getOutputStream().write("HTTP/1.1 200 OK\r\n".getBytes());
resp.getOutputStream().write(("Content-type: text/plain;charset=utf-8\r\n").getBytes());
resp.getOutputStream().write(("Content-Length: "+(content.getBytes("utf-8").length)+"\r\n\r\n").getBytes());
resp.getOutputStream().write(content.getBytes());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getOutputStream().write("666".getBytes());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"> <!--注册Servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.naihe.MyServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>hello2</servlet-name>
<servlet-class>com.naihe.MyServlet</servlet-class>
</servlet>
<!--Servlet的请求路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello2</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
</web-app>