分布式计算——实现简单的浏览器和web服务器

此次是分布式的第三次作业,作业要求如下:

1、基于TCP通讯(ServerSocket、Socket套接字),编写一个带有图形用户界面的浏览器和

一个支持文档读取并返回给客户浏览器的web服务器。客户端支持超链接事件处理,服务器采用多

线程技术支持并发访问。

2、在此基础上,修改服务器侧设计与实现,使之能够动态地添加客户端请求的类文件,即设计

一个小服务程序容器。

3、试在服务器侧代码中对客户端请求行、请求头和请求体部分进行处理。

====================================================================分割线

没有完成第3个要求。


客户端有两个类,一个是主类Client.java,
一个是浏览器类Browser.java。
Client.java代码如下:
package com.client;

import java.io.IOException;

public class Client {

	public static  String request;
	public static void main(String[] args) throws IOException{
			
		Browser browser = new Browser();	//生成浏览器对象
		browser.setBrowser();				//调用setBrowser()方法初始化浏览器
	}
}

Browser.java的代码如下:
package com.client;

import java.awt.Button;
import java.awt.Frame;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.*;
import java.net.Socket;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;

@SuppressWarnings("serial")
public class Browser extends Frame implements HyperlinkListener {

	JEditorPane jEditorPane = new JEditorPane();//浏览器显示框
	String request = null;	//定义字符串保存用户输入的请求
	
	//初始化浏览器框
	public void setBrowser() throws IOException {

		JFrame frame = new JFrame("浏览器");
		frame.setSize(800, 600);
		frame.setLocation(100, 50);
		frame.setLayout(null);
		frame.setResizable(false);

		jEditorPane.setEditable(false);
		jEditorPane.setContentType("text/html");//设置显示框的文本类型,使可以显示html
		JScrollPane jScrollPane = new JScrollPane(jEditorPane);//加一个滑动框
		jScrollPane.setSize(200, 200);
		jScrollPane.setLocation(100, 100);
		jScrollPane.setBounds(50, 100, 700, 450);
		jEditorPane.addHyperlinkListener(this);//添加超链接监听
		
		TextField textField = new TextField(50);
		textField.setBounds(200, 50, 320, 30);
		textField.setText("http://127.0.0.1:8080/");//初始化的时候添加访问地址和端口,不用重复输入
		Button button = new Button("前往");
		button.setBounds(550, 50, 70, 30);

		frame.add(textField);
		frame.add(button);
		frame.add(jScrollPane);

		myActionListener myActionListener = new myActionListener(textField);
		button.addActionListener(myActionListener);//在button上添加监听

		//给主frame上添加关闭按钮的功能
		frame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				// TODO Auto-generated method stub
				System.exit(0);
			}
		});
		
		frame.setVisible(true);//设置可见
	}
	
	//处理文JEditPane中的超链接
	public void hyperlinkUpdate(HyperlinkEvent e){
		try{
			if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED){
				jEditorPane.setPage(e.getURL());	//setPage方法可以直接获取连接内容
			}
		} catch (IOException e1) {
			
			e1.printStackTrace();
		}
	}
	
	//自定义监听类实现ActionListener接口
	class myActionListener implements ActionListener{

		TextField textField = new TextField();
		//将textField对象传进来,用以取用户的输入内容
		public myActionListener(TextField textField) {
			
			this.textField = textField;//获取到用户请求的url地址
		}
		
		//actionPerformed方法用以处理请求
		public void actionPerformed(ActionEvent e){
		
			//简单处理请求的url,将其处理成http请求行的样子 
			String url = textField.getText();
			
			url.trim();			//去掉空格
			int beginindex = url.indexOf("//");
			url = url.substring(beginindex+2, url.length());
			beginindex = url.indexOf("/");
			url = url.substring(beginindex, url.length());
			request = "POST " + url + " HTTP/1.0\\r\\n";	//设置请求
			
			try {
				getConnect();	//连接服务器
			} catch (IOException e1) {
				
				e1.printStackTrace();
			}
		}
	}
	
	public void getConnect() throws IOException{
		
		Socket socket = new Socket("localhost", 8080);	//new socket对象访问服务器
		socket.setSoTimeout(10000);						//设置访问超时的时限,此处设置为10秒
		
		//获取一个输入流和一个输出流,并将两个流都包装起来
		OutputStream outputStream = socket.getOutputStream();
		PrintWriter socketOutput = new PrintWriter(new OutputStreamWriter(outputStream));
		InputStream inputStream = socket.getInputStream();
		BufferedReader socketInput = new BufferedReader(new InputStreamReader(inputStream));
		
		//将处理后的用户请求写入输出流,此时的请求格式为http的请求行的格式
		socketOutput.println(request);//将用户请求放入输出流中,传送给服务器
		socketOutput.flush();
		//----以上是处理发送请求到服务器的操作
		
		//----以下是处理服务器反馈的结果
		String responseline = new String();	//保存每次从流中取出的信息
		String response = new String();		//保存服务器返回的所有信息
		
		responseline = socketInput.readLine();	//从流中读取一行内容
        while(responseline != null){
            System.out.println(responseline);
            response = response.concat(responseline);
            responseline = socketInput.readLine();
        }
        
        jEditorPane.setText(response);	//将服务器返回的内容写入浏览器中
        jEditorPane.repaint();			//重画一下浏览器的显示部分
	}
}

