计算机网络——JAVA实现web服务器

趁着这几天刚开学,时间还比较宽裕,决定来整理一下自己的实验报告,先从计网开始。

实验目的

  1. 处理一个 http 请求
  2. 接收并解析 http 请求 

  3. 从服务器文件系统中获得被请求的文件 

  4. 创建一个包括被请求的文件的 http 响应信息 

  5. 直接发送该信息到客户端 


实验环境

  1. 系统:Mac OS X 10.13.1
  2. 语言:JAVA
  3. 浏览器:Google Chrome

实验步骤

前期准备

为了完成本次试验的要求,我编写了两个java代码文件,分别命名为:“HttpRequest.java”和“WebServer.java”。除此之外,我还编写了两个带跳转的测试网页“index.html”和“next.html”以及其各自包含的图片“first.jpg”和“next.jpg”

  • HttpRequest.java的主要功能为:获取http请求中的套接字的输入流和输出流,即完成“对从服务器端发回的数据流”和“将要发送到服务器端的数据流”的处理和输出。
  • WebServer.java的主要功能为:创建线程,连续创建套接字以及处理http请求。

文件清单如下:
Alt text

测试阶段

我们使用jdk的javac指令对java代码进行编译,然后启动服务器的默认端口8080(代码中预设),然后在Google Chrome浏览器中对各种情况进行测试和检测输出,具体步骤如下。

  1. 首先我们启动终端,进入Java代码所在的文件夹下,然后利用javac命令编译main方法所在的Java文件“WebServer.java”,启动服务器,具体如下:
    Alt text
  2. 由于代码中设置的启动端口为8080,如下:
    Alt text
    所以我们在浏览器中需要输入网址“localhost:8080/index.html”以达到访问该网页的目的,具体如下:
    Alt text
  3. 在成功访问该网页之后,我们来观察一下终端的输出情况,发现其请求网页index.html请求成功如下:
    Alt text
    其请求网页包含的图片first.jpg请求成功如下:
    Alt text
  4. 然后点击跳转链接,跳转至next.html,访问下一个网页,同样的,网页跳转成功如下:
    Alt text
  5. 在成功打开网页之后,我们来观察一下终端的输出情况。同样的,请求网页“next.html”和“next.jpg”成功,输出如下结果:
    Alt text
    Alt text
  6. 当然,除了能成功访问的网页外,我们还需要测试一下错误路径的文件访问结果,于是在浏览器中输入一个错误的网址如“localhost:8080/404.html”,结果如下,提示文件不存在:
    Alt text
    终端反馈输出对404.html这个不存在的文件的访问结果:
    Alt text

代码附录

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
/***********HttpRequest***************/
import java.io.* ;
import java.net.* ;
import java.util.* ;

final class HttpRequest implements Runnable {
final static String CRLF = "\r\n";
Socket socket;

//构造请求类
public HttpRequest(Socket socket) throws Exception {
this.socket = socket;
}

//启动线程
public void run() {
try {
processRequest();
}
catch (Exception e) {
System.out.println(e);
}
}

private void processRequest() throws Exception {
//获取套接字的输入流,即从服务器端发回的数据流
InputStream is = socket.getInputStream();
//获取套接字的输出流,返回的输出流就是将要发送到服务器端的数据流
DataOutputStream os = new DataOutputStream(socket.getOutputStream());

//缓冲字符输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));

// 获取http的request信息
String requestLine = br.readLine();

// 从请求行中提取文件信息
StringTokenizer tokens = new StringTokenizer(requestLine);
// 由于token将请求行分解为了单词,我们在这里跳过第一个单词,即“GET”
tokens.nextToken();
// GET之后即为文件名
String fileName = tokens.nextToken();

// 为了将文件转化到当前目录下,我们在其之前加一个“.”
fileName = "." + fileName ;

// 打开文件
FileInputStream fis = null ;
boolean fileExists = true ;

//判断文件是否查询到
try {
fis = new FileInputStream(fileName);
}
catch (FileNotFoundException e) {
fileExists = false ;
}


//测试输出
System.out.println("\n*************Request**************\n");
System.out.println(requestLine);
String headerLine = null;
while ((headerLine = br.readLine()).length() != 0) {
System.out.println(headerLine);
}

// 构造response信息
String statusLine = null;
String contentTypeLine = null;
String entityBody = null;

if (fileExists) {
//执行成功的话返回“200 OK”
statusLine = "HTTP/1.0 200 OK" + CRLF;
contentTypeLine = "Content-Type: "+contentType(fileName) + CRLF;
}

else {
//执行失败,文件未找到的时候返回“404 File Not Found”
statusLine = "HTTP/1.0 404 File Not Found" + CRLF;
contentTypeLine = "Content-Type: text/html" + CRLF;
entityBody = "<HTML>" +
"<HEAD><TITLE>Not Found</TITLE></HEAD>" +
"<P>File name:</P>"+fileName+
"<BODY>Not Found</BODY></HTML>";
}

// 将上述返回值依次输出
os.writeBytes(statusLine);
os.writeBytes(contentTypeLine);
os.writeBytes(CRLF);
System.out.println("\n*************Response**************\n");
System.out.println(statusLine);


//调用文件类型输出方法
if (fileExists) {
sendBytes(fis, os);
fis.close();
}
else {
os.writeBytes(entityBody) ;
}

// 结束关闭
os.close();
br.close();
socket.close();
}

private static void sendBytes(FileInputStream fis, OutputStream os) throws Exception {
//构造一个1K缓冲区,承接套接字的内容
byte[] buffer = new byte[1024];
int bytes = 0;

//将请求的文件复制到套接字的输出流中。
while ((bytes = fis.read(buffer)) != -1) {
os.write(buffer, 0, bytes);
}
}

private static String contentType(String fileName) {
if(fileName.endsWith(".htm") || fileName.endsWith(".html")) {
return "text/html";
}
else return "other Content-type" ;
}
}
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
/***********WebServer***************/
import java.io.* ;
import java.net.* ;
import java.util.* ;

public final class WebServer {
public static void main(String argv[]) throws Exception {

int port=8080;
// 建立一个套接字
ServerSocket socket = new ServerSocket(port);

while (true) {
// 监听一个tcp连接的request.
Socket connection = socket.accept();

// 处理HTTP请求消息。
HttpRequest request = new HttpRequest(connection);

// 建立一个线程处理请求
Thread thread = new Thread(request);
thread.start();
}
}
}
小手一抖⬇️