新建maven项目Minicat, 需要做的事情,作为一个服务器软件提供服务的,我们可以通过浏览器客户端发送http请求,Minicat可以接收到请求进行处理,处理之后可以返回浏览器客户端。

1)提供服务,接收请求(Socket通信)

2)请求信息封装成Request对象,Response对象

3)客户端请求资源(静态资源html 和动态资源servlet)

4)资源返回给客户端浏览器

首先要实现的功能是浏览器请求http://localhost:8080,返回一个固定的字符串到页面“”“Hello minicat”

新建一个BootStrap主类,定义端口号,程序启动的入口

/**
* Minicat主类
*/
public class BootStrap {
/**
* 定义socket监听的端口号
*/
private int port = 8080;

public int getPort() {
return port;
}

public void setPort(int port) {
this.port = port;
}

/**
* Minicat启动需要初始化展开的一些操作
*/
public void start() throws Exception {

ServerSocket serverSocket = new ServerSocket(port);
System.out.println("===>>minicat start on port" + port);
while (true) {
Socket socket = serverSocket.accept();
//有了socket, 接收到请求,获取输出流
OutputStream outputStream = socket.getOutputStream();
String data = "Hello minicat";

String responseText = HttpProtocolUtil.getHttpHeader200(data.getBytes().length) + data;
outputStream.write(responseText.getBytes());

socket.close();
}

}

public static void main(String[] args) {
BootStrap bootStrap = new BootStrap();
try {
//启动
bootStrap.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}

Http协议工具类,如果没有这个步骤,会报failed to load response data

/*
http协议工具类,主要是提供响应头信息,这里只提供200和404的情况
*/
public class HttpProtocolUtil {
/**
*为响应码200提供请求头信息
*/
public static String getHttpHeader200(int length) {

return "HTTP/1.1 200 OK \n" +
"Content-Type:text/html\n" +
"Content-Length:" + length + "\n" +
"\r\n";
}
/**
* 为响应码404提供请求头信息
* */
public static String getHttpHeader404() {
String str404 = "<h1>404 not found</h1>";

return "HTTP/1.1 404 NOT Found \n" +
"Content-Type: text/html \n" +
"Content-Length: " + str404.getBytes().length + " \n" +
"\r\n" + str404;
}
}

启动BootStrap,在浏览器输入http://localhost:8080,此时会输出Hello minicat
查看请查看当前的网页的一些网络请求

 

 



然后封装Request和Response对象,返回html静态资源文件。

在resources下建立index.html,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>static resouce</title>
</head>
<body>
Hello Minicat-static resouce!
</body>
</html>

修改BootStrap中的start方法
/**
* 封装Request和Response对象,返回html静态资源文件
*/
while (true) {
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();

//封装Request和Response对象
Request request = new Request(inputStream);
Response response = new Response(socket.getOutputStream());
response.outputHtml(request.getUrl());

socket.close();
}

public class Request {
private String url;//例如/,/index.html

private String method;//请求方式,比如get/post

private InputStream inputStream;//输入流,其他属性从输入流中解析出来

public Request(InputStream inputStream) throws IOException {
this.inputStream = inputStream;

//从输入流中获取请求信息
int count = 0;
while (count == 0) {
count = inputStream.available();
}
byte[] bytes = new byte[count];
inputStream.read(bytes);

String inputStr = new String(bytes);
//获取第一行请求头信息
String firstLinestr = inputStr.split("\\n")[0];//GET / HTTP/1.1
String[] strings = firstLinestr.split(" ");
this.method = strings[0];
this.url = strings[1];
System.out.println("----method" + method);
System.out.println("----url" + url);


}

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;
}
}
public class Response {
private OutputStream outputStream;

public Response() {
}

public Response(OutputStream outputStream) {
this.outputStream = outputStream;
}

public void output(String content) throws IOException {
this.outputStream.write(content.getBytes());
}

public void outputHtml(String path) throws Exception {
String absoluteResourcePath = StaticResourceUtil.getAbsolutePath(path);
File file = new File(absoluteResourcePath);
if (file.exists() && file.isFile()) {
StaticResourceUtil.outputStaticResource(new FileInputStream(file), this.outputStream);
} else {
this.output(HttpProtocolUtil.getHttpHeader404());
}

}
}
public class StaticResourceUtil {
public StaticResourceUtil() {
}

public static String getAbsolutePath(String path) {
String path1 = StaticResourceUtil.class.getResource("/").getPath();
System.out.println("absolutepath:" + path1);
String var10000 = path1.replaceAll("\\\\", "/");
return var10000 + path;
}

public static void outputStaticResource(InputStream inputStream, OutputStream outputStream) throws IOException {
int count;
for(count = 0; count == 0; count = inputStream.available()) {
}

int resourceSize = count;
outputStream.write(HttpProtocolUtil.getHttpHeader200(count).getBytes());
long written = 0L;
int byteSize = 1024;

for(byte[] bytes = new byte[byteSize]; written < (long)resourceSize; written += (long)byteSize) {
if (written + (long)byteSize > (long)resourceSize) {
byteSize = (int)((long)resourceSize - written);
bytes = new byte[byteSize];
}

inputStream.read(bytes);
outputStream.write(bytes);
outputStream.flush();
}

}
}

打开浏览器输入http://localhost:8080/index.html,此时打印出index.html中内容。

最后,实现功能请求动态资源(Servlet)

在resources下新建web.xml内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<web-app>
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>server.TestServlet</servlet-class>
</servlet>


<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
</web-app>
BootStrap类中的start方法修改如下:
public void start() throws Exception {
//加载解析相关的配置web.xml
loadServlet();

ServerSocket serverSocket = new ServerSocket(port);
System.out.println("===>>minicat start on port" + port);

/**
* 可以请求动态资源(Servlet)
*/
while (true) {
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
//封装Request和Response对象
Request request = new Request(inputStream);
Response response = new Response(socket.getOutputStream());

if (servletMap.get(request.getUrl()) == null) {
//静态资源处理
response.outputHtml(request.getUrl());
} else {
//动态资源servlet请求
HttpServlet httpServlet = servletMap.get(request.getUrl());
httpServlet.service(request, response);
}
socket.close();
}

}


类中添加以下内容解析web.xml中的servlet放入map集合中:
private Map<String, HttpServlet> servletMap = new HashMap<>();

/**
* 加载解析web.xml,初始化servlet
*/
private void loadServlet() throws Exception {
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");

SAXReader saxReader = new SAXReader();
Document read = saxReader.read(resourceAsStream);
Element rootElement = read.getRootElement();
List<Element> selectNodes = rootElement.selectNodes("//servlet");

for (int i = 0; i < selectNodes.size(); i++) {
Element element = selectNodes.get(i);
Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
String name = servletnameElement.getStringValue();
Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
String selvletClass = servletclassElement.getStringValue();
//根据servlet-name的值找到url-pattern
Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='"+ name + "']");
String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();

servletMap.put(urlPattern, (HttpServlet) Class.forName(selvletClass).newInstance());

}
}
新建Servlet 
public interface Servlet {

void init();

void destory();

void service(Request request, Response response);
}