客户端运行起来的样子会在最后演示。

--------------------------------------------------------------------------------------小分割线

服务器端写了五个类,分别是:
Server.java(主类)
MyServer.java(多线程服务器实现类)
Request.java(包装请求类)
Response.java(包装回复类)
ServletCon.java(ServletCon是servlet容器的意思。)

具体代码如下:

Server.java(主类):
package com.server;

import java.io.IOException;

public class Server {

	public static void main(String[] args) throws IOException{
		
		MyServer myserver = new MyServer(); //生成服务器对象
	}
}

MyServer.java(多线程服务器实现类):
package com.server;

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;

import javax.servlet.ServletException;

//此类实现Runnable接口,以实现多线程功能
public class MyServer implements  Runnable  {
	
	int port = 8080;					//定义监听端口
	ServerSocket serverSocket = null;	//定义一个服务Socket
	Socket socket  = null;				//定义一个普通Socket
	String requestline = null;			//定义一个字符串保存用户的请求行
	
	public MyServer() throws IOException{
	
		serverSocket = new ServerSocket(8080);		//监听端口8080,参数为端口号
		System.out.println("Server服务已经启动。。。。");
		
		//while死循环监听请求,用线程去处理请求
		while(true){
			socket = serverSocket.accept();
			Thread thread = new Thread(this);	//将当前对象作为线程参数
			thread.start();						//启动线程,执行run()方法
		}
	}
	
