1、实验目的
熟悉简单网络的搭建与基本配置;
熟悉socket、多线程编程;
熟悉JDK编程工具的基本使用;
熟悉HTTP协议;
熟悉Web服务器的基本工作原理和配置。
2、实验任务
以JDK为开发工具,利用Socket通信机制实现一个多线程的WEB服务器,该服务器具有以下功能:
能够并行服务于多个请求。
对于每个请求,显示接收到的HTTP请求报文的内容,并产生适当的响应(若找到用户请求对象,则返回该对象。否则发送一个包含适当提示信息的响应消息,从而可以在浏览器窗口中显示差错信息。)
二、实验性质及学时
验证性实验,4学时
三、实验环境
与Internet连接的计算机网络系统;主机操作系统为Windows或Linux;
协议分析系统Wireshark。
四、实验报告内容(实验题目及答案,其中答案部分标红)
- Web服务器程序的结构框图如下:
源代码:
package webserver;
import java.net.ServerSocket;
import java.net.Socket;
public class MultiThreadWebServer {
public static void main(String argv[]) throws Exception {
//创建Web服务器的监听端口(套接字) TCP协议
var socket = new ServerSocket(8189);
while (true) {
// 监听8189端口
var connection = socket.accept();
//创建httprequest对象,将标志着所建立TCP连接的Socket作为参数传递到它的构造函数中
HttpServer request = new HttpServer(connection);
//创建一个Thread对象,将HttpRequest对象作为参数传递给Thread的构造函数
Thread thread = new Thread(request);
//启动线程
thread.start();
}
}
}
package webserver;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class HttpServer implements Runnable {
final static String CRLF = "\r\n";//用”回车换行”作为Response消息头部行的结束 该符号为一行的判定标准
Socket socket;
//构造方法
public HttpServer(Socket socket) {
this.socket=socket;
}
//报文处理
public void messageprocess() throws IOException {
try(InputStreamReader inStream=new InputStreamReader(socket.getInputStream()); DataOutputStream outStream=new DataOutputStream(socket.getOutputStream())){
//process input and send response
BufferedReader br = new BufferedReader(inStream);//往缓冲区读取(缓存)
//获取请求头
String requestLine = br.readLine();
System.out.println(requestLine);
String headerLine = null;
while ((headerLine = br.readLine()).length() != 0) {
System.out.println(headerLine);
}
switch(requestLine){
case "GET / HTTP/1.1":
get(br,outStream);
case "POST / HTTP/1.1":
post(br,outStream);
case "DELETE / HTTP/1.1":
del(outStream);
}
}catch (IOException e){
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//get方法处理
private void get(BufferedReader br,DataOutputStream outStream) throws Exception {
FileInputStream fis = null ;
boolean fileExists = true ;
try {
fis = new FileInputStream("F:/text/look.txt");
} catch (FileNotFoundException e) {
fileExists = false ;
}
// 分析客户Request消息的代码,并发送Response消息
String statusLine = null; //状态行
String contentTypeLine = null; //Content-Type行
String entityBody = null; //Entity body部分
//请求文件存在生成头部行Content-type
if (fileExists) {
statusLine="HTTP/1.1 200 OK"+CRLF; //指示返回内容的类型
contentTypeLine = "Content-type:text/html;charset=utf-8" + CRLF;
} else { //请求文件不存在生成状态行、头部行以及实体的主体,使用CRLF进行间隔
statusLine="HTTP/1.1 404 NotFound"+CRLF;
contentTypeLine = "Content-type: text/html"+CRLF;
entityBody ="<html><title>Not found</title><h1>404 NotFound</h1></html>";
}
outStream.writeBytes(statusLine);
outStream.writeBytes(contentTypeLine);
outStream.writeBytes(CRLF);
//如果文件存在调用另一个方法发送文件,不存在则向客户发送一个HTML编码的错误信息
if (fileExists) {
sendBytes(fis, outStream);
fis.close();
} else {
outStream.writeBytes(entityBody);
}
//关闭输入/出流和connection
System.out.println();
outStream.close();
}
private static void sendBytes(FileInputStream fis,OutputStream os) throws Exception {
byte[] buffer = new byte[1024];//1KB
int bytes=0;
while((bytes=fis.read(buffer))!=-1){
os.write(buffer,0,bytes);
}
}
//post方法处理
private void post(BufferedReader br,DataOutputStream outStream)throws IOException{
char[] c=new char[1024];
br.read(c);
var str=new String(c);
var sp=StringUtils.substringAfter(str, "name");
System.out.println(sp);
byte[] srtbyte = sp.getBytes();
FileOutputStream fis = null ;
boolean Exists = true ;
try {
fis = new FileOutputStream("F:/text/see.txt");
} catch (FileNotFoundException e) {
Exists = false ;
}
try {
fis.write(srtbyte);
}catch (Exception e){
e.printStackTrace();
Exists = false;
}finally {
fis.close();
}
// 分析客户Request消息的代码,并发送Response消息
String statusLine = null; //状态行
String contentTypeLine = null; //Content-Type行
String entityBody = null; //Entity body部分
if (Exists) {
statusLine="HTTP/1.1 200 OK"+CRLF; //指示返回内容的类型
contentTypeLine = "Content-type:text/html;charset=utf-8" + CRLF;
} else { //请求文件不存在生成状态行、头部行以及实体的主体,使用CRLF进行间隔
statusLine="HTTP/1.1 404 NotFound"+CRLF;
contentTypeLine = "Content-type: text/html"+CRLF;
entityBody ="<html><title>Not found</title><h1>404 NotFound</h1></html>";
}
outStream.writeBytes(statusLine);
outStream.writeBytes(contentTypeLine);
outStream.writeBytes(CRLF);
//如果文件存在调用另一个方法发送文件,不存在则向客户发送一个HTML编码的错误信息
//关闭输入/出流和connection socket。
System.out.println();
outStream.close();
}
//delete方法处理
private void del(DataOutputStream outStream) throws IOException {
FileOutputStream fis = null ;
boolean Exists = true ;
try {
fis = new FileOutputStream("F:/text/look.txt");
} catch (FileNotFoundException e) {
Exists = false ;
}
try {
fis.write(null);
}catch (Exception e){
Exists = false;
}finally {
fis.close();
}
// 分析客户Request消息的代码,并发送Response消息
String statusLine = null; //状态行
String contentTypeLine = null; //Content-Type行
String entityBody = null; //Entity body部分
if (Exists) {
statusLine="HTTP/1.1 200 OK"+CRLF; //指示返回内容的类型
contentTypeLine = "Content-type:text/html;charset=utf-8" + CRLF;
} else { //请求文件不存在生成状态行、头部行以及实体的主体,使用CRLF进行间隔
statusLine="HTTP/1.1 404 NotFound"+CRLF;
contentTypeLine = "Content-type: text/html"+CRLF;
entityBody ="<html><title>Not found</title><h1>404 NotFound</h1></html>";
}
outStream.writeBytes(statusLine);
outStream.writeBytes(contentTypeLine);
outStream.writeBytes(CRLF);
//如果文件存在调用另一个方法发送文件,不存在则向客户发送一个HTML编码的错误信息
//关闭输入/出流和connection socket。
outStream.close();
}
@Override
public void run() {
try {
messageprocess();
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务器的运行结果如下:
访问文档:
文件中的内容:
浏览器响应:
修改文档:发起post请求,修改文档:
文档中显示的结果:
清空文档:
发起delete请求
注:代码在运行的时候有问题会报错但并不会停止,可以完成HTTP这三种方法的操作。代码不完美,大家将就用。