目录
一.前言
如果你觉得对你有所帮助,别忘了给博主一个赞哟!
本篇我们来正式来编写服务器,我们可以利用socket连接来构建一个简单的服务器
下面我们来实验一下:
二.示例
首先,我们创建一个maven项目,我这里给我的服务器起名为JhWebServer
这是md文件中的自述,下面我们来看一下架构:
三.原理
主要就是通过socket连接, 然后把输入流传给HttpServletRequest对象封装,然后根据路径UrlMappingHandler处理器来判断路径后缀是静态还是动态servlet资源,静态直接通过输出流把文件写回去,动态则调用HttpServlet的service()方法,去执行逻辑业务操作,最后不管是静态还是动态请求,都经过HttpResponseHandler响应处理器对结果进行封装,主要就是响应行,响应头,响应体,然后通过socket输出流写回前端
四.逐步实现
1.创建ServletContext容器
我们需要先把servletContext容器创建好:
/**
* servlet容器
* @author JJH
*/
public class ServletContext {
/**
* 每个web应用都要一个名称
*/
private String appName;
/**
* 所有的servlet实例
* K: 路径
* v: servlet的字节码对象
*/
private Map<String,Class<?>> servletMap = new ConcurrentHashMap<>();
/**
* servlet的处理器,负责把所有的servlet注册到map中
*/
public ServletContext(String appName){
this.appName = appName;
this.run();
}
/**
* 将所有被@WebServlet注解标记的类加载到map集合中
*/
private void run(){
Map<String, Class<?>> servlets = FileUtils.getServlets();
this.servletMap.putAll(servlets);
}
/**
* 返回所有的servlet字节码对象集合
* @return
*/
public Map<String,Class<?>> getServlets(){
return this.servletMap;
}
public String getAppName() {
return appName;
}
}
容器中主要放的是所有servlet集合,为了简便,我们使用注解@WebServlet(path)
2.@WebServlet注解扫描
底层就是只要是被这个注解的类,就会被扫描到,然后以path为键,这个类的字节码对象为值存入map集合中:我们来看一下扫描的代码:
/**
* @author JJH
*/
public class FileUtils {
/**
* 返回被@WebServlet注解标注的类的字节码集合
*
* @return
*/
public static Map<String, Class<?>> getServlets() {
HashMap<String,Class<?>> map = new HashMap<>();
List<File> list = getClassFileByAnnatation(WebServlet.class);
for (File file : list) {
String path = file.getPath();
String className = getClassName(path);
Class<?> aClass = null;
try {
aClass = Class.forName(className);
WebServlet annotation = aClass.getAnnotation(WebServlet.class);
String url = annotation.path();
map.put(url,aClass);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
return map;
}
public static String getClassName(String path) {
String replace = path.replace("\\", ".");
int index = replace.lastIndexOf("classes");
String substring = replace.substring(index + 8);
String classname = substring.substring(0, substring.lastIndexOf("class") - 1);
return classname;
}
public static void listFile(File file, List<File> fileList) {
if (!file.isDirectory()) {
fileList.add(file);
} else {
File[] files = file.listFiles();
File[] var3 = files;
int var4 = files.length;
for(int var5 = 0; var5 < var4; ++var5) {
File subFile = var3[var5];
listFile(subFile, fileList);
}
}
}
public static List<File> getClassFileByPackage(String[] packagePaths) {
List<File> fileList = new ArrayList();
ArrayList<File> classFileList = new ArrayList();
String[] var3 = packagePaths;
int var4 = packagePaths.length;
for(int var5 = 0; var5 < var4; ++var5) {
String packge = var3[var5];
String replace = packge.replace(".", "/");
URL url = ClassLoader.getSystemResource(replace);
String path = url.getPath();
File file = new File(path);
listFile(file, fileList);
Iterator var11 = fileList.iterator();
while(var11.hasNext()) {
File fiLe = (File)var11.next();
String name = fiLe.getName();
if (name.endsWith(".class")) {
classFileList.add(fiLe);
}
}
}
return classFileList;
}
public static List<File> getClassFiles() {
URL url = Thread.currentThread().getContextClassLoader().getResource("");
String path = url.getPath();
File file = new File(path);
List<File> fileList = new ArrayList();
listFile(file, fileList);
ArrayList<File> classFileList = new ArrayList();
Iterator var5 = fileList.iterator();
while(var5.hasNext()) {
File fiLe = (File)var5.next();
String name = fiLe.getName();
if (name.endsWith(".class")) {
classFileList.add(fiLe);
}
}
return classFileList;
}
public static List<File> getClassFileByAnnatation(Class<WebServlet> webServletClass) {
List<File> list = new ArrayList();
List<File> classFiles = getClassFiles();
Iterator var3 = classFiles.iterator();
while(var3.hasNext()) {
File classFile = (File)var3.next();
String path = classFile.getPath();
String className = getClassName(path);
Class<?> aClass = null;
try {
aClass = Class.forName(className);
} catch (ClassNotFoundException var9) {
throw new RuntimeException(var9);
}
Annotation annotation = aClass.getAnnotation(webServletClass);
if (annotation != null) {
list.add(classFile);
}
}
return list;
}
/**
* 返回webapps目录下所有的静态资源
* @return
*/
public static List<File> getWebappsStaticFiles(){
List<File> files = new ArrayList<>();
List<File> resFiles = new ArrayList<>();
File file = new File("src/main/webapp");
listFile(file,files);
for (File file1 : files) {
String name = file1.getName();
if(!name.endsWith("web.xml")){
resFiles.add(file1);
}
}
return resFiles;
}
// public static void main(String[] args) {
// FileUtils.getWebappsStaticFiles();
// }
/**
* 返回文件的字节数组
* @param file 文件
* @return 字节数组
*/
public static byte[] getFileByte(File file){
byte[] bytes = new byte[(int)file.length()];
InputStream in = null;
try {
in = new FileInputStream(file);
in.read(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
assert in != null;
in.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return bytes;
}
/**
* 获取webapp目录下的某一个文件
* @param name
* @return
*/
public static File getWebAppOneFile(String name){
List<File> files = getWebappsStaticFiles();
for (File file : files) {
if (name.contains(file.getName())){
return file;
}
}
return null;
}
}
可以看到被注解了@WebServlet对象的文件会被返回到map集合中
2.实现socket连接
/**
* @author JJH
*/
public class ServerSocketHandler {
private ServletContext context;
private int port =8080;
public ServerSocketHandler(ServletContext context){
this.context = context;
this.start(this.context);
}
public ServerSocketHandler(ServletContext context,int port){
this.context = context;
this.port = port;
this.start(this.context);
}
public void start(ServletContext context) {
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(this.port);
System.out.println("JHWebServer start success! Listening port: "+this.port);
} catch (IOException e) {
throw new RuntimeException(e);
}
//换成线程池来处理,更加高效
ThreadPoolExecutor pool =
new ThreadPoolExecutor(10,15,30, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
//只要服务器没有关闭,就一直运行
while (!serverSocket.isClosed()){
//接受客户端发来的请求
Socket socket ;
try {
socket = serverSocket.accept();
} catch (IOException e) {
throw new RuntimeException(e);
}
//将socket交给线程处理
// new Thread(new JhRequestHandler(socket)).start();
pool.submit(new JhRequestHandler(socket,context));
}
}
}
这里是我的最终版了,加入了线程池,线程池可以提高效率,提前创建好线程,可以实现复用,
每次来一个请求,就提交任务让他执行就可以了
2.交给JhRequsetHandler处理
下面看一下这个任务类:
public class JhRequestHandler implements Runnable{
private Socket socket =null;
private ServletContext context;
public JhRequestHandler(Socket socket,ServletContext context){
this.context = context;
this.socket = socket;
}
@Override
public void run() {
try {
//1.获取输入流
InputStream in = socket.getInputStream();
OutputStream os = socket.getOutputStream();
//2.创建俩对象
HttpServletRequest requset = new HttpServletRequest(in);
HttpServletResponse response = new HttpServletResponse();
//3.路径映射
UrlMappingHandler urlMappingHandler =
new UrlMappingHandler(requset,response,context);
Object result = urlMappingHandler.urlMapping(requset,response,context);
//处理找不到的情况
if(result==null){
response.setStatus(404);
}else {
//静态文本资源
if(result.getClass()==String.class){
response.setStatus(200);
response.write(result.toString());
}else if(result.getClass()==File.class){
response.setStatus(200);
response.write((File) result);
}
else{
//servlet动态资源
HttpServlet servlet = (HttpServlet) result;
servlet.service(requset,response);
response.setStatus(200);
}
}
//4。对响应在封装
new JhResponseHandler(response,os);
//关闭流
os.flush();
os.close();
in.close();
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
//最终一定要确保socket关闭
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
这个任务类,也就是请求的处理类,做了什么事?我们来分析一下:
(1).获取到socket连接的输入流和输出流
(2)创建HttpServletRequest和HttpServletResponse对象
(3).进行路径映射
(4).用JhResponseHandler类进行响应结果封装并发送响应
3.HttpServletRequset封装请求对象
我们来看一下封装请求对象做了哪些事情?
public class HttpServletRequest extends JhRequset{
/**
* 方法类型
*/
private String method;
/**
* 请求路径
*/
private String url;
/**
* 参数列表
*/
private HashMap<String,String> params = new HashMap<>();
/**
* 请求头信息
*/
private HashMap<String,String> headers = new HashMap<>();
/**
* 请求体(json格式)
*/
private String requestBody;
/**
* socket的输入流
*/
private InputStream inputStream;
public HttpServletRequest(InputStream inputStream) throws IOException {
this.inputStream = inputStream;
this.init(this.inputStream);
}
/**
* 对http请求的封装
* @param inputStream socket输入流
*/
private void init(InputStream inputStream) throws IOException {
try {
BufferedReader reader =
new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
//读取第一行
String firstLine = reader.readLine();
// System.out.println(firstLine);
String[] s = firstLine.split(" ");
//设置该请求的方法
this.method = s[0];
//设置请求路径
String allPath = s[1];
if(allPath.contains("?")){
//如果包含了?,那么要截取?之前的
this.url = allPath.substring(0, allPath.indexOf("?"));
}else {
this.url = allPath;
}
/**
* 对get请求处理
*/
if("GET".equals(method)){
int index = allPath.indexOf("?");
if (index != -1) {
//有参数
String params = allPath.substring(index + 1);
//判断是否有&号
if (params.contains("&")) {
String[] parms = params.split("&");
for (String parm : parms) {
String[] str = parm.split("=");
this.params.put(str[0], str[1]);
}
} else {
//就一个参数
String[] str = params.split("=");
this.params.put(str[0], str[1]);
}
}
}
/**
* 对请求头处理
*/
String msg;
while ((msg = reader.readLine()) != null) {
if (msg.length() == 0) {
break;
}
String[] headers = msg.split(":");
String header = headers[0];
String value = headers[1].trim();
this.headers.put(header,value);
}
/**
* 对POST请求处理
*/
if("POST".equals(method)) {
StringBuffer sb = new StringBuffer();
String length = this.headers.get("Content-Length");
int len = Integer.parseInt(length);
for(int i=0;i<len;i++){
char ch= (char) reader.read();
/*
用一下正则表达式来排除数字,英文字母特殊字符
这样这个字符肯定是中文,utf-8中文是三个字符表示的,
这时让i=i+2就可以了
*/
String chs = String.valueOf(ch);
if (RegexUtil.isChinese(chs)) {
i = i+2;
}
sb.append(chs);
}
String body = sb.toString();
//判断是那种提交格式
String type = this.headers.get("Content-Type");
if("application/x-www-form-urlencoded".equals(type)){
//表单提交格式,把它添加到参数列表中去
if(body.contains("&")){
String[] split = body.split("&");
for (String parms : split) {
String[] p = parms.split("=");
this.params.put(p[0],p[1]);
}
}else {
//就一个参数
String[] str = body.split("=");
this.params.put(str[0],str[1]);
}
} else if (type.contains("application/json")) {
//json格式的请求体
this.requestBody = body;
}
}
}catch (Exception e){
e.printStackTrace();
}
}
private void setCharacterEncoding(String encoding){
}
/**
* 返回请求方法
* @return 请求方法
*/
public String getMethod() {
return method;
}
/**
* 返回请求路径
* @return 请求路径
*/
public String getUrl() {
return url;
}
/**
* 返回请求参数,包括get请求和post请求的表单形式
* @return 请求参数map集合
*/
public HashMap<String, String> getParams() {
return params;
}
/**
* 返回psot请求体
* @return 请求体
*/
public String getRequestBody(){
return this.requestBody;
}
/**
* 返回请求头信息
* @return 请求头信息集合
*/
public HashMap<String,String> getHeaders(){
return this.headers;
}
/**
* 根据参数名称返回参数值
* @param parm 参数名称
* @return 参数值
*/
public String getPramater(String parm){
if (this.params.get(parm) != null) {
return this.params.get(parm);
}else{
return null;
}
}
}
为了简单,我们先暂时实现对GET和POST请求的处理,
主要就是判断是哪个请求方式,然后获取请求参数,请求路径,请求体等,然后把它们放到属性里封装起来
4.UrlMappingHandler路径映射处理器
下面我们来看一下路径映射做了哪些事情?
/**
* 路径映射处理器
* @author JJH
*/
public class UrlMappingHandler {
private HttpServletRequest request;
private HttpServletResponse response;
private ServletContext context;
public UrlMappingHandler(HttpServletRequest request, HttpServletResponse response, ServletContext context) {
this.context = context;
this.request = request;
this.response = response;
}
/**
* 路径映射
*
* @param request requset对象
* @return httpservlet对象
*/
public Object urlMapping(HttpServletRequest request, HttpServletResponse response, ServletContext context) {
String url = request.getUrl();
//判断静态资源
if (RegexUtil.isStaticFiles(url)) {
return mappingStaticResources(url, response);
} else {
HttpServlet[] servletes = new HttpServlet[1];
//匹配servlet动态资源
Map<String, Class<?>> servlets = context.getServlets();
servlets.forEach((path, clazz) -> {
if (url.equals(path)) {
//创建servlet对象
try {
HttpServlet servlet = (HttpServlet) clazz.newInstance();
servletes[0] = servlet;
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
});
return servletes[0];
}
}
/**
* 匹配静态资源
*/
public Object mappingStaticResources(String url, HttpServletResponse response) {
//0表示是字符串形式, 1表示是二进制文件
int flag = 1;
if(url.equals("/")){
//空字符串,就去默认找index.html页面
url = "/index.html";
flag = 0;
}else if (url.endsWith(".html")) {
flag = 0;
} else if (url.endsWith(".js")) {
response.setHeader("Content-Type: application/javascript");
flag = 0;
} else if (url.endsWith(".css")) {
response.setHeader("Content-Type: text/css");
flag = 0;
} else if (url.endsWith(".jpeg") || url.endsWith(".jpg")) {
response.setHeader("Content-Type: image/jpge");
response.setHeader("Accept-Ranges: bytes");
} else if (url.endsWith(".webp")) {
response.setHeader("Content-Type: image/webp");
response.setHeader("Accept-Ranges: bytes");
} else if (url.endsWith(".gif")) {
response.setHeader("Content-Type: image/gif");
response.setHeader("Accept-Ranges: bytes");
} else if (url.endsWith(".png")) {
response.setHeader("Content-Type: image/png");
response.setHeader("Accept-Ranges: bytes");
}
List<File> files = FileUtils.getWebappsStaticFiles();
if (flag == 0) {
//文本格式
BufferedReader in;
StringBuffer sb = new StringBuffer();
for (File file : files) {
if (url.contains(file.getName())) {
try {
in = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
int msg;
while ((msg = in.read()) != -1) {
sb.append((char) msg);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
return sb.toString();
}else {
return FileUtils.getWebAppOneFile(url);
}
}
}
可以看到当是静态请求时,将该文件直接写到StringBuffer缓冲区中返回,如果是动态请求,那么会根据请求路径去ServletContext中的servletmap集合中去找对应的servlet键,如果找到,就利用反射创建这个servlet的对象,然后执行service()方法,我们下面来看一下HttpServlet做了哪些事情:
5.HttpServlet抽象类
public abstract class HttpServlet implements Servlet{
@Override
public void init(ServletConfig config) {
}
@Override
public void service(JhRequset request,JhResponse respone) {
HttpServletRequest request1 = (HttpServletRequest) request;
HttpServletResponse respone1 = (HttpServletResponse) respone;
String method = request1.getMethod();
if("GET".equals(method)){
this.doGet(request1,respone1);
} else if ("POST".equals(method)) {
this.doPost(request1,respone1);
} else if ("OPTIONS".equals(method)) {
this.doOptions(request1,respone1);
}
}
@Override
public void destroy() {
}
/**
* 执行对应的get方法
* @param request
* @param respone
*/
public abstract void doGet(HttpServletRequest request, HttpServletResponse respone);
/**
* 执行对应的post方法
* @param request
* @param respone
*/
public abstract void doPost(HttpServletRequest request, HttpServletResponse respone);
/**
* 执行对应的options方法
* @param request
* @param respone
*/
public void doOptions(HttpServletRequest request, HttpServletResponse respone){
respone.setHeader("Access-Control-Allow-Origin:*");
respone.setHeader("Access-Control-Allow-Methods:POST,GET,OPTIONS,DELETE");
respone.setHeader("Access-Control-Allow-Headers: access-control-allow-origin, authority, content-type, version-info, X-Requested-With");
respone.setHeader("Access-Control-Allow-Age:3600");
respone.setStatus(200);
}
}
可以看到该抽象类根据请求方式去调用对应的处理方法,至于为什么写成抽象类,那是因为我们的业务servlet有很多,我们只需要继承这个HttpServlet抽象类,就必须实现doGet()和doPost()方法,这样,我们自己的业务就只要关心这两个请求就可以了,参照javaweb。
6.HttpServletResponse对象封装
对应该对象的封装,它其实是一开始就创建好了,然后一直传递,在传递的过程中,把他的属性值都给赋上了,我们来看一下他的属性:
/**
* @author JJH
*/
public class HttpServletResponse extends JhResponse{
/**
* 状态码
*/
private int status;
/**
* 响应头信息
*/
private HashMap<String,String> headers = new HashMap<>();
/**
* 响应体
* (主要是html,css,json响应体)
*/
private String responseBody;
/**
* 二进制文件
*/
private File resFile;
/**
* 对响应体那部分要写的内容流
*/
private JhOutputStream resOutputStream;
public HttpServletResponse(){}
/**
* 打印流
*/
public PrintWriter getWriter(){
Writer writer = new OutputStreamWriter(this.resOutputStream);
return new JhPrintwriter(writer,this.responseBody);
}
/**
* 直接写文本文件(html,js,css,json)
* @param res 字符串
*/
public void write(String res){
this.responseBody = res;
}
/**
* 二进制文件
* @param file 文件
*/
public void write(File file){
this.resFile = file;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public HashMap<String, String> getHeaders() {
return headers;
}
public void setHeaders(HashMap<String, String> headers) {
this.headers = headers;
}
/**
* 添加响应头
* @param header
*/
public void addHeader(String header){
String[] str = header.split(":");
this.headers.put(str[0],str[1]);
}
/**
* 设置响应头,如果有重复的,就覆盖掉
* @param header
*/
public void setHeader(String header){
String[] str = header.split(":");
this.headers.put(str[0],str[1]);
}
/**
* 根据响应头获取响应头内容
* @param header 响应头
* @return 头内容
*/
public String getHeader(String header){
AtomicReference<String> str = new AtomicReference<>("");
this.headers.forEach((head,value)->{
if(head.equals(header)){
str.set(value);
}
});
return str.get();
}
public String getResponseBody() {
return responseBody;
}
public File getResFile() {
return resFile;
}
}
这里我主要挑了几个常见的重要属性,比如响应头信息,响应体信息还有添加响应头,设置响应头等,至于对字符编码的方法暂时没有做,
7.JhResponseHandler响应处理器
当我们做完以上的流程后,就会到我们的响应处理类中,做最后的封装,下面,我们来看一下它做了哪些事情:
/**
* 对响应进行处理
* @author JJH
*/
public class JhResponseHandler {
private JhResponse response;
private OutputStream outputStream;
public JhResponseHandler(JhResponse response,OutputStream outputStream) throws FileNotFoundException {
this.response = response;
this.outputStream = outputStream;
handleResponse(response,outputStream);
}
/**
* 处理响应,可以是所有类型的响应
* @param response
* @param outputStream
*/
private void handleResponse(JhResponse response,OutputStream outputStream) throws FileNotFoundException {
if(response.getClass()== HttpServletResponse.class){
//转化为http的响应对象
HttpServletResponse response1 = (HttpServletResponse) response;
handleHttpResponse(response1,outputStream);
}
}
/**
* 处理http的响应
*/
private void handleHttpResponse(HttpServletResponse response,OutputStream outputStream) throws FileNotFoundException {
//添加一些统一的响应头
//设置响应的时间
response.setHeader("Date:"+ TimeUtil.now());
response.setHeader("Server:JhWebServer");
//获取它的状态码,来判断
int status = response.getStatus();
if(status==200){
StringBuffer sb = new StringBuffer();
//200 OK,返回一个正确结果
String row = "HTTP/1.1 200 OK\r\n";
sb.append(row);
HashMap<String, String> headers = response.getHeaders();
headers.forEach((head,value)->{
sb.append(head).append(":").append(value).append("\r\n");
});
//判断一下是那种格式
if(headers.get("Content-Type")!=null) {
byte[] bytes;
String type = headers.get("Content-Type");
if(type.contains("jpg")||type.contains("jpeg")||type.contains("png")
||type.contains("webp")){
sb.append("\r\n");
File resFile = response.getResFile();
byte[] fileByte = FileUtils.getFileByte(resFile);
for (byte b : fileByte) {
sb.append(b);
}
try {
outputStream.write(sb.toString().getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
}else {
sb.append("\r\n");
sb.append(response.getResponseBody());
bytes = sb.toString().getBytes();
try {
outputStream.write(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}else {
//主要是options请求
if(!("").equals(response.getHeader("Access-Control-Allow-Headers"))){
try {
outputStream.write(sb.toString().getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
}else{
//默认没设置响应的格式,那么我们设置为html文本
sb.append("Content-Type:text/html; charset=utf-8\r\n");
sb.append("Content-Length:").append(response.getResponseBody().length()).append("\r\n");
sb.append("\r\n\r\n");
sb.append(response.getResponseBody());
try {
outputStream.write(sb.toString().getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}else if (status==404) {
StringBuffer sb = new StringBuffer();
String row = "HTTP/1.1 404\r\n";
sb.append(row);
HashMap<String, String> headers = response.getHeaders();
response.setHeader("Content-Type:text/html; charset=utf-8");
headers.forEach((head,value)->{
sb.append(head+":"+value+"\r\n");
});
sb.append("\r\n");
sb.append("<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +
" <title>NotFound</title>\n" +
"</head>\n" +
"<body>\n" +
" <h1>404 Not Found</h1>\n" +
"</body>\n" +
"</html>");
try {
outputStream.write(sb.toString().getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
可以看到,主要是对200和404的状态码做了解析,如果是200,那么就对响应进行组装,检查响应的格式,看看是json还是文化,json返回文本,文件返回字节数组等,如果是404,就直接写一个404 Not Found的页面返回就可以了,其他的状态码暂时没做,如果大家有兴趣可以自己实现一下。
8.启动方式
以上就是该服务器的主要流程了,下面让我们来看一下,如何使用该服务器:
/**
* JhWebServer服务器总体
* @author JJH
*/
public class JhWebServer {
private static ServletContext servletContext;
private static ServerSocketHandler serverSocketHandler;
/**
* 直接启动
* @param appName
*/
public static ServletContext run(String appName){
servletContext = new ServletContext(appName);
serverSocketHandler = new ServerSocketHandler(servletContext);
return servletContext;
}
/**
* 修改端口启动
* @param appName
* @param port
*/
public static ServletContext run(String appName,int port){
servletContext = new ServletContext(appName);
serverSocketHandler = new ServerSocketHandler(servletContext,port);
return servletContext;
}
}
可以看到该JhWebServer类中有一个静态方法,run(),我们只需要在我们的main方法中写:
JhWebServer.run("应用名称",端口号)
就可以启动运行了
五.待优化地方
该服务器只能实现大致功能,还有很多功能没有实现:
1.Filter过滤器没有实现
2.Cookie和Session存储没有实现
3.其他状态码的解析没有实习
4.PUT,DELETE请求方式没有实现
这些待优化地方以后会逐步优化,如果你觉得对你有所帮助,别忘了给博主一个赞哟!我们下期再见!