servlet-服务器与客户端的动态交互

概述

WEB服务器动态执行的程序可分为两种方式。

  • 第一种方式:完全用编程语言编写的程序,例如CGI(Commond Gateway Interface)程序和Java编写的Servlet程序。
  • 第二种方式:嵌入了程序代码的HTML文档,例如PHP、ASP和JSP文档。JSP文档是指嵌入了Java代码的HTML文档。

  在此,以Servlet为例,介绍Web服务器动态执行完全用编程语言编写的程序的原理。
值得注意的是:Web服务器动态执行特定程序代码,其特征是Web服务器在运行时加载并执行由第三方提供测程序代码。所谓Web服务器动态生成HTML文档,就是指Web服务器在运行时才通过执行特定程序代码来生成HTML文档,而不是直接从文件系统中获取已经存在的HTML文档。
  执行过程如下图所示,在服务器端存放一个使用Java语言编写的程序.class文件:HelloServlet.class。当用户在浏览器端输入指向该类的URL时,Web服务器就会运行HelloServlet类,HelloServlet类生成HTML文档,并把它发送给浏览器。
在这里插入图片描述

HTTPServer1-服务器程序主类

  HTTPServer1自生的业务逻辑规定:如果用户请求URI位于servlet子目录下,就按照Servlet来处理,否则,就按照普通的静态文件来处理。当客户请求访问特定的Servlet时,HTTPServer1先从自己的servletCache缓存中寻找特定的Servlet实例,把它放入servletCache缓存中,再调用它的service()方法。

package server;
import java.io.*;
import java.net.*;
import java.util.*;

public class HttpServer1 {
private static Map<String, Servlet> servletCache = new HashMap<String, Servlet>();

public static void main(String args[])
{
	int port;
	ServerSocket serverSocket;
	
	try {
		port = Integer.parseInt(args[0]);
	}catch(Exception e) {
		System.out.println("port = 8080(默认)");
		port = 8080;
	}
	
	try {
		serverSocket = new ServerSocket(port);
		System.out.println("服务器监听端口:"+serverSocket.getLocalPort());
		
		//服务器在一个无限循环中不断接收来自客户端的TCP连接请求
		while(true)
		{
			try {
				//等待客户端的TCP请求连接
				final Socket socket = serverSocket.accept();
				System.out.println("建立了与客户的一个新的TCP连接,该客户的地址为:"+socket.getInetAddress()+":"+socket.getPort());
				
				//响应客户请求
				service(socket);
			}catch(Exception e){
				System.out.println("客户端请求的资源不存在");
			}
		}
	}catch(Exception e) { e.printStackTrace();}
}

//响应客户端的HTTP请求
public static void service(Socket socket)throws Exception{
	//读取HTTP请求信息
	//获取输入流
	InputStream socketIn = socket.getInputStream();
	Thread.sleep(500);//睡眠500毫秒等待HTTP请求
	int size = socketIn.available();
	byte[] requestBuffer = new byte[size];
	socketIn.read(requestBuffer);
	String request = new String(requestBuffer);
	System.out.println(request); //打印HTTP请求
	
	//解析HTTP请求
	//获取HTTP请求的第一行
	int endIndex = request.indexOf("\r\n");
	if(-1 == endIndex)
		endIndex = request.length();
	String firstLineOfRequest = request.substring(0, endIndex);
	//解析HTTP请求的第一行
	String[] parts = firstLineOfRequest.split(" ");
	String uri = "";
	if(2 <= parts.length)
	{
		//获取HTTP请求中的uri
		uri = parts[1];
	}
	
	//如果请求访问Servelet,则动态调用Servlet对像的service()方法
	if(-1 != uri.indexOf("servlet"))
	{
		//获取Servlet的名字
		String servletName = null;
		if(-1 != uri.indexOf("?"))
			servletName = uri.substring(uri.indexOf("servlet/")+8, uri.indexOf("?"));
		else
			servletName = uri.substring(uri.indexOf("servlet/")+8, uri.length());
		
		//尝试从servlet缓存中获取Servlet对像
		Servlet servlet = servletCache.get(servletName);
		//如果Servlet缓存中不存在Servlet对像,则创建它,并把他放在Servlet缓存中
		if(null == servlet) {
			servlet = (Servlet)Class.forName("server."+servletName).getDeclaredConstructor().newInstance();
			servlet.init(); //先调用Servlet对像init方法
			servletCache.put(servletName, servlet);
		}
		
		//调用Servlet的service方法
		servlet.service(requestBuffer, socket.getOutputStream());
		
		//睡眠1秒,等待客户端接受HTTP响应结果
		Thread.sleep(1000);
		socket.close();
		return;
	}
	
	//决定HTTP响应正文的类型
	String contentType;
	if(-1 != uri.indexOf("html") || -1 != uri.indexOf("htm"))
		contentType = "text/html";
	else if(-1 != uri.indexOf("jpg") || -1 != uri.indexOf("jpeg"))
		contentType = "image/jpeg";
	else 
		contentType = "application/octet-stream";
	
	//创建HTTP响应结果
	//HTTP响应的第一行
	String responseFirstLine = "HTTP/1.1 200 ok\r\n";
	//HTTP响应头
	String responseHeader = "Content-Type:"+contentType+"\r\n\r\n";
	//获得读取响应正文数据的输入流
	InputStream in = HttpServer1.class.getResourceAsStream("root/"+uri);
	
	//发送HTTP响应结果
	OutputStream socketOut = socket.getOutputStream();//获得输出流
	//发送HTTP响应的第一行
	socketOut.write(responseFirstLine.toString().getBytes());
	//发送HTTP的响应头
	socketOut.write(responseHeader.toString().getBytes());
	//发送HTTP响应额正文
	int len = 0;
	byte[] buffer = new byte[128];
	while(-1 != (len = in.read(buffer)))
	{
		socketOut.write(buffer, 0, len);
	}
	//睡眠1秒,等待客户端接收HTTP响应结果
	Thread.sleep(1000);
	//关闭TCP连接
	socket.close();
	}
}

Servelet-服务程序接口

Servlet接口有一个init()方法和一个service()方法:

  • init()方法:为初始化方法,当HTTPServlet1创建了实现该接口的类的一个实例后,就会立即调用该实例的init()方法。
  • service()方法:用于响应HTTP请求,产生具体的HTTP响应结果。HTTPServer1服务器在响应HTTP请求时会调用实现了Servlet接口的特定类的service()方法。
package server;
import java.io.*;

public interface Servlet {
	public void init()throws Exception;
	
	public void service(byte[] requestBuffer, OutputStream out)throws Exception;
}

HelloServlet-Servlet接口具体实现

  HelloServlet类实现了Servlet接口。HelloServlet类的service()方法能解析HTTP请求中的请求参数,根据请求参数username的取值来生成HTML文档。

package server;

import java.io.OutputStream;

public class HelloServlet implements Servlet {

	@Override
	public void init() throws Exception {
		// TODO 自动生成的方法存根
		System.out.println("HelloServlet is inited");
	}

	@Override
	public void service(byte[] requestBuffer, OutputStream out) throws Exception {
		// TODO 自动生成的方法存根
		String request = new String(requestBuffer);
		
		//获得HTTP请求的第一行
		String firstLineOfRequest = request.substring(0, request.indexOf("\r\n"));
		//解析HTTP请求第一行
		String[] parts = firstLineOfRequest.split(" ");
		String method = parts[0]; //获取HTTP请求中的请求方式
		String uri = parts[1]; //获取HTTP请求中的uri
		
		//获得请求参数username
		String username = null;
		//如果请求方式为GET,则请求参数紧跟HTTP请求的第一行的uri的后面
		if(method.equalsIgnoreCase("get") && -1 != uri.indexOf("username"))
		{
			/* 假定uri="servlet/HelloServlet?username=Tom&password=1234" */
			//parameters"username=Tom&password=1234"
			String parameters = uri.substring(uri.indexOf("?"), uri.length());
			//parts={"username=Tom","password=1234"}
			parts = parameters.split("&");
			//parts={"username","Tom"}
			parts = parts[0].split("=");
			username = parts[1];
		}
	    //如果请求方式为“POST”,则请求参数位于HTTP请求的请求正文中。
		else if(method.equalsIgnoreCase("post")){
		  int locate=request.indexOf("\r\n\r\n");
		  //获得响应正文 
		  String content=request.substring(locate+4,request.length());
		  if(content.indexOf("username=")!=-1){
			/*假定content="username=Tom&password=1234"*/
			//parts={"username=Tom","password=1234"};
			parts=content.split("&");
			//parts={"username","Tom"};
			parts=parts[0].split("=");
		    username=parts[1];   
		  }
	    }
		else
		{
			System.out.println("查找方法失败!!");
		}
		//创建并发送HTTP响应
		//发送HTTP响应第一行
		out.write("HTTP/1.1 200 ok\r\n".getBytes());
		//发送HTTP响应头
		out.write("Content-Type:text/html\r\n\r\n".getBytes());
		//发送HTTP响应正文
		String content = "<html><head><title>hello world</title></head><body>";
		content += "<h1>Hello:" + username + "</h1><body><head>";
		out.write(content.getBytes());
	}
}

运行结果

  先运行服务器程序,再通过浏览器输入URL:
http://localhost:8080/servlet/HelloServlet?username=georgeHotz
以上URL中?后面的就是请求参数,参数名为username,参数值为georgeHotz。对于此URL浏览器接受的HTML页面如下图:
在这里插入图片描述
  当username参数值改变时,通过执行结果可知,返回结果也会跟着改变。尽管浏览器访问的都是HelloServlet,但是服务器端会根据请求参数的不同值来生成不同的HTML文档,这体现了服务器端动态生成网页的功能,而且也体现了服务器端与用户的交互功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值