实验八 应用层网络编程(二)
浙江大学城市学院实验报告
课程名称计算机网络应用
实验项目名称实验八应用层网络编程(二)
实验成绩指导老师(签名)日期2014-06-10
一. 实验目的和要求
1.通过实现使用Java应用层客户端和服务器来获得关于使用Java Socket
网络编程的经验(HTTP)。
二. 实验内容、原理及实验结果与分析
1. HTTP编程(参考电子讲义“网络编程参考资料-应用层.pdf”及教材“第
2章Socket编程”)
阅读“网络编程参考资料-应用层.pdf”中8.3.3部分,实现“HTTP/1.0服务器实现”的源代码(WebServerDemo.java),并在机器上编译运行通过。
设置WEB服务器的发布目录,编写测试网页,并设置端口号,然后通过在命令行中输入相应命令(如“java WebServerDemo d:\src 8001”)启动服务器,在浏览器中进行测试。
【程序源代码】
WebServerDemo.java
import java.io.*;
import https://www.360docs.net/doc/976375558.html,.*;
import java.util.*;
public class WebServerDemo {
// Directory of HTML pages and other files
private String docroot;
// Port number of web server
private int port;
// Socket for the web server
private ServerSocket ss;
// Handler for a HTTP request
// You do not have to make Handler as an inner class.
class Handler extends Thread {
private Socket socket;
private PrintWriter pw;
private BufferedOutputStream bos;
private BufferedReader br;
private File docroot;
public Handler(Socket _socket, String _docroot) throws Exception {
socket=_socket;
// Get the absolute directory of the filepath
docroot=new File(_docroot).getCanonicalFile();
}
public void run() {
try {
// Prepare our readers and writers
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
bos = new BufferedOutputStream(socket.getOutputStream());
pw = new PrintWriter(new OutputStreamWriter(bos));
// Read HTTP request from user (hopefully GET /file...... )
String line = br.readLine();
// Places the input stream for this socket at "end of stream". Any
// data sent to the input stream side of the socket is acknowledged
// and then silently discarded. If you read from a socket input stream
// after invoking shutdownInput() on the socket, the stream will
// return EOF.
socket.shutdownInput();
if(line == null) {
socket.close();
return;
}
// let's take a look at what the server received
System.out.println(line);
if(line.toUpperCase().startsWith("GET")) {
// Eliminate any trailing ? data, such as for a CGI GET request
StringTokenizer tokens = new StringTokenizer(line," ?");
tokens.nextToken();
String req = tokens.nextToken();
// If a path character / or \ is not present, add it to the // document root
// and then add the file request, to form a full filename
String name;
if(req.startsWith("/") || req.startsWith("\\"))
name = docroot + req;
else
name = docroot + File.separator + req;
// Get absolute file path
File file = new File(name).getCanonicalFile();
// Check to see if request doesn't start with our document root ....
if(!file.getAbsolutePath().startsWith(docroot.getAbsolutePath())) {
pw.println("HTTP/1.0 403 Forbidden");
pw.println();
}
// ... if it's missing .....
else if(!file.exists()) {
pw.println("HTTP/1.0 404 File Not Found");
pw.println();
}
// ... if it can't be read for security reasons ....
else if(!file.canRead()) {
pw.println("HTTP/1.0 403 Forbidden");
pw.println();
}
// ... if its actually a directory, and not a file ....
else if(file.isDirectory()) {
sendDir(bos,pw,file,req);
}
// ... or if it's really a file
else {
sendFile(bos, pw, file.getAbsolutePath());
}
}
// If not a GET request, the server will not support it
else {
pw.println("HTTP/1.0 501 Not Implemented");
}
pw.flush();
bos.flush();
} catch(Exception e) {
e.printStackTrace();
}
try {
socket.close();
} catch(Exception e) {
e.printStackTrace();
}
}
protected void sendFile(BufferedOutputStream bos, PrintWriter pw,
String filename) throws Exception { try {
BufferedInputStream bis = new BufferedInputStream(new
FileInputStream(filename));
byte[] data = new byte[10*1024];
int read = bis.read(data);
pw.println("HTTP/1.0 200 Okay");
pw.println();
pw.flush();
bos.flush();
while(read != -1) {
bos.write(data,0,read);
read = bis.read(data);
}
bos.flush();
} catch(Exception e) {
pw.flush();
bos.flush();
}
}
protected void sendDir(BufferedOutputStream bos, PrintWriter pw,
File dir, String req) throws Exception { try {
pw.println("HTTP/1.0 200 Okay");
pw.flush();
pw.print("
pw.print(req);
pw.print("
Directory of ");
pw.print(req);
pw.println("
pw.print(req); pw.print(contents[i].getName()); if(contents[i].isDirectory()) pw.print("/"); pw.print("\">"); if(contents[i].isDirectory()) pw.print("Dir -> "); pw.print(contents[i].getName()); pw.print(" | ");
pw.flush();
} catch(Exception e) {
pw.flush();
bos.flush();
}
}
}
// Check that a filepath has been specified and a port number
protected void parseParams(String[] args) throws Exception {
switch(args.length) {
case 1:
case 0:
System.err.println ("Syntax:
" docroot port");
System.exit(0);
default:
this.docroot = args[0];
this.port = Integer.parseInt(args[1]);
break;
}
}
public WebServerDemo(String[] args) throws Exception {
System.out.println ("Checking for paramters");
// Check for command line parameters
parseParams(args);
System.out.print ("Starting web server...... ");
// Create a new server socket
ss = new ServerSocket(port);
System.out.println ("OK");
for (;;) {
// Accept a new socket connection from our server socket
Socket accept = ss.accept();
// Start a new handler instance to process the request
new Handler(accept, docroot).start();
}
}
// Start an instance of the web server running
public static void main(String[] args) throws Exception {
WebServerDemo webServerDemo = new WebServerDemo(args); }
}
【实验结果与分析】
2. Ethereal抓包分析
用Ethereal软件截获上面程序运行时客户端和服务器之间发送的数据包,并且根据截获的数据包内容进行分析。
【实验结果与分析】
host 10.66.19.27 and tcp port 8001
TCP数据包结构
下面的表格的值取自图中选中的数据包:
前32位
三次握手的数据包
10.66.19.35 -> 10.66.19.27 [SYN](Seq=0);
10.66.19.27 -> 10.66.19.35 [SYN,ACK](Seq=0,Ack=1);
10.66.19.35 -> 10.66.19.27 [ACK](Seq=1,Ack=1);
三. 讨论、心得
记录实验感受、上机过程中遇到的困难及解决办法、遗留的问题、意见和建议等。