文档库 最新最全的文档下载
当前位置:文档库 › 实验准备--Multi-Threaded Web Server--20151115

实验准备--Multi-Threaded Web Server--20151115

实验准备--Multi-Threaded Web Server--20151115
实验准备--Multi-Threaded Web Server--20151115

Programming Assignment 1: Building a

Multi-Threaded Web Server

本试验中我们将通过两个阶段来开发一个web服务器,最后完成一个能够并行服务与多个请求的多线程Web服务器。

我们将实现在RFC 1945定义的HTTP1.0。根据定义,每个Web page中的对象将通过单独的HTTP 消息来获取。所实现Web服务器将能够并发地服务于多个请求,这意味着Web服务器是多线程的。Web服务器的主线程负责侦听某个端口,当收到TCP连接请求时,将创建一个新的socket负责与该TCP连接,并创建新的线程具体负责通过该连接的消息传递。为了简化程序设计任务,我们分两阶段来设计Web服务器。第一阶段:编写仅仅显示所收到HTTP Request消息所有头部行的一个多线程Web服务器。当该程序运行正确后,将添加适当的代码以实现对Request消息的适当响应。

开发Web服务器时,可以通过Web浏览器来测试它。不过,所编写的Web服务器通常并不工作于80端口,因此,测试时在浏览器的地址栏中需要指定Web服务器的工作端口。例如:假设Web服务器运行在域名为https://www.wendangku.net/doc/dc12514870.html,的主机上,监听端口6789,我们想获取文件index.html。需要在浏览器的地址栏中输入如下的URL:

https://www.wendangku.net/doc/dc12514870.html,:6789/index.html

如果忽略了":6789", 浏览器则默认地认为Web服务器监听80端口。

当Web服务器遇到问题,将向浏览器发送包含适当响应消息的HTML页面,以便在浏览器中显示错误信息。[由谁负责生成错误响应消息HTML页面。。。,由本Web服务器。]

Web Server in Java: Part A

下面,我们将实现第一阶段的编程任务。当看到"?"时,你需要在该处添加相应的代码。

我们的第一个Web服务器将是多线程的,所收到的每个Request消息将交由单独的线程进行处理。这使得服务器可以并发地为多个客户服务, 或者是并发地服务于一个客户的多个请求.当创建一个新线程时,需要向线程的构造函数传递实现了Runnable 接口的类的一个实例(即通过实现接口Runnable来实现多线程)。这正是我们定义单独的类HttpRequest的原因。

Web服务器的结构如下:

import java.io.* ;

import https://www.wendangku.net/doc/dc12514870.html,.* ;

import java.util.* ;

public final class WebServer

{

public static void main(String argv[]) throws Exception

{

. . .

}

}

final class HttpRequest implements Runnable

{

. . .

}

通常,Web服务器为通过周知端口80收到的请求提供服务。你可以选择大于1024的任意端口作为Web服务器的监听端口,但需要记着在浏览器地址栏中输入URL时指定Web服务器的动作端口。

public static void main(String argv[]) throws Exception

{

// Set the port number.

int port = 6789;

. . .

}

下面,创建监听端口以等待TCP连接请求。由于Web服务器将不间断地提供服务,我们将侦听操作放在一个无穷循环的循环体中。这意味着需要通过在键盘上输入^C来结束Web服务器的运行。

// Establish the listen socket.

? ////ServerSocket socket = new ServerSocket(port);

// Process HTTP service requests in an infinite loop.

while (true) {

// Listen for a TCP connection request.

? ////Socket connection = socket.accept();

. . .

}

当收到请求后,我们创建一个HttpRequest对象,将标征着所建立TCP连接的Socket作为参数传递到它的构造函数中。

// Construct an object to process the HTTP request message.