	//重写run()方法
	public void run(){
		
		//定义一个输出流和一个输入流
		InputStream inputStream = null;
		OutputStream outputStream = null;
		
		try {
			inputStream = socket.getInputStream();		//获取输入流
			outputStream = socket.getOutputStream();	//获取输出流
			
			//通过Request的构造方法,将输入流传入,然后通过getUri就可以获取用户的请求,并会返回处理后的请求
			Request clientrequset = new Request(inputStream);	//生成Request对象,将此流传入
			requestline = clientrequset.getUri();		//调用getUri方法获取请求行中的路径
			
			//判断是请求页面还是请求servlet
			if(requestline.startsWith("servlets/")){
				
				//如果是servlets请求,先将输入流传给Response对象
				Response responseservlet = new Response(outputStream);
				//此处生成servlet容器对象,用来处理servlet程序,需要将请求的内容传给servlet容器
				ServletCon servletCon = new ServletCon(requestline);
				
				try {
					//通过processServletRequest()方法处理用户请求,需要一个Request对象,和一个Response对象
					servletCon.processServletRequest(clientrequset, responseservlet);
				} catch (ServletException e) {
					
					e.printStackTrace();
				}
				System.out.println("处理servlet");
			}
			else{
				
				//如果是请求页面的话,将请求的内容通过setRequest()方法传给Response对象
				Response responsehtml = new Response(outputStream);//生成一个Response对象,并将输出流传入
				responsehtml.setRequest(requestline);	//将请求对象传入
				responsehtml.sendStaticResource();		//通过sendStaticResourct()方法将请求的页面返回给客户端
				System.out.println("处理html");
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

Request.java(包装请求类),因为此类实现了ServletRequest接口,实现的一些方法没有用,
所以只给出需要用到的方法和扩展的方法:
package com.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;


public class Request implements ServletRequest{

	InputStream inputStream = null;		//定义一个输入流,用来接收socket输入流的引用
	public static String clientrequest = new String();	//保存用户请求行
	
	public  Request(InputStream input) {
		this.inputStream = input;	//获取输入流
	}
	
	//获取用户请求行中的uri标识
	public String getUri(){
		
		String requestString = new String();	//保存uri
		
		try{
			BufferedReader socketInput = new BufferedReader(new InputStreamReader(inputStream));
			
			requestString = socketInput.readLine();//读取用户请求行,保存在requestString中
			clientrequest = requestString;	//此处需要赋值一次,因为在Servlet程序中要用到
			requestString = this.parseUri(requestString);	//调用parseUri处理用户请求行
			
		}catch(Exception e){
			e.printStackTrace();
		}

		return requestString;	//将uri返回,用作比较
	}
	
	//处理用户请求行
	private String parseUri(String requestString){
		
		int beginindex = requestString.indexOf(" ");
		int endindex = requestString.lastIndexOf(" ");
		//因为请求行中的资源标识符在两个空格之间,所以取到两个空格的索引
		
		//取出资源定位符
		requestString = requestString.substring(beginindex+2, endindex);
		
		return requestString;	//将定位符返回
	}
	
	...
	
}

Response.java(包装回复类),此类同上,实现了ServletResponse接口,有些没用到的方法
就没有贴出来:
package com.server;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Locale;

import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Response implements ServletResponse{

	String requestline;			//定义请求行字符串,用来保存用户请求
	OutputStream output = null;	//定义一个输出流
	
	//通过构造方法 ,获取到输出流
	public Response(OutputStream output){
		
		this.output = output;
	}
	
	//处理客户的静态页面请求
	public void sendStaticResource() throws IOException{
		
		File responsehtml = new File(requestline);	//生成用户请求页面对象
		
		//判断文件是否存在
		if(responsehtml.exists()){
			//存在的话,读取文件中的内容,并放到输出流中
			FileReader fileReader = new FileReader(responsehtml);
			PrintWriter socketOutput = this.getWriter();
			
			//读取文件内容,并写入输出流
			int i;
			while((i = fileReader.read()) != -1){
				socketOutput.print((char)i);	//转换成char型	
			}
			
			socketOutput.close();	//关闭输出流
		}else{
			//如果不存在,则输出404
			PrintWriter socketOutput = this.getWriter();
			socketOutput.println("404 NotFound");
			socketOutput.close();	//关闭输出流
		}
	}
	
	//获取请求行
	public void setRequest(String requestline){
		
		this.requestline = requestline;//获取到Request对象
	}
	
	//重写此方法
	public PrintWriter getWriter() throws IOException {
	
		return new PrintWriter(new OutputStreamWriter(output));
	}
	
	...
}

ServletCon.java(ServletCon是servlet容器的意思。):
package com.server;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ServletCon {

	ServletRequest request;
	ServletResponse response;
	String requestline;
	
	//通过构造方法获取到用户请求的内容
	public ServletCon(String requestline) {
		
		this.requestline = requestline;
	}
	
	//处理servlet请求
	public void processServletRequest(Request request, Response response) throws ServletException, IOException{
		
		this.request = request;		//获取到Request对象
		this.response = response;	//获取到Response对象
		
		String servletname = getServletName(requestline);	//通过getServletName()方法获取到用户请求的类名
		
		Servlet servlet = loadServlet(servletname);			//通过类名获取到对象的servlet程序
		servlet.service(request, response);					//执行servlet的service方法
	}
	
	//通过servletname导入servlet程序
	private Servlet loadServlet(String servletname) throws ServletException, IOException {
		
		//此处的URL要定义到包名之外,不能连报名定义在一块
		URLClassLoader loader = new URLClassLoader(new URL[] { new URL("file:E:\\Eclipesworkspace\\fenbushi3\\bin") });  
        Servlet servlet = null;
        
        try {  	
        	//此处注意在获取类文件的时候需要写全路径,也就是包含包名
            Class servletClass = loader.loadClass("com.servlets."+servletname);  
            servlet = (Servlet) servletClass.newInstance();	//实例化一个对象

        } catch (ClassNotFoundException e) {  
            e.printStackTrace();  
        } catch (InstantiationException e) {  
            e.printStackTrace();  
        } catch (IllegalAccessException e) {  
            e.printStackTrace();  
        }  
        return servlet;  
		
	}

	//通过用户的请求行获取到请求的内容
	public String getServletName(String requestline){
		
		int beginindex = requestline.indexOf("/");
		int endindex = requestline.length();
		
		String servletname = requestline.substring(beginindex+1, endindex);
		
		return servletname;
	}
	
}

------------------------------------------------------------------------------------------------------小分割线

以上内容就是服务器端和客户端的代码,接下来还有一个用户请求的html页面的代码
和一个servlet程序代码。

html页面代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<center>
		<h1>this is a html page</h1>
	</center>
</body>
</html>

servlet程序代码:
package com.servlets;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.server.Request;
import com.server.Response;

public class MyServlet extends HttpServlet{

	//重写service方法
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		
		Request request = (Request)req;		//强制转化成子类对象

		String clientrequest = request.clientrequest;	//获取到完整请求行
		
		//以下是获取到用户请求方式GET或者是POST
		int beginindex = 0;
		int endindex = clientrequest.indexOf(" ");
		String requestmethod = clientrequest.substring(beginindex, endindex);
		//System.out.println(requestmethod);
		
		//根据不同的请求方式调用不用的方法,默认是POST
		if(requestmethod.equals("POST")){
			doPost(req, res);
		}else if(requestmethod.equals("GET")){
			doGet(req, res);
		}

	}

	//重写doPost方法
	protected void doPost(ServletRequest req, ServletResponse res) throws ServletException, IOException {

		PrintWriter socketOutput = res.getWriter();	//获取到输出流
		
		//输出一句HelloWorld到客户端
		socketOutput.println("<h1>");
		socketOutput.println("HelloWorld, I am servlet");
		socketOutput.println("</h1>");
		
		socketOutput.flush();	//flush一下
		socketOutput.close();	//关闭输出流
	}

	protected void doGet(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		
	}

}


====================================================================分割线

以上就是所有的代码,下面对运行的结果进行演示。

代码结构如下图:


启动服务器如下图:


客户端请求html页面和请求不存在的html页面如下图:
存在:

不存在:


请求servlet如下图:


====================================================================分割线

总结:

对于整个处理过程还没有完全吃透,还准备再看看,等差不多完全明白后,再来整理
一篇博文。这里面涉及到的知识点还会再回来整理,时间不多了,马上断电了,得赶紧保
存了。
对了,还没有测试多线程和网络中访问的情况,现在只是在本地可以执行。

发布了38 篇原创文章 · 获赞 35 · 访问量 8万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览