单线程版本:
转载自:https://blog.csdn.net/weixin_39033443/article/details/83901722
分以下几个步骤:
(1)提供Socket服务
(2)把请求和响应封装成request/response
(3)进行请求的转发
代码实现如下:
1、工程截图:
2、封装请求对象:通过输入流,对HTTP协议进行解析,拿到了HTTP请求头的方法和URL:
/**
* @author wangjie
* @version 2018/11/9
* 封装请求对象
* 通过输入流,对http协议进行解析,拿到http请求头的方法和url
*/
public class MyRequest {
private String url; // 请求url
private String method; // 请求方法
public MyRequest(InputStream inputStream) throws IOException{
String httpRequest ="";
byte[] httpRequestBytes =new byte[1024];
int length =0;
if((length=inputStream.read(httpRequestBytes)) >0){
httpRequest=new String(httpRequestBytes,0,length);
}
String httpHead = httpRequest.split("\n")[0]; // 切割换行符“\n”
url=httpHead.split("\\s")[1]; // 切割空格“\\s”
method=httpHead.split("\\s")[0];
System.out.println(this);
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
}
小结:
String httpHead = httpRequest.split("\n")[0]; // 切割换行符“\n”
String url=httpHead.split("\\s")[1]; // 切割空格“\\s”
3、封装响应对象:基于HTTP协议的格式进行输出写入。
/**
* @author wangjie
* @version 2018/11/9
* 封装响应对象
* 基于HTTP协议的格式进行输出写入。
*/
public class MyResponse {
private OutputStream outputStream;
public MyResponse(OutputStream outputStream){
this.outputStream = outputStream;
}
public void write(String content)throws IOException {
StringBuffer httpResponse = new StringBuffer();
httpResponse.append("HTTP/1.1 200 OK\n")
.append("Content-Type: text/html\n")
.append("\r\n")
.append("<html><body>")
.append(content)
.append("</body></html>");
outputStream.write(httpResponse.toString().getBytes());
outputStream.close();
}
}
小结:
Response对象的write()方法,底层调用的是OutputStream的write()方法来往客户端进行写出响应
4、servlet请求处理基类:Tomcat是满足Servlet规范的容器,所以Tomcat需要提供API:doGet/doPost/service。
/**
* @author wangjie
* @version 2018/11/9
* Servlet请求处理基类
*/
public abstract class MyServlet {
public abstract void doGet(MyRequest myRequest,MyResponse myResponse);
public abstract void doPost(MyRequest myRequest,MyResponse myResponse);
public void service(MyRequest myRequest,MyResponse myResponse){ // 判断请求方法类型,进行请求转发
if(myRequest.getMethod().equalsIgnoreCase("POST")){
doPost(myRequest,myResponse);
}else if(myRequest.getMethod().equalsIgnoreCase("GET")){
doGet(myRequest,myResponse);
}
}
}
5、Servlet实现类:提供2个实现类,用于测试。
/**
* @author wangjie
* @version 2018/11/9
* servlet实现类
*/
public class FindGirlServlet extends MyServlet{
@Override
public void doGet(MyRequest myRequest,MyResponse myResponse){
try{
myResponse.write("get gril....");
}catch (IOException e){
e.printStackTrace();
}
}
@Override
public void doPost(MyRequest myRequest,MyResponse myResponse){
try{
myResponse.write("post girl...");
} catch (IOException e){
e.printStackTrace();
}
}
}
/**
* @author wangjie
* @version 2018/11/9
*/
public class HelloWorldServlet extends MyServlet {
@Override
public void doGet(MyRequest myRequest,MyResponse myResponse){
try{
myResponse.write("get world...");
}catch (IOException e){
e.printStackTrace();
}
}
@Override
public void doPost(MyRequest myRequest,MyResponse myResponse){
try{
myResponse.write("post world...");
}catch (IOException e){
e.printStackTrace();
}
}
}
6、Servlet配置:对比之前在web开发中,会在web.xml中通过和指定哪个URL交给哪个servlet来处理。
/**
* @author wangjie
* @version 2018/11/9
* servlet配置
*/
public class ServletMapping {
private String servletName;
private String url;
private String clazz;
public ServletMapping(String servletName, String url, String clazz){
this.servletName=servletName;
this.url=url;
this.clazz=clazz;
}
public String getServletName() {
return servletName;
}
public void setServletName(String servletName) {
this.servletName = servletName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
}
/**
* @author wangjie
* @version 2018/11/9
*/
public class ServletMappingConfig {
public static List<ServletMapping> servletMappingList =new ArrayList<>();
//制定哪个URL交给哪个servlet来处理
static{
servletMappingList.add(new ServletMapping("findGirl","/girl","wj.mytomcat.FindGirlServlet"));
servletMappingList.add(new ServletMapping("helloWorld","/world","wj.mytomcat.HelloWorldServlet"));
}
}
7、启动类:
tomcat的处理流程:
1)把URL对应处理的Servlet关系形成,
2)解析HTTP协议,
3)封装请求/响应对象,
4)利用反射实例化具体的Servlet进行请求处理。
/**
* @author wangjie
* @version 2018/11/9
* tomcat启动类
*/
public class MyTomcat {
private int port=8088;
private Map<String,String> urlServletMap =new HashMap<String,String>();
public MyTomcat(int port){
this.port=port;
}
public void start(){
// 初始化URL与对应处理的servlet的关系
initServletMapping();
ServerSocket serverSocket=null;
try{
serverSocket = new ServerSocket(port);
System.out.println("MyTomcat is start...");
while(true){
Socket socket= serverSocket.accept();
InputStream inputStream=socket.getInputStream();
OutputStream outputStream=socket.getOutputStream();
MyRequest myRequest= new MyRequest(inputStream);
MyResponse myResponse =new MyResponse(outputStream);
// 请求分发
dispatch(myRequest,myResponse);
socket.close();
}
}catch (IOException e){
e.printStackTrace();
}finally {
if (null != serverSocket){
try{
serverSocket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
private void initServletMapping(){
for(ServletMapping servletMapping:ServletMappingConfig.servletMappingList){
urlServletMap.put(servletMapping.getUrl(),servletMapping.getClazz());
}
}
public void dispatch(MyRequest myRequest,MyResponse myResponse){
String clazz =urlServletMap.get(myRequest.getUrl());
//反射:生成具体的Servlet实例
try{
Class<MyServlet> myServletClass =(Class<MyServlet>) Class.forName(clazz);
MyServlet myServlet= myServletClass.newInstance();
myServlet.service(myRequest,myResponse); // 调用Servlet的service()进行请求处理
}catch (ClassNotFoundException e){
e.printStackTrace();
}catch (InstantiationException e){
e.printStackTrace();
}catch (IllegalAccessException e){
e.printStackTrace();
}
}
public static void main(String[] args){
new MyTomcat(8088).start();
}
}
8、测试:
运行项目后,在浏览器输入:localhost:8088/girl
在浏览器输入:localhost:8088/world
实践完成。
多线程版本:
一个ClientRequestHandler负责接收一个clientSocket,从而处理一次客户端请求
package cn.huangyan.web.tomcat;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Map;
/*
* 一个ClientRequestHandler负责接收一个clientSocket,从而处理一次客户端请求
* */
public class ClientRequestHandler implements Runnable {
Socket clientSocket = null;
Map<String,String> urlServletMap = null;
public ClientRequestHandler(Socket clientSocket,Map<String,String> urlServletMap) {
this.clientSocket = clientSocket;
this.urlServletMap = urlServletMap;
}
@Override
public void run() {
OutputStream os = null;
InputStream is = null;
System.out.println("当前线程是:"+Thread.currentThread().getName());
try {
is = clientSocket.getInputStream();
os = clientSocket.getOutputStream();
MyRequest request = new MyRequest(is);
MyResponse response = new MyResponse(os);
doDispatch(request,response);
} catch (IOException e) {
e.printStackTrace();
}finally {
if(clientSocket != null) {
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void doDispatch(MyRequest request, MyResponse response) {
String url = request.getUrl();
String className = urlServletMap.get(url);
try {
Class clazz = Class.forName(className);
MyServlet servlet = (MyServlet) clazz.newInstance();
servlet.service(request, response);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
MyTomcat类
package cn.huangyan.web.tomcat;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class MyTomcat {
private int port = 8080;
private Map<String,String> urlServletMap = new HashMap<>();
public MyTomcat(int port) {
this.port = port;
}
public void start() {
initServletMapping();
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
while(true) {
Socket clientSocket = serverSocket.accept();
// 每个客户端请求分配一个线程进行处理
new Thread(new ClientRequestHandler(clientSocket, urlServletMap)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void initServletMapping() {
for(ServletMapping servletMapping : ServletMappingConfig.servletMappingList) {
urlServletMap.put(servletMapping.getUrl(), servletMapping.getClazzName());
}
}
public static void main(String[] args) {
System.out.println("进来了");
new MyTomcat(8080).start();
}
}