HttpRequest request = new HttpRequest( ? ////connection);

// Create a new thread to process the request.

Thread thread = new Thread(request);

// Start the thread.

thread.start();

为了让HttpRequest对象在一个单独的线程中处理随后的HTTP请求,我们首先创建一个Thread对象,将HttpRequest对象作为参数传递给Thread的构造函数,然后调用Thread的start()方法启动线程。

当一个Thread创建并启动后,主线程回到了循环体的首部。主线程将被阻塞block在accept处,以等待另一个TCP 连接请求的到达。此时,刚刚创建的线程正在运行。当另一个TCP连接请求到达时,主线程将不管前面创建的线程是否结束,重复上面的操作,创建新线程负责新连接的请求处理。

到这为止,主线程的工作就完成了,后面我们将集中精力设计类HttpRequest。

我们声明HttpRequest类中的两个变量:CRLF and socket。根据HTTP规范, 我们需要用”回车换行”作为Response消息头部行的结束。因此,为了使用方便,我们定义了一个CRLR字符串变量。变量socket用作connection socket, 它将被类HttpRequest的构造函数初始化。

////不需要是其他类或其他类的子类吗?////也有说是从其他类继承并使用runnable接口。

final class HttpRequest implements Runnable

{

final static String CRLF = "\r\n";

Socket socket;

// Constructor ////HttpRequest的构造函数

public HttpRequest(Socket socket) throws Exception

{

////将参数socket即Socket connection赋给当前对象的(this)成员变量socket即this.socket

this.socket = socket;

}

// Implement the run() method of the Runnable interface.

public void run()

{

. . .

}

private void processRequest() throws Exception

{

. . .

}

}

为了将类HttpRequest的实例作为参数传输传递到Thread的构造函数中,(////用实现了Runnable接口的类的对象中所定义的run()方法, 来覆盖新创建的线程对象的run()方法

)HttpRequest必须实现Runnable接口。因此,必须定义HttpRequest的public方法run(),其返回值类型为void。我们在run()中调用实现Request消息处理绝大部分操作的方法processRequest()。

直到现在,我们其实一直在抛出异常, 而不是catching他们。不过,我们不能从方法run()中抛出异常,因为我们必须严格遵守Runnable接口对run()的声明。Runnable接口的run()方法不抛出任何异常。我们将在processRequest中放置处理代码,并从此在run方法中利用try/catch块处理异常。

// Implement the run() method of the Runnable interface.

public void run()

{

try {

processRequest();

} catch (Exception e) {

System.out.println(e);

}

}

现在,设计processRequest()中的代码。首先获得socket的输入/出流的reference引用。然后,我们给input stream包装过滤器(filters)。但是,输出流无须包装任何过滤器,主要原因是我们将向输出流直接写入bytes。

private void processRequest() throws Exception

{

// Get a reference to the socket's input and output streams.

InputStream is = ?; ////socket.getInputStream();

DataOutputStream os = ?;//// socket.getOutputStream();

// Set up input stream filters.

BufferedReader br = ?; ////new BufferedReader(new InputStreamReader(is));

. . .

}

现在我们已经准备好来获得客户发来的HTTP Request消息了(通过从socket的输入流读取消息)。类BufferedReader的方法readLine()方法将从输入流中读取字符,直到遇到CRLF为止(也就是从input stream中读取一行,行的结束符为CRLF)。

从input stream中读出的第一行为HTTP Request消息的请求行(参看教材的2.2 部分,了解请求行的定义)。

// Get the request line of the HTTP request message.

String requestLine = ?; ////br.readLine();

// Display the request line.

System.out.println();

System.out.println(requestLine);

读取消息的请求行后,读取消息的其它头部行。由于我们并不知道客户发送消息中有多少头部行,必须利用一个循环操作来获取Request消息的所有头部行。

// Get and display the header lines.

String headerLine = null;

while ((headerLine = br.readLine()).length() != 0) {

System.out.println(headerLine);

}

由于除了需要将头部行中的内容显示在屏幕上外,现阶段无须针对头部行做其它的处理,我们仅仅利用临时变量headerLine来保存头部行的信息。循环操作直到下面的表达式值等于0时停止。也就是读取的头部行的长度如果为零,表示读出了一个空行,意味着所有的头部行已经全部读出(参看教材的2.2 部分,头部行和entity body之间利用一个空行作为分割)。

(headerLine = br.readLine()).length()

后面我们将添加分析客户Request消息的代码,并发送Response消息。在进行后面的程序设计前,我们先完成第一阶段的任务,并通过浏览器来测试它。添加如下代码以关闭输入/出流和connection socket。

// Close streams and socket.

os.close();

br.close();

socket.close();

当程序编译成功后,以适当的端口作为参数运行Web服务器,并利用浏览器访问它。在浏览器地址栏中输入下面的示例:

https://www.wendangku.net/doc/dc12514870.html,:6789/

Web服务器将显示HTTP Request消息的内容。检查请求消息的格式是否与教材2.2中描述的HTTP Request消息格式相符。[问:即当用户的请求到达服务器后,服务器显示出的请求行各个头部行是否与所学格式一致?]

Web Server in Java: Part B

Web服务器不能仅仅显示收到的Request消息的内容,而是应该分析收到的Request消息并产生适当的Response消息。我们将忽略Request消息头部行中包含的信息,仅仅关注Request消息的

请求行中包含的文件名字。我们将假设客户发送的Request消息中的Request行总是使用GET方法,实际上,一个浏览器可能使用GET、POST和HEAD方法(HTTP1.0)

利用类StringTokenizer从Request行中解析出文件名字。首先,创建一个StringTokenizer 对象来容纳Request行;第二步:跳过Method字段(因为总是GET方法);第三步,解析出文件名字。

// Extract the filename from the request line.

StringTokenizer tokens = new StringTokenizer(requestLine);

tokens.nextToken(); // skip over the method, which should be "GET" String fileName = tokens.nextToken();

// Prepend a "." so that file request is within the current directory. fileName = "." + fileName;

由于浏览器在文件名字前加了一个“/“,我们在它前面加上一个字符”.”,从而限定从当前目录开始获取文件。

现在有了客户请求的文件名字,我们可以打开该文件作为向客户发送该文件的第一步。如果文件不存在,构造函数FileInputStream()将抛出异常FileNotFoundException,为了在抛出此可能的异常后不终止线程的执行,利用一个try/catch块将布尔型变量fileExists设置为false。后面我们将使用该变量来构建一个错误响应消息,而不是发送一个根本不存在的文件。

// Open the requested file.

FileInputStream fis = null;

boolean fileExists = true;

try {

fis = new FileInputStream(fileName);

} catch (FileNotFoundException e) {

fileExists = false;

}

////显示头部行:String headerLine = null;

////while ((headerLine = br.readLine()).length() != 0){

//// System.out.println(headerLine);}

////自加部分,来自PART A。

Response消息有三部分:the status line, the response headers, 和entity body。状态行、头部行以CRLF作为结束。利用变量statusLine来保存响应消息的statusline、contentTypeLine 保存Content-Type头部行信息。当文件不存在时,Web服务器将返回状态行为“404 Not Found “,entity body中保存利用HTML创建的错误消息????是的,用HTML构建一个消息,作为返回给用户的出错提示,该消息由entity body承载。

// Construct the response message.

String statusLine = null;

String contentTypeLine = null;

String entityBody = null;

if (fileExists) {

statusLine = ?//// "HTTP/1.1 200 OK" + CRLF;

contentTypeLine = "Content-type: " +

contentType( fileName ) + CRLF;

} else {

statusLine = ?; //// "HTTP/1.1 404 Not Found"+CRLF;

contentTypeLine = ?//// "Content-type: txt/html" + CRLF;

entityBody = "" +

"Not Found" +

"Not Found";

}

当文件存在,需要确定文件的MIME类型和发送适当的MIME-Type指示符,利用private方法contentType()实现该上述任务。该方法将返回包含在Conten-Type头部行的信息(字符串)。

现在,我们可以通过向socket的输出流写入status line 和唯一的一个header line 来向客户浏览器发送信息。

// Send the status line.

os.writeBytes(statusLine);

// Send the content type line.

os.writeBytes(?////contentTypeLine);

// Send a blank line to indicate the end of the header lines.

os.writeBytes(CRLF);

下面需要发送消息的entity body了。如果请求的文件存在,我们调用另一个方法来发送文件;如果请求的文件不存在,我们向客户发送一个HTML编码的错误消息(前面已经准备好,即在变量entityBody中。

// Send the entity body.

if (fileExists) {

sendBytes(fis, os);

fis.close();

} else {

os.writeBytes(?////entityBody);

}

发送完entitybody后,线程的任务已经全部完成,在结束线程前需要关闭流和socket.

我们还需要实现前面提到的两个方法:contentType()和sendBytes()。

private static void sendBytes(FileInputStream fis, OutputStream os) throws Exception

{

// Construct a 1K buffer to hold bytes on their way to the socket.

byte[] buffer = new byte[1024];

int bytes = 0;

// Copy requested file into the socket's output stream.

while((bytes = fis.read(buffer)) != -1 ) {

os.write(buffer, 0, bytes);

}

}

read()和write()均抛出异常,我们在sendBytes中并不处理这些异常,而是将异常处理的任务交给调用sendBytes的方法。

变量buffer,用于作为文件和输出流之间的中间存储空间。当从FileInputStream中读取字节时,,检查读取的字节是否为-1(即文件结束标识EOF)。如果[未???]读到了EOF,read()

返回已经放入buffer的字节数。利用方法类OutputStream的方法write()将保存在buffer 中的字节数据发送到输出流, write的参数buffer、0、bytes分别为byte数组的名字、第一个字节的位置、需要写出的字节数。

Web Server中需要完成最后一部分代码为contentType,实现根据文件的扩展名来确定所代表的MIME 类型。如果文件扩展名未知,则方法返回application/octet-stream.

private static String contentType(String fileName)

{

if(fileName.endsWith(".htm") || fileName.endsWith(".html")) {

return "text/html";

}

if(?) {////fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") ?;//// return "image/jpeg";

}

if(?) {////fileName.endsWith(".txt")

?; ////return "text/plain";

}

return "application/octet-stream";

}

到现在未知,我们完成了Web Server的第二阶段任务。尝试从保存有homepage的目录运行Web 服务器,记住在URL中包含Web服务器的工作端口。

java web实验报告

一.实验目的 实现学生信息管理系统,学生登录身份验证,信息的录入和信息的查询。并在实验的过程中熟练掌握网页设计的各种工具,如Dreamwawer,tomcat等,提高网页设计的能力。 二.实验过程 1.实现基本页面的设计,使用的工具:Dreamwawer。 2.实现服务器端程序设计,对页面数据进行处理。 3.与数据库进行连接,实现对学生数据的操作,如查询,存储, 修改等。 4.进一步完善系统,如页面的美化等等。 本系统有三个模块组成,学生登录模块,学生信息数据库模块,出错处理模块,学生登录以后输入相应的学号,密码,登陆学生信息界面,队学生的成绩等基本信息进行查询,若学号或密码出错,或者不符合,弹出相应的出错界面。本系统采用Jsp+JavaBean+SQL2000设计方式,其中JavaBean担当数据库连接以及逻辑控制,这样在Jsp就省去了繁琐的数据库连接,以及复杂的逻辑控制,使Jsp成为表示逻辑。 三.运用软件 Windows环境下的Tomcat7.0,SQL2000。 四.过程截图: 1.学生登录界面的设计:

2.与数据库系统的连接:

3.实现对学生数据的操作:

4.对学生数据的保护的完善。 1,验证学生身份: Student: import java.sql.*; public class student {

private String name; private String password; private String id; private String jiguan; private String sex; private String dep; public void setDep(String s){dep=s;} public String getDep(){return dep;} public void setSex(String s){sex=s;} public String getSex(){return sex;} public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { https://www.wendangku.net/doc/dc12514870.html, = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getJiguan() { return jiguan; } public void setJiguan(String jiguan) { this.jiguan = jiguan; }

WEB开发技术实验报告

实验一JSP开发环境构建 实验目的:了解动态页面技术及B/S系统 掌握开发环境的构建 理解Eclipse开发WEB应用 实验内容: 实训项目一:安装JDK并配置环境变量 请阐述配置环境变量的方法: 实训项目二:安装TOMCAT并配置Server.xml修改端口号为8090 问题一:如何测试TOMCAT是否已经成功启动? 问题二:在浏览器地址栏输入什么地址可以访问到TOMCA T的测试页? 请阐述配置Server.xml修改端口号为8090基本实验步骤: 实训项目三:应用Eclipse建立项目并浏览一个JSP页面 请阐述应用Eclipse建立项目并浏览一个JSP页面基本实验步骤: 实验心得:(遇到了哪些问题,如何解决的,有那些体会) 实验二JSP语法 实验目的:了解JSP程序的组成元素 掌握JSP中使用JA V A程序片段的方法 实验内容: 实训项目一:编写一个JSP页面输出26个小写英文字母表 实训项目二:编写页面实现九九乘法表 实训项目三:利用成员变量被所有客户共享这一性质,实现一个简单的计数器 实训项目四:使用JA V A表达式输出系统当前时间 实训项目五:编写程序shijian2_9.jsp和computer.jsp两个页面,在第一个页面中使用include动作标记动态包含文件computer.jsp,并向它传递一个矩形的长和宽,computer.jsp 收到参数后,计算矩形的面积,并显示结果。 实训项目六:编写3个JSP页面:main.jsp,first.jsp和second.jsp,将3个JSP文件保存在同一个WEB工程中,main.jsp使用include动作标记加载first.jsp和second.jsp页面。First.jsp 页面可以画一张表格,second.jsp页面可以计算两个正整数的最大公约数。当first.jsp被加载时,获取main.jsp页面include动作标记的param子标记提供的表格行数和列数,当second.jsp 被加载时,获取main.jsp页面include动作标记的param子标记提供的两个正整数的值。 要求:上机编程完成上述实训项目,上机演示给教师检查,从中挑选三个程序的核心代码写在实训报告上 实验核心代码:

Web程序设计实验报告

Web程序设计实验报告 姓名:冯刚 学号:200905030324 班级:计科3班

Html代码: 1.首页代码 无标题文档