手写Web服务器

手写Web服务器

涉及到的知识点

OOP、容器、IO流、多线程、网络编程、XML解析、反射、html、http

WebServer简介

  • 上网浏览网页,离不开服务器,客户请求页面,服务器响应内容。都是基于HTTP协议。
  • Web请求都是使用Request和Response式的交流

反射

反射Reflection:把Java类中的各种结构方法、属性、构造器、类名映射成一个个Java对象。利用反射技术可以对一个类进行解剖,反射是框架设计的灵魂。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package zhuchuli.server;

import java.lang.reflect.InvocationTargetException;

/***
*
* @author 朱楚利
*反射:把Java类中的结构方法,属性,类名,构造器映射成一个个Java对象
*1、获取Class对象
* 三种方式:Class.forName(完整路径);
*2、可以动态创建对象
*/
public class ReflectTest01 {

public static void main(String[] args) throws Exception{
//三种方式
//1. 对象.getClass()
Class clz=new Iphone().getClass();
//2.类.class
clz=Iphone.class;
//3.定位 Class.forName("包名.类名");
clz=Class.forName("zhuchuli.server.Iphone");
//JDK9.0提供的创建对象的方法
Iphone iphone=(Iphone)clz.getConstructor().newInstance();
System.out.println(iphone);

}

}
class Iphone{
public Iphone() {};
}

XML解析

  • 解析流程(按流的方式进行解析)
    • 获取解析工程:SAXParserFactory factory = SAXParserFactory.newInstance();
    • 获取解析器:SAXParser parser = factory.newSAXParser();
    • 编写处理器,加载文档document注册处理器:PHandler handler = new PHandler();
    • 解析:parser.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("zhuchuli/server/p.xml"), handler);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package zhuchuli.server;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/***
* 熟悉SAX解析流程
*
* @author 朱楚利
*
*/
public class XMLTest01 {
public static void main(String[] args) throws Exception {
//SAX解析
//1.获取解析工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
//2.从解析工厂获取解析器
SAXParser parser = factory.newSAXParser();
//3.编写处理器
//4.加载文档document注册处理器
PHandler handler = new PHandler();
//5、解析
parser.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("zhuchuli/server/p.xml"), handler);

}
}
class PHandler extends DefaultHandler{
@Override
public void startDocument() throws SAXException {
System.out.println("************** 解析文档开始 ******************");
}
//解析元素 标签
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
System.out.println(qName+"------>"+"解析开始");
}
//标签内容
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
//trim()方法 去掉前后空格
String contents=new String(ch,start,length).trim();
if(contents.equals("")) {
System.out.println("内容为 --->"+"空");
}else {
System.out.println("内容为 --->"+contents);
}
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
System.out.println(qName+"------>"+"解析结束");
}
@Override
public void endDocument() throws SAXException {
System.out.println("**************解析文档结束******************");

}
}

html

html:HyperText Markup Language:超文本标记语言,简单理解为浏览器使用的语言。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>第一个html页面</title>
</head>
<body>
<h1>表单的使用</h1>
<pre>
name: web服务器使用的属性<br>
id: 前端js使用的属性<br>
action:请求web服务器的资源 <br>
method: get/post<br>
get:从服务器端获取数据,默认发送行为,发送数据量不宜较大,不安全,请求参数跟在url上<br>
post:向服务器端发送数据,发送数据量相对较大,安全,请求参数在请求体中
</pre>
<form method="post" action="https://localhost:8888/index.html">
用户名:<input type="text" name="uname" id="uname" /><br>
密码:<input type="password" name="pswd" id="pswd" /><br>
<input type="submit" value="提交"/>
</form>
</body>
</html>

HTTP协议

超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议,所以的WWW文件都必须遵守这个标准。

  • 请求协议
    • 请求行:方法(GET/POST) URI 协议/版本;
    • 请求头:(Request Header)
    • 请求正文:
  • 响应协议
    • 状态行:协议/版本 状态码 描述
    • 响应头:(Response Header)
    • 响应正文:

