项目名称:Java版本精简Tomcat
项目功能描述:
1.实现静态文件服务器
2.动态实现接口
3.支持方法:Get方法
涉及技术:
1.网络编程(Java基础语法,Socket API)
2.面型对象的接口多态实现
3.HTTP协议
4.多线程技术
项目实现:
1.服务器端建立Socket链接
2.读取请求数据
2.1 解析请求数据,包装成请求对象
3.按照业务逻辑处理
3.1 不同的URL进行不同的处理
4.处理响应数据,包装成响应对象
将HTTP协议抽象成Java的三个类(请求类,响应类,处理器类),方法使用get()方法
处理器类:
服务器建立Socket链接,绑定ip地址和端口号,由于每次只接受一个客户端请求效率太慢,使用 newFixedThreadPool() 线程池来实现多线程
package com.bittech.httpd.core;
/**
* HTTP 服务器
*/
import com.bittech.httpd.Handler.*;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
public class HttpServer {
private final ExecutorService executor = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors() * 2,
new ThreadFactory() {
private final AtomicInteger count = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("Thread-Handler-"+count.getAndIncrement());
return thread;
}
}
);
public void start(){
try {
ServerSocket serverSocket = new ServerSocket(80);
System.out.println("Server start on 127.0.0.1:80,Please visited http://127.0.0.1:80/");
//分发处理器
DispatcherHandler dispatcherHandler = new DispatcherHandler();
this.loadHandler(dispatcherHandler);
while(true){
Socket socket = serverSocket.accept();
//线程池 outputStream.write
executor.execute(dispatcherHandler.handler(socket));
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 新写的 Handler 处理器都在这里加载
*/
private void loadHandler(DispatcherHandler dispatcherHandler){
//业务
dispatcherHandler.registerHandler("/",IndexHandler.class);
dispatcherHandler.registerHandler("/sum",SumHandler.class);
dispatcherHandler.registerHandler("/info",InfoHandler.class);
//内置
dispatcherHandler.registerHandler("_default_",StaticHandler.class);
dispatcherHandler.registerHandler("404",NotFoundHandler.class);
}
}
请求类:
package com.bittech.httpd.core;
/**
* 默认Request 实现类
* 对于Request参数,首行,header,参数
*/
import com.bittech.httpd.common.HttpMethod;
import com.bittech.httpd.common.HttpRequest;
import java.util.List;
import java.util.Map;
public class DefaultHttpRequest implements HttpRequest {
private final Map<String,String> line;
private final Map<String,String> header;
private final Map<String,List<String>> params;
public DefaultHttpRequest(Map<String, String> line, Map<String, String> header, Map<String, List<String>> params) {
this.line = line;
this.header = header;
this.params = params;
}
@Override
public HttpMethod method() {
return HttpMethod.lookup(line.get("method"));
}
@Override
public String url() {
return line.get("url");
}
@Override
public String version() {
String version = line.get("version");
return version == null? "HTTP/1.1" : version;
}
@Override
public Map<String, String> header() {
return header;
}
@Override
public Map<String, List<String>> params() {
return params;
}
}
响应类:
根据HTTP协议响应报文的格式拼接出响应报文.
package com.bittech.httpd.core;
/**
* 默认 Response 实现类
*/
import com.bittech.httpd.common.HttpRequest;
import com.bittech.httpd.common.HttpResponse;
import com.bittech.httpd.common.HttpStatus;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class DefaultHttpResponse implements HttpResponse {
private final Socket socket;
private final HttpRequest request;
private HttpStatus httpStatus = HttpStatus.OK;
private Map<String,String> header = new HashMap<>();
private ByteArrayOutputStream content = new ByteArrayOutputStream();
public DefaultHttpResponse(Socket socket, HttpRequest request) {
this.socket = socket;
this.request = request;
}
@Override
public void setHttpStatus(HttpStatus httpStatus) {
this.httpStatus = httpStatus;
}
@Override
public void setHeader(String key, String value) {
this.header.put(key,value);
}
@Override
public void setContentType(String value) {
this.header.put("Content-Type",value);
}
@Override
public void write(byte[] value) {
try {
content.write(value);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void write(String value) {
try {
content.write(value.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void flush() {
// HTTP/1.1 200 ok
// header1 : value1
// header2 : value2
//data
try {
OutputStream outputStream = socket.getOutputStream();
outputStream.write(
encodeContent(
request.version()
+ " " +
this.httpStatus.getRequestStatus()
+ " "
+this.httpStatus.getRequestStatus()
+ "\r\n"
)
);
for(Map.Entry<String,String> entry : header.entrySet()){
outputStream.write(encodeContent(entry.getKey()+ ": "
+ entry.getValue()+"\r\n"));
}
outputStream.write(encodeContent("\r\n"));
ByteArrayInputStream inputStream = new ByteArrayInputStream(content.toByteArray());
byte[] buff = new byte[1024];
int len;
while((len = inputStream.read(buff))!=-1){
outputStream.write(buff,0,len);
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static byte[] encodeContent(String value){
return value.getBytes();
}
}
解析类:
使用解析类解析出HTTP请求报文,并且得到 request信息和 response信息,就可以在 doGet()方法中写响应内容,然后通过Socket进行响应
package com.bittech.httpd.core;
import com.bittech.httpd.common.HttpRequest;
import com.bittech.httpd.common.HttpResponse;
import java.io.*;
import java.net.Socket;
import java.net.URLDecoder;
import java.util.*;
/**
* 此类专门用来解析
*/
public class HttpReqRespWrapper {
private HttpRequest request;
private HttpResponse response;
private final Socket socket;
public HttpReqRespWrapper(Socket socket) {
this.socket = socket;
this.parseHandler();
}
public HttpRequest request(){
return request;
}
public HttpResponse response(){
return response;
}
public void parseHandler(){
Map<String,String> line = new HashMap<>();
Map<String,String> header = new HashMap<>();
Map<String, List<String>> params = new HashMap<>();
try {
InputStream inputStream = socket.getInputStream();
// InputStream 是字节流,将其变为字符流
InputStreamReader buffer = new InputStreamReader(inputStream);
BufferedReader reader = new BufferedReader(buffer);
//拿到首行
String lineValue = reader.readLine();
// 开始解析首行 GET /index?key1=value1&&key2=value2 HTTP/1.1
//使用此类来进行首行参数分割
StringTokenizer tokenizer = null;
if(lineValue != null){
tokenizer = new StringTokenizer(lineValue);
}
if(tokenizer !=null && tokenizer.hasMoreTokens()){
String method = tokenizer.nextToken();
line.put("method",method);
}
if(tokenizer != null && tokenizer.hasMoreTokens()){
String url = tokenizer.nextToken();
int index = url.indexOf("?");
if(index != -1){
params = decodeParameters( url.substring(index+1));
line.put("url",url.substring(0,index));
}else{
line.put("url",url);
}
}
if(tokenizer != null && tokenizer.hasMoreTokens()){
String version = tokenizer.nextToken();
line.put("version",version);
}
//响应首行解析完毕
// 第一行与第二行之间是个空行 \r\n
lineValue = reader.readLine();
if(lineValue !=null) {
// 现在读取报头,由于报头中参数较多,需要反复去读 key : value
while (!(lineValue.trim().isEmpty())) {
int index = lineValue.indexOf(":");
String key = lineValue.substring(0, index);
String value = lineValue.substring(index + 1);
header.put(key, value);
lineValue = reader.readLine();
}
}
this.request = new DefaultHttpRequest(line,header,params);
this.response = new DefaultHttpResponse(socket,request);
} catch (IOException e) {
e.printStackTrace();
}
}
private static Map<String, List<String>> decodeParameters(String queryString) {
Map<String, List<String>> params = new HashMap<>();
if(queryString !=null){
StringTokenizer tokenizer = new StringTokenizer(queryString,"&");
while(tokenizer.hasMoreTokens()){
String kv = tokenizer.nextToken();
int index = kv.indexOf("=");
String key = decodePercent(kv.substring(0,index));
String value = decodePercent(kv.substring(index+1));
if(params.containsKey(key)){
List<String> values = params.get(key);
values.add(value);
}else{
List<String> values = new ArrayList<>();
values.add(value);
params.put(key,values);
}
}
}
return params;
}
// 解码
private static String decodePercent(String str) {
String decoded = null;
try {
decoded = URLDecoder.decode(str, "UTF-8");
} catch (UnsupportedEncodingException ignored) {
}
return decoded;
}
}
服务器:
1.服务器建立Socket链接,绑定ip地址和端口号,由于每次只接受一个客户端请求效率太慢,使用 newFixedThreadPool() 线程池来实现多线程,每当有新客户端请求链接时就新建线程链接,然后服务器创建ServerSocket实例,监听客户端发来的请求
2.使用accept()方法获取客户端socket,使用 DispatcherHandler 这个类,根据 HTTP协议请求报文的格式,使用解析类 HttpReqRespWrapper 将URL解析出来,如果是 " . ",就是静态页面,如果输入的url在服务器中不存在,则返回 “404” 无法找到,如果url存在,则根据HTTP协议响应报文的格式拼接出响应报文.
3.这就得到了response信息,在doGet方法中写响应内容,通过socket响应,显示出 静态页面或者动态页面