public abstract class HttpServlet implements Servlet{

@Override
public void service(Request request, Response response) {

if ("GET".equalsIgnoreCase(request.getMethod())) {
doGet(request, response);
} else {
doPost(request, response);
}
}

public abstract void doGet(Request request, Response response);

public abstract void doPost(Request request, Response response);
}
根据web.xml中配置的servlet,在相应位置新建此类继承httpServlet,可以处理具体的get和post请求
public class TestServlet extends HttpServlet{
@Override
public void init() {

}

@Override
public void destory() {

}

@Override
public void doGet(Request request, Response response){
String content = "<h1>testServlet get</h1>";
try {
response.output(HttpProtocolUtil.getHttpHeader200(content.getBytes().length) + content);
} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public void doPost(Request request, Response response) {

String content = "<h1>testServlet post</h1>";
try {
response.output(HttpProtocolUtil.getHttpHeader200(content.getBytes().length) + content);
} catch (IOException e) {
e.printStackTrace();
}
}
}

重启项目,输入http://localhost:8080/test,此时输出:

 

 优化: 使用线程池处理

//定义一个线程池
int corePoolSize = 10;
int maximumPoolSize =50;
long keepAliveTime = 100L;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler
);

while (true) {
Socket accept = serverSocket.accept();
RequestProcessor processor = new RequestProcessor(accept, servletMap);
threadPoolExecutor.execute(processor);
}
RequestProcessor 类继承了Thread实现run方法,run方法里面处理具体请求。
public class RequestProcessor extends Thread{
private Socket socket;
private Map<String,HttpServlet> servletMap;
public RequestProcessor(Socket socket, Map<String,HttpServlet> servletMap) {
this.socket = socket;
this.servletMap = servletMap;
}

@Override
public void run() {
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
//封装request和response对象
Request request = new Request(inputStream);
Response response = new Response(socket.getOutputStream());
if (servletMap.get(request.getUrl()) == null) {
//静态资源处理
response.outputHtml(request.getUrl());
} else {
//动态资源servlet请求
HttpServlet httpServlet = servletMap.get(request.getUrl());
httpServlet.service(request, response);
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}

}
}









posted on 2021-07-18 18:06  jeolyli  阅读(64)  评论(0编辑  收藏  举报