计算机网路实验二 多线程Web服务器的设计与实现
一、 实验目的及任务
1、实验目的
熟悉简单网络的搭建与基本配置;
熟悉socket、多线程编程;
熟悉JDK编程工具的基本使用;
熟悉HTTP协议;
熟悉Web服务器的基本工作原理和配置。
2、实验任务
(1)以JDK为开发工具,利用Socket通信机制实现一个多线程的WEB服务器,该服务器具有以下功能:
能够并行服务于多个请求。
对于每个请求,显示接收到的HTTP请求报文的内容,并产生适当的响应(若找到用户请求对象,则返回该对象。否则发送一个包含适当提示信息的响应消息,从而可以在浏览器窗口中显示差错信息。)
(2)搭建一个简单的网络环境,调试 web服务器程序。
二、实验性质
设计性实验
三、 实验环境
操作系统:Windows2000 server
开发工具:JDK或其他开发工具
实验设备:交换机(一台)、计算机(两台)、网线若干
四、 实验步骤
1、 开发程序
2、 搭建简单的C/S网络
3、 配置并测试web服务器程序
五、 实验内容
1.Web服务器程序的结构框图如下:
2.实验源代码如下:
MultiTHreadWebServer.java
package networktwo;
import java.net.* ;
//创建监听端口等待TCP连接请求
public final class MultiThreadWebServer {
public static void main(String argv[]) throws Exception {
//创建Web服务器的监听端口(套接字)
ServerSocket socket = new ServerSocket(8900);
while (true) {
// Listen for a TCP connection request.
Socket connection = socket.accept();
//创建httprequest对象,将标志着所建立TCP连接的Socket作为参数传递到它的构造函数中
HttpRequest request = new HttpRequest(connection);
//创建一个Thread对象,将HttpRequest对象作为参数传递给Thread的构造函数
Thread thread = new Thread(request);
//启动线程
thread.start();
}
}
}
HttpRequest.java
package networktwo;
import java.io.* ;
import java.net.* ;
import java.util.* ;
final class HttpRequest implements Runnable {
//用”回车换行”作为Response消息头部行的结束
final static String CRLF = "\r\n";
Socket socket;
//将参数socket即Socket connection赋给当前对象的(this)成员变量socket即this.socket
public HttpRequest(Socket socket) throws Exception {
this.socket=socket;
}
public void run() {
try {
//调用实现Request消息处理绝大部分操作的方法
processRequest();
} catch (Exception e) {
System.out.println(e);
}
}
//实现Request消息处理
private void processRequest() throws Exception {
InputStreamReader is=new InputStreamReader(socket.getInputStream());
DataOutputStream os=new DataOutputStream(socket.getOutputStream());
BufferedReader br = new BufferedReader(is);//往缓冲区读取(缓存)
String requestLine;
//从输入流中读取字符,直到遇到CRLF为止
requestLine=br.readLine();
System.out.println(requestLine);
//设置循环操作来获取Request消息的所有头部行
String headerLine = null;
while ((headerLine = br.readLine()).length() != 0) {
System.out.println(headerLine);
}
//Openfile,获取文件信息
StringTokenizer tokens = new StringTokenizer(requestLine);
tokens.nextToken(); //跳过请求方法
String fileName = tokens.nextToken();//获取路径
fileName="."+fileName;
FileInputStream fis = null ;
boolean fileExists = true ;
try {
fis = new FileInputStream(fileName);
} 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: " + contentType( fileName ) + 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>";
}
os.writeBytes(statusLine);
os.writeBytes(contentTypeLine);
os.writeBytes(CRLF);
//如果文件存在调用另一个方法发送文件,不存在则向客户发送一个HTML编码的错误信息
if (fileExists) {
sendBytes(fis, os);
fis.close();
} else {
os.writeBytes(entityBody);
}
//关闭输入/出流和connection socket。
System.out.println();
os.close();
br.close();
socket.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);
}
}
//判断文件类型
private static String contentType(String fileName) {
if(fileName.endsWith(".htm") || fileName.endsWith(".html")|| fileName.endsWith(".txt")) {
return "text/html";
}
if(fileName.endsWith(".jpg")) {
return "image/jpeg";
}
if(fileName.endsWith(".gif")) {
return "image/gif";
}
return "application/octet-stream";
}
}
服务器的运行结果如下:
访问存在的文本文档:
访问已存在的图片文件如下:
访问不存在的文件: