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

来源:互联网 发布:中外文电子期刊数据库 编辑:程序博客网 时间:2024/05/16 10:11

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

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");//设置显示框的文本类型,使可以显示htmlJScrollPane 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 stubSystem.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;//定义一个服务SocketSocket socket  = null;//定义一个普通SocketString 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方法获取请求行中的路径//判断是请求页面还是请求servletif(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();//保存uritry{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{//如果不存在,则输出404PrintWriter 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或者是POSTint beginindex = 0;int endindex = clientrequest.indexOf(" ");String requestmethod = clientrequest.substring(beginindex, endindex);//System.out.println(requestmethod);//根据不同的请求方式调用不用的方法,默认是POSTif(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如下图:


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

总结:

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

1 0
原创粉丝点击