手写服务器

  • 获取请求协议
    • 创建ServerSocket
    • 建立连接获取Socket
    • 通过输入流获取请求协议
    • 注意:GET和POST不一致的地方
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.zhuchuli.server;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/***
* 目标:使用ServerSocket建立与浏览器的连接,获取请求协议
* @author 朱楚利
*
*/
public class Server01 {
private ServerSocket serverSocket;
public static void main(String[] args) {
Server01 server=new Server01();
server.start();
server.receive();
}
//启动服务
public void start() {
try {
serverSocket=new ServerSocket(8888);
} catch (IOException e) {
System.out.println("服务器启动失败...");
e.printStackTrace();
}
}
//接收连接处理
public void receive() {
try {
Socket client = serverSocket.accept();
System.out.println("一个客户端建立了连接....");
//获取请求协议
InputStream is=client.getInputStream();
byte[] datas=new byte[1024*1024];
int len=is.read(datas);
String requestInfo = new String(datas,0,len);
System.out.println(requestInfo);
} catch (Exception e) {
System.out.println("客户端连接出现错误...");
}
}
//停止服务
public void stop() {

}

}
  • 返回响应协议
    • 准备内容。
    • 获取字节数的长度。
    • 拼接响应协议。
      • 注意:空格与换行。
    • 使用输出流输出。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package com.zhuchuli.server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

/***
* 目标:返回响应协议
* @author 朱楚利
*
*/
public class Server02 {
private ServerSocket serverSocket;
public static void main(String[] args) {
Server02 server=new Server02();
server.start();
}
//启动服务
public void start() {
try {
serverSocket=new ServerSocket(8888);
receive();
} catch (IOException e) {
System.out.println("服务器启动失败...");
e.printStackTrace();
}
}
//接收连接处理
public void receive() {
try {
Socket client = serverSocket.accept();
System.out.println("一个客户端建立了连接....");
//获取请求协议
InputStream is=client.getInputStream();
byte[] datas=new byte[1024*1024];
int len=is.read(datas);
String requestInfo = new String(datas,0,len);
System.out.println(requestInfo);
//返回响应协议
StringBuilder content=new StringBuilder();
content.append("<html>");
content.append("<head>");
content.append("<title>");
content.append("服务器响应成功");
content.append("</title>");
content.append("</head>");
content.append("<body>");
content.append("zhuchuli Server回来了...");
content.append("</body>");
content.append("</html>");
//字节长度
int size= content.toString().getBytes().length;//必须获取字节的长度
StringBuilder responseInfo=new StringBuilder();
String blank=" ";//空格
String CRLF="\r\n";//换行符
//1.响应行 Http/1.1 200 OK
responseInfo.append("HTTP/1.1").append(blank);
responseInfo.append(200).append(blank);
responseInfo.append("OK").append(CRLF);
//2.响应头:(最后一行存在空行)
/* Date:Mon,31 Dec208804:36:57GMT
* Server:zhuchuli Server/0.0.1;charset=GBK
* Content-Type:text/html
* Content-Length:39725426
*/
responseInfo.append("Date:").append(new Date()).append(CRLF);
responseInfo.append("Server:").append("zhuchuli Server/0.0.1;charset=GBK").append(CRLF);
responseInfo.append("Content-Type:text/html").append(CRLF);
responseInfo.append("Content-Length:").append(size).append(CRLF);
responseInfo.append(CRLF);
//3.正文
responseInfo.append(content.toString());

//写出到客户端
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(client.getOutputStream(),"UTF-8"));
bw.write(responseInfo.toString());
bw.flush();
} catch (Exception e) {
System.out.println("客户端连接出现错误...");
}
}
//停止服务
public void stop() {

}

}
  • 封装响应信息
    • 动态添加内容print;
    • 累加字节数的长度;
    • 根据状态码拼接响应头协议;
    • 根据状态码同意推送出去;
    • 调用处:动态调用print + 传入状态码推送。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package com.zhuchuli.server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.Date;

public class Response {
//缓存输出流
private BufferedWriter bw;
//正文
private StringBuilder content;
//协议头(状态行与响应头 回车与空格)信息
private StringBuilder headInfo;
private final String BLANK=" ";//空格
private final String CRLF="\r\n";//换行符
private int len;//正文的字节数
private Response() {
content=new StringBuilder();
headInfo=new StringBuilder();
len=0;
}
public Response(Socket client) {
this();
try {
bw=new BufferedWriter(new OutputStreamWriter(client.getOutputStream(),"UTF-8"));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
headInfo=null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
headInfo=null;
}
}
public Response(OutputStream os) {
this();
bw=new BufferedWriter(new OutputStreamWriter(os));
}
//动态添加内容
public Response print(String info) {
content.append(info);
len+=info.getBytes().length;
return this;//流模式 返回自身
}
public Response println(String info) {
content.append(info).append(CRLF);
len+=(info+CRLF).getBytes().length;
return this;//流模式 返回自身
}
//推送响应信息
public void pushToBrower(int code) throws IOException {
if(null == headInfo) {
code=505;
}
createHeadInfo(code);
bw.append(headInfo);
bw.append(content);
bw.flush();
}
//构建头信息
private void createHeadInfo(int code) {
//1.响应行 Http/1.1 200 OK
headInfo.append("HTTP/1.1").append(BLANK);
headInfo.append(code).append(BLANK);
switch(code) {
case 200: headInfo.append("OK").append(CRLF);
break;
case 404: headInfo.append("NOT FOUND").append(CRLF);
break;
case 505: headInfo.append("SERVER ERROR").append(CRLF);
break;
}
//2.响应头:(最后一行存在空行)
/* Date:Mon,31 Dec208804:36:57GMT
* Server:zhuchuli Server/0.0.1;charset=GBK
* Content-Type:text/html
* Content-Length:39725426
*/
headInfo.append("Date:").append(new Date()).append(CRLF);
headInfo.append("Server:").append("zhuchuli Server/0.0.1;charset=GBK").append(CRLF);
headInfo.append("Content-Type:text/html").append(CRLF);
headInfo.append("Content-Length:").append(len).append(CRLF);
headInfo.append(CRLF);

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.zhuchuli.server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

/***
* 目标:封装响应信息
* 1、内容可以动态添加。
* 2、只关注状态码,拼接好响应信息。
* @author 朱楚利
*
*/
public class Server03 {
private ServerSocket serverSocket;
public static void main(String[] args) {
Server03 server=new Server03();
server.start();
}
//启动服务
public void start() {
try {
serverSocket=new ServerSocket(8888);
receive();
} catch (IOException e) {
System.out.println("服务器启动失败...");
e.printStackTrace();
}
}
//接收连接处理
public void receive() {
try {
Socket client = serverSocket.accept();
System.out.println("一个客户端建立了连接....");
//获取请求协议
InputStream is=client.getInputStream();
byte[] datas=new byte[1024*1024];
int len=is.read(datas);
String requestInfo = new String(datas,0,len);
System.out.println(requestInfo);

Response response=new Response(client);
//只关注了内容
response.print("<html>");
response.print("<head>");
response.print("<title>");
response.print("服务器响应成功");
response.print("</title>");
response.print("</head>");
response.print("<body>");
response.print("zhuchuli Server回来了...");
response.print("</body>");
response.print("</html>");

//只关注了状态码
response.pushToBrower(200);

} catch (Exception e) {
System.out.println("客户端连接出现错误...");
}
}
//停止服务
public void stop() {

}

}
  • 封装请求信息
    • 通过分解字符串获取method、url和请求参数 Post请求参数可能在请求体中存在。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.zhuchuli.server;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

/***
* 封装请求协议:获取method、URI以及请求参数
* @author 朱楚利
*
*/
public class Request {
private String requestInfo;//协议信息
private String method;//请求方式
private String url;//请求的url
private String queryStr;//请求参数
private final String CRLF="\r\n";//换行符
public Request(Socket client) throws IOException {
this(client.getInputStream());
}
public Request(InputStream is) {
byte[] datas=new byte[1024*1024];
int len;
try {
len = is.read(datas);
this.requestInfo = new String(datas,0,len);
System.out.println(requestInfo);
} catch (Exception e) {
return;
}
//分解字符串
parseRequestInfo();
}

private void parseRequestInfo() {
System.out.println("-------分解---------");
System.out.println("------1、获取请求方式:开头到第一个/----------");
this.method=this.requestInfo.substring(0, this.requestInfo.indexOf("/")-1).toLowerCase();
System.out.println("------2、获取请求URL:第一个/到HTTP/----------");
System.out.println("---------可能包含请求参+数?前面的为url------------");
//1)获取第一个/位置
int startIdx=this.requestInfo.indexOf("/")+1;
//2)获取第一个/HTTP/位置
int endIdx=this.requestInfo.indexOf("HTTP/");
//3)分割字符串
this.url=this.requestInfo.substring(startIdx,endIdx);
//4)获取问号的位置
int queryIdx=this.url.indexOf("?");
if(queryIdx >= 0) {//表示带有参数
String[] urlArray=this.url.split("\\?");
this.url=urlArray[0];
this.queryStr=urlArray[1].trim();//请求参数
}
System.out.println("------3、获取请求URL后面的请求参数GET已经获取,如果是Post可能在请求体中----------");
if(method.equals("post")) {

String qStr= this.requestInfo.substring(this.requestInfo.lastIndexOf(CRLF)).trim();
if(null== queryStr) {
queryStr=qStr;
}else {
queryStr += "&"+qStr;
}
}
queryStr = null == queryStr?"":queryStr;
System.out.println(method+"---->"+url+"---->"+this.queryStr);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package com.zhuchuli.server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

/***
* 目标:封装请求信息
* @author 朱楚利
*
*/
public class Server04 {
private ServerSocket serverSocket;
public static void main(String[] args) {
Server04 server=new Server04();
server.start();
}
//启动服务
public void start() {
try {
serverSocket=new ServerSocket(8888);
receive();
} catch (IOException e) {
System.out.println("服务器启动失败...");
e.printStackTrace();
}
}
//接收连接处理
public void receive() {
try {
Socket client = serverSocket.accept();
System.out.println("一个客户端建立了连接....");
//获取请求协议
Request request=new Request(client);
//获取响应协议
Response response=new Response(client);
//只关注了内容
response.print("<html>");
response.print("<head>");
response.print("<title>");
response.print("服务器响应成功");
response.print("</title>");
response.print("</head>");
response.print("<body>");
response.print("zhuchuli Server回来了...");
response.print("</body>");
response.print("</html>");

//只关注了状态码
response.pushToBrower(200);

} catch (Exception e) {
System.out.println("客户端连接出现错误...");
}
}
//停止服务
public void stop() {

}

}
  • 处理请求参数
    • 通过Map封装请求参数两个方法:getParameter(String key)getParameterValues(String key)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package com.zhuchuli.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/***
* 封装请求协议:封装请求参数为Map
* @author 朱楚利
*
*/
public class Request02 {
private String requestInfo;//协议信息
private String method;//请求方式
private String url;//请求的url
private String queryStr;//请求参数
private Map<String,List<String>> parameterMap;
private final String CRLF="\r\n";//换行符
public Request02(Socket client) throws IOException {
this(client.getInputStream());
}
public Request02(InputStream is) {
parameterMap = new HashMap<String, List<String>>();
byte[] datas=new byte[1024*1024];
int len;
try {
len = is.read(datas);
this.requestInfo = new String(datas,0,len);
System.out.println(requestInfo);
} catch (Exception e) {
return;
}
//分解字符串
parseRequestInfo();
}

private void parseRequestInfo() {
System.out.println("-------分解---------");
System.out.println("------1、获取请求方式:开头到第一个/----------");
this.method=this.requestInfo.substring(0, this.requestInfo.indexOf("/")-1).toLowerCase();
System.out.println("------2、获取请求URL:第一个/到HTTP/----------");
System.out.println("---------可能包含请求参+数?前面的为url------------");
//1)获取第一个/位置
int startIdx=this.requestInfo.indexOf("/")+1;
//2)获取第一个/HTTP/位置
int endIdx=this.requestInfo.indexOf("HTTP/");
//3)分割字符串
this.url=this.requestInfo.substring(startIdx,endIdx);
//4)获取问号的位置
int queryIdx=this.url.indexOf("?");
if(queryIdx >= 0) {//表示带有参数
String[] urlArray=this.url.split("\\?");
this.url=urlArray[0];
this.queryStr=urlArray[1].trim();//请求参数
}
System.out.println("------3、获取请求URL后面的请求参数GET已经获取,如果是Post可能在请求体中----------");
if(method.equals("post")) {

String qStr= this.requestInfo.substring(this.requestInfo.lastIndexOf(CRLF)).trim();
if(null== queryStr) {
queryStr=qStr;
}else {
queryStr += "&"+qStr;
}
}
queryStr = null == queryStr?"":queryStr;
System.out.println(method+"---->"+url+"---->"+this.queryStr);
//转成Map fav=1,fav=2,uname=sss,age=18 others=
convertMap();
}
//处理请求参数为map
private void convertMap() {
//分割字符串 "&"
String[] keyValues= this.queryStr.split("&");
for(String keyValue:keyValues) {
//再次分割 "="
String[] kv=keyValue.split("=");
//保证数组的长度为2
kv = Arrays.copyOf(kv, 2);
//获取key和value
String key=kv[0];
String value=kv[1] == null?null:decode(kv[1],"UTF-8");
//存储到map中
if(!parameterMap.containsKey(key)) { //表示第一次存储
parameterMap.put(key,new ArrayList<String>());
}
parameterMap.get(key).add(value);
}
}
/**
* 处理中文问题
* @return
*/
private String decode(String value,String enc) {
try {
return java.net.URLDecoder.decode(value,enc);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}

}
/***
* 通过name获取对应的表单的多个值
* @param key
* @return
*/
public String[] getParameterValues(String key) {
List<String> list=this.parameterMap.get(key);
if(null == list || list.size()<1) {
return null;
}
//将list转化为String[]
return list.toArray(new String[0]);
}
/***
* 通过name获取对应的表单的一个值
* @param key
* @return
*/
public String getParameter(String key) {
String[] values=getParameterValues(key);
return values == null?null:values[0];
}
//获取请求方式
public String getMethod() {
return method;
}
//获取请求路径
public String getUrl() {
return url;
}
//获取请求参数
public String getQueryStr() {
return queryStr;
}


}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package com.zhuchuli.server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

/***
* 目标:将获取的请求参数转化为Map
* @author 朱楚利
*
*/
public class Server05 {
private ServerSocket serverSocket;
public static void main(String[] args) {
Server05 server=new Server05();
server.start();
}
//启动服务
public void start() {
try {
serverSocket=new ServerSocket(8888);
receive();
} catch (IOException e) {
System.out.println("服务器启动失败...");
e.printStackTrace();
}
}
//接收连接处理
public void receive() {
try {
Socket client = serverSocket.accept();
System.out.println("一个客户端建立了连接....");
//获取请求协议
Request02 request=new Request02(client);
//获取响应协议
Response response=new Response(client);
//只关注了内容
response.print("<html>");
response.print("<head>");
response.print("<title>");
response.print("服务器响应成功");
response.print("</title>");
response.print("</head>");
response.print("<body>");
response.print("zhuchuli Server回来了..."+request.getParameter("uname"));
response.print("</body>");
response.print("</html>");

//只关注了状态码
response.pushToBrower(200);

} catch (Exception e) {
System.out.println("客户端连接出现错误...");
}
}
//停止服务
public void stop() {

}

}
  • 引入Servlet
    • 将业务代码解耦到对应的业务类中(具体的Servlet)
    • 例子:Request与Response与上面雷同。
1
2
3
4
5
6
7
8
9
package com.zhuchuli.server;
/***
* 服务器小脚本接口
* @author 朱楚利
*
*/
public interface Servlet {
void service(Request request,Response response);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.zhuchuli.server;

public class RegisterServlet implements Servlet{

@Override
public void service(Request request,Response response) {
response.print("<html>");
response.print("<head>");
response.print("<title>");
response.print("第一个Servlet");
response.print("</title>");
response.print("</head>");
response.print("<body>");
response.print("注册成功:"+request.getParameter("uname"));
response.print("</body>");
response.print("</html>");
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.zhuchuli.server;

public class LoginServlet implements Servlet{

@Override
public void service(Request request,Response response) {
response.print("<html>");
response.print("<head>");
response.print("<title>");
response.print("第一个Servlet");
response.print("</title>");
response.print("</head>");
response.print("<body>");
response.print("欢迎回来:"+request.getParameter("uname"));
response.print("</body>");
response.print("</html>");
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.zhuchuli.server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

/***
* 目标:加入Servlet,解耦了业务代码
* @author 朱楚利
*
*/
public class Server06 {
private ServerSocket serverSocket;
public static void main(String[] args) {
Server06 server=new Server06();
server.start();
}
//启动服务
public void start() {
try {
serverSocket=new ServerSocket(8888);
receive();
} catch (IOException e) {
System.out.println("服务器启动失败...");
e.printStackTrace();
}
}
//接收连接处理
public void receive() {
try {
Socket client = serverSocket.accept();
System.out.println("一个客户端建立了连接....");
//获取请求协议
Request request=new Request(client);
//获取响应协议
Response response=new Response(client);
//只关注了内容
if(request.getUrl().equals("login")) {
Servlet servlet=new LoginServlet();
servlet.service(request, response);
}else if(request.getUrl().equals("reg")){
Servlet servlet=new RegisterServlet();
servlet.service(request, response);
}
//只关注了状态码
response.pushToBrower(200);

} catch (Exception e) {
System.out.println("客户端连接出现错误...");
}
}
//停止服务
public void stop() {

}

}
  • 整合web.xml
    • 根据配置文件动态的读取类名,再进行反射获取具体的Servlet来处理业务,真正的以不变应万变。
    • 例子:Request类、Response类、Servlet接口、LoginServlet类、RegisterServlet类与上面雷同;web.xml配置文件暂时存放在src下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.zhuchuli.server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

/***
* 目标:整合配置文件
* @author 朱楚利
*
*/
public class Server07 {
private ServerSocket serverSocket;
public static void main(String[] args) {
Server07 server=new Server07();
server.start();
}
//启动服务
public void start() {
try {
serverSocket=new ServerSocket(8888);
receive();
} catch (IOException e) {
System.out.println("服务器启动失败...");
e.printStackTrace();
}
}
//接收连接处理
public void receive() {
try {
Socket client = serverSocket.accept();
System.out.println("一个客户端建立了连接....");
//获取请求协议
Request request=new Request(client);
//获取响应协议
Response response=new Response(client);
//只关注了内容
Servlet servlet=WebApp.getServletFromUrl(request.getUrl());
System.out.println(servlet+"--->"+servlet);
if(null!=servlet) {
servlet.service(request, response);
//只关注了状态码
response.pushToBrower(200);
}else {
//错误...
response.pushToBrower(404);
}
} catch (Exception e) {
System.out.println("客户端连接出现错误...");
}
}
//停止服务
public void stop() {

}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.zhuchuli.server;

import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/***
* 解析配置文件
* @author 朱楚利
*
*/
public class WebApp {
private static WebContext webContext;
static {
try {
//SAX解析
//1.获取解析工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
//2.从解析工厂获取解析器
SAXParser parser = factory.newSAXParser();
//3.编写处理器
//4.加载文档document注册处理器
WebHandler handler = new WebHandler();
//5、解析
parser.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("web.xml"), handler);
//分析和处理数据
webContext=new WebContext(handler.getEntitys(), handler.getMappings());
}catch(Exception e) {
System.out.println("解析配置文件错误");
}
}
/***
* 通过url获取配置文件对应的Servlet
* @param url
* @return
*/
public static Servlet getServletFromUrl(String url) {
String className = webContext.getClz("/" + url);
Class clz;
try {
clz = Class.forName(className);
Servlet servlet=(Servlet) clz.getConstructor().newInstance();
return servlet;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package com.zhuchuli.server;

import java.util.ArrayList;
import java.util.List;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/***
* 处理器
* @author 朱楚利
*
*/

public class WebHandler extends DefaultHandler{
//定义容器 用于存储用户
private List<Entity> entitys;
private List<Mapping> mappings;
private Entity enity;
private Mapping mapping;
//存储当前操作的标签名
private String tag;
private boolean isMapping = false;//标志操作哪个标签
@Override
public void startDocument() throws SAXException {
entitys=new ArrayList<Entity>();
mappings=new ArrayList<Mapping>();
}
//解析元素 标签
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if(null!=qName) {
tag=qName;//存储当前操作的标签名
if(tag.equals("servlet")) {
enity=new Entity();
isMapping = false;
}else if(tag.equals("servlet-mapping")) {
mapping=new Mapping();
isMapping = true;
}
}
}
//标签内容
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String contents=new String(ch,start,length).trim();
//trim()方法 去掉前后空格
if(null != tag) {
if(isMapping) { // 操作servlet-mapping
if(tag.equals("servlet-name")) {
mapping.setName(contents);
}else if(tag.equals("url-pattern")) {
mapping.addPatterns(contents);
}
}else {
if(tag.equals("servlet-name")) {
enity.setName(contents);
}else if(tag.equals("servlet-class")) {
enity.setClz(contents);
}
}
}
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if(null!=qName) {
if(qName.equals("servlet")) {
entitys.add(enity);
}else if(qName.equals("servlet-mapping")) {
mappings.add(mapping);
}
}
tag=null;
}
@Override
public void endDocument() throws SAXException {

}
public List<Entity> getEntitys() {
return entitys;
}
public List<Mapping> getMappings() {
return mappings;
}


}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.zhuchuli.server;

import java.util.HashSet;
import java.util.Set;
/***
* servlet-mapping
* @author 朱楚利
*
*/
public class Mapping {
private String name;
private Set<String> patterns;

public Mapping() {
patterns = new HashSet<String>();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<String> getPatterns() {
return patterns;
}
public void setPatterns(Set<String> patterns) {
this.patterns = patterns;
}
public void addPatterns(String pattern) {
this.patterns.add(pattern);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.zhuchuli.server;
/***
* servlet
* @author 朱楚利
*
*/
public class Entity {
private String name;
private String clz;

public Entity() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClz() {
return clz;
}
public void setClz(String clz) {
this.clz = clz;
}


}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.zhuchuli.server.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern>
<url-pattern>/g</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>reg</servlet-name>
<servlet-class>com.zhuchuli.server.RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>reg</servlet-name>
<url-pattern>/reg</url-pattern>
<url-pattern>/r</url-pattern>
</servlet-mapping>
</web-app>
  • 高效分发器
    • 加入了多线程,可以同时处理多个请求,使用的是短链接。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.zhuchuli.server;

import java.io.IOException;
import java.net.Socket;
/***
* 分发器
* @author 朱楚利
*
*/
public class Dispatcher implements Runnable{
private Socket client;
private Request request;
private Response response;
public Dispatcher(Socket client) {
this.client=client;
try {
//获取请求协议
request=new Request(client);
//获取响应协议
response=new Response(client);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
this.release();
}
}
@Override
public void run() {
try {
Servlet servlet=WebApp.getServletFromUrl(request.getUrl());
if(null!=servlet) {
servlet.service(request, response);
//只关注了状态码
response.pushToBrower(200);
}else {
//错误...
response.pushToBrower(404);
}
}catch(Exception e) {
//错误...
try {
response.pushToBrower(505);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
release();
}
//释放资源
private void release() {
try {
client.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.zhuchuli.server;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

/***
* 目标:多线程处理加入分发器
* @author 朱楚利
*
*/
public class Server08 {
private ServerSocket serverSocket;
private boolean isRunning;
public static void main(String[] args) {
Server08 server=new Server08();
server.start();
}
//启动服务
public void start() {
try {
serverSocket=new ServerSocket(8888);
isRunning = true;
receive();
} catch (IOException e) {
System.out.println("服务器启动失败...");
stop();
e.printStackTrace();

}
}
//接收连接处理
public void receive() {
while(isRunning) {
try {
Socket client = serverSocket.accept();
System.out.println("一个客户端建立了连接....");
new Thread(new Dispatcher(client)).start();
} catch (Exception e) {
System.out.println("客户端连接出现错误...");
}
}
}
//停止服务
public void stop() {
isRunning=false;
try {
this.serverSocket.close();
System.out.println("服务器已经停止");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}
  • 经典404以及首页处理
    • 读取错误页面和首页内容即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package com.zhuchuli.server;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
/***
* 分发器
* @author 朱楚利
*
*/
public class Dispatcher2 implements Runnable{
private Socket client;
private Request request;
private Response response;
public Dispatcher2(Socket client) {
this.client=client;
try {
//获取请求协议
request=new Request(client);
//获取响应协议
response=new Response(client);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
this.release();
}
}
@Override
public void run() {
try {
if(null==request.getUrl() || request.getUrl().equals("")) {
//错误...
//类加载器
InputStream is=Thread.currentThread().getContextClassLoader().getResourceAsStream("index.html");
response.print(new String(is.readAllBytes(),"utf-8"));
response.pushToBrower(200);
is.close();
return;
}
Servlet servlet=WebApp.getServletFromUrl(request.getUrl());
if(null!=servlet) {
servlet.service(request, response);
//只关注了状态码
response.pushToBrower(200);
}else {
//错误...
//类加载器
InputStream is=Thread.currentThread().getContextClassLoader().getResourceAsStream("error.html");
response.print(new String(is.readAllBytes(),"utf-8"));
response.pushToBrower(404);
is.close();
}
}catch(Exception e) {
//错误...
try {
response.pushToBrower(505);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
release();
}
//释放资源
private void release() {
try {
client.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}

—> 还差中文乱码有待解决 <—-

内容到这里暂时就结束了