由于这本书的代码大多为实践性,依赖配置好JDBC、Tomcat等IDE,代码内容多为测试API本身,因此为节约时间,代码部分从书中截图并仅给出相关说明。
第 1 章
这一章主要讲Web相关的基本概念
Web是一种分布式应用架构,以互联网为媒介,旨在分享分布在网络上的各个Web服务器中所有的链接的信息(资源/服务);通信协议HTTP,信息的表现形式HTML,用户为客户端/浏览器(CS/BS);
Web的3个特征:
- 采用HTML表达信息;
- 使用URL实现资源定位;
- 使用应用层HTTP协议来规范通信过程(报文结构);
- HTML超文本标记语言,XML风格,包括:标签(<html> <head> <body> <...>)、内容(即一般的文本或资源链接,与标签符号冲突可以加转义字符)、样式(内联样式/外联CSS);
- URL统一资源定位器,URL=应用层协议(http)+IP/PORT(IP地址或者域名+端口)+资源路径(资源/服务);
- HTTP协议超文本传输协议,采用客户端(如浏览器)/服务器(Web服务器)的通信模式,基于TCP/IP协议可靠传输,,默认端口80;
- 5层协议:物理层、数据链路层、网络层、传输层、应用层;自上而下-自下而上,减小耦合,规范各自的能力,便于扩展;
HTTP过程:
(1)客户端发起-建立TCP连接(3次握手);
(2)发出HTTP请求(解析:域名、HTTP报文(头+内容));
(3)服务器发出HTTP相应(封装相应的HTTP报文);
(4)客户端/浏览器收到相应并展示(解析HTTP相应);
(5)关闭TCP连接(4次挥手);
报文格式
请求:
(1)第一行,请求方式(如POST、GET、PUT、DELETE)、URI、HTTP协议版本;
(2)中间多行,请求头HTTP HEAD,包括:浏览器信息、语言、主机、请求内容的长度、是否Keep-Alive,是否No-Cache;
(3)最后一行,请求正文,如:username=admin&password=123456;
image.png
响应
(1)第一行,HTTP版本+状态码(10X信息提示 200 OK成功相应 30X重定向跳转 40X浏览器请求有问题 50X服务器故障)
(2)中间多行,相应头RESPONSE HEAD,包括:服务器信息、正文类型编码、长度;
(3)最后一段,相应正文,一般是HTML文档;
写一个Socket模拟HTTP过程
(1) 服务端:
- 服务器SocketServer绑端口;
- while(true)监听;accept得到客户端对象;
- Service()提供服务,包括:解析HTTP请求(如getInputStream按行解析)、相应-发送(如getOutputStream写状态码+HTML内容);
- 最后关闭TCP连接;
eg:
import java.io.*;
import java.net.*;
public class HTTPServer {
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; //默认端口为8080
}
try {
serverSocket = new ServerSocket(port);
System.out.println("服务器正在监听端口:" + serverSocket.getLocalPort());
while (true) { //服务器在一个无限循环中不断接收来自客户的TCP连接请求
try {
//等待客户的TCP连接请求
final Socket socket = serverSocket.accept();
System.out.println("建立了与客户的一个新的TCP连接,该客户的地址为:" +
socket.getInetAddress() + ":" + socket.getPort());
service(socket); //响应客户请求
} catch (Exception e) {
e.printStackTrace();
}
} //#while
} 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请求的第一行
String firstLineOfRequest = request.substring(0, request.indexOf("\r\n"));
//解析HTTP请求的第一行
String[] parts = firstLineOfRequest.split(" ");
String uri = parts[1]; //获得HTTP请求中的uri
/*决定HTTP响应正文的类型*/
String contentType;
if (uri.indexOf("html") != -1 || uri.indexOf("htm") != -1)
contentType = "text/html";
else if (uri.indexOf("jpg") != -1 || uri.indexOf("jpeg") != -1)
contentType = "image/jpeg";
else if (uri.indexOf("gif") != -1)
contentType = "image/gif";
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.getBytes());
//发送HTTP响应的头
socketOut.write(responseHeader.getBytes());
//发送HTTP响应的正文
int len = 0;
byte[] buffer = new byte[128];
while ((len = in.read(buffer)) != -1)
socketOut.write(buffer, 0, len);
Thread.sleep(1000); //睡眠1秒,等待客户接收HTTP响应结果
socket.close(); //关闭TCP连接
}
}
(2) 客户端:
- 绑定目的服务器的IP+PORT,即建立连接;
- OutputStream发送HTTP请求;
- getInputStream接收/解析相应;
- 最后,关闭socket;
(以上过程可以使用IDE两个类间或者浏览器访问Tomcat上的指定IP测试,发现都可以互相通信)
import java.net.*;
import java.io.*;
import java.util.*;
public class HTTPClient {
public static void main(String args[]) {
//确定HTTP请求的uri
String uri = "index.htm";
if (args.length != 0) uri = args[0];
doGet("localhost", 8080, uri); //按照GET请求方式访问HTTPServer
}
/**
* 按照GET请求方式访问HTTPServer
*/
public static void doGet(String host, int port, String uri) {
Socket socket = null;
try {
socket = new Socket(host, port); //与HTTPServer建立FTP连接
} catch (Exception e) {
e.printStackTrace();
}
try {
/*创建HTTP请求 */
StringBuffer sb = new StringBuffer("GET " + uri + " HTTP/1.1\r\n");
sb.append("Accept: */*\r\n");
sb.append("Accept-Language: zh-cn\r\n");
sb.append("Accept-Encoding: gzip, deflate\r\n");
sb.append("User-Agent: HTTPClient\r\n");
sb.append("Host: localhost:8080\r\n");
sb.append("Connection: Keep-Alive\r\n\r\n");
/*发送HTTP请求*/
OutputStream socketOut = socket.getOutputStream(); //获得输出流
socketOut.write(sb.toString().getBytes());
Thread.sleep(2000); //睡眠2秒,等待响应结果
/*接收响应结果*/
InputStream socketIn = socket.getInputStream(); //获得输入流
int size = socketIn.available();
byte[] buffer = new byte[size];
socketIn.read(buffer);
System.out.println(new String(buffer)); //打印响应结果
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
} //#doGet()
}
由上可知,浏览器的作用:
(1)通过URL访问,发送请求(可以传入用户的输入内容);
(2)可以解析相应,并且解析HTML将其显示在网页上,并且可以调用安装的浏览器插件(如AdobeFlashPlayer或者pdf阅读器);
Tomcat的作用:
(1)可以解析HTTP请求的内容;
(2)将请求封装于一个request对象,找对应的Servlet去执行service()方法去相应请求,并且可以将相应封装于一个response对象,写入相应的内容,发送给浏览器;
HTML(动态的,如曲线、表格里的监控信息,服务器与用户交互)
即Web服务器利用特定的程序来实现动态的生成HTML文档,如使用嵌入java代码的html文档(JSP);
过程:
- 浏览器访问特定servlet;
- web服务器运行HelloServlet类(编译成HelloServlet.class);
- 返回动态的html内容;
- 编写代码模拟这一过程(P26)
(1)
import java.io.*;
import java.net.*;
import java.util.*;
public class HTTPServer1 {
private static Map servletCache = new HashMap();
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; //默认端口为8080
}
try {
serverSocket = new ServerSocket(port);
System.out.println("服务器正在监听端口:" + serverSocket.getLocalPort());
while (true) { //服务器在一个无限循环中不断接收来自客户的TCP连接请求
try {
//等待客户的TCP连接请求
final Socket socket = serverSocket.accept();
System.out.println("建立了与客户的一个新的TCP连接,该客户的地址为:" +
socket.getInetAddress() + ":" + socket.getPort());
service(socket); //响应客户请求
} catch (Exception e) {
e.printStackTrace();
}
} //#while
} 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请求的第一行
String firstLineOfRequest = request.substring(0, request.indexOf("\r\n"));
//解析HTTP请求的第一行
String[] parts = firstLineOfRequest.split(" ");
String uri = parts[1]; //获得HTTP请求中的uri
/*如果请求访问Servlet,则动态调用Servlet对象的service()方法*/
if (uri.indexOf("servlet") != -1) {
//获得Servlet的名字
String servletName = null;
if (uri.indexOf("?") != -1)
servletName = uri.substring(uri.indexOf("servlet/") + 8, uri.indexOf("?"));
else
servletName = uri.substring(uri.indexOf("servlet/") + 8, uri.length());
//尝试从Servlet缓存中获取Servlet对象
Servlet servlet = (Servlet) servletCache.get(servletName);
//如果Servlet缓存中不存在Servlet对象,就创建它,并把它存放在Servlet缓存中
if (servlet == null) {
servlet = (Servlet) Class.forName("server." + servletName).newInstance();
servlet.init();//先调用Servlet对象的init()方法
servletCache.put(servletName, servlet);
}
//调用Servlet的service()方法
servlet.service(requestBuffer, socket.getOutputStream());
Thread.sleep(1000); //睡眠1秒,等待客户接收HTTP响应结果
socket.close(); //关闭TCP连接
return;
}
/*决定HTTP响应正文的类型*/
String contentType;
if (uri.indexOf("html") != -1 || uri.indexOf("htm") != -1)
contentType = "text/html";
else if (uri.indexOf("jpg") != -1 || uri.indexOf("jpeg") != -1)
contentType = "image/jpeg";
else if (uri.indexOf("gif") != -1)
contentType = "image/gif";
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 ((len = in.read(buffer)) != -1)
socketOut.write(buffer, 0, len);
Thread.sleep(1000); //睡眠1秒,等待客户接收HTTP响应结果
socket.close(); //关闭TCP连接
}
}
(2)
import java.io.*;
public class HelloServlet implements Servlet {
public void init() throws Exception {
System.out.println("HelloServlet is inited");
}
public void service(byte[] requestBuffer, OutputStream out) throws Exception {
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;
if (method.equalsIgnoreCase("get") && uri.indexOf("username=") != -1) {
/*假定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];
}
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];
}
}
/*创建并发送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响应正文
out.write("<html><head><title>HelloWorld</title></head><body>".getBytes());
out.write(new String("<h1>Hello:" + username + "</h1></body><head>").getBytes());
}
}
Web服务器
(1)从输入内容中解析出要访问的servlet的名称+请求的内容;
(2)先从servlet缓存中找,找不到则通过ClassName反射出一个对应的servlet对象,调用servlet对象的init、service(传入request/response对象)、destroy方法;
(3)close()方法关闭;
Web服务
一个Web服务器上面有多个Web服务(靠Web服务器发布),一个网络中可能有多个Web服务器,Web、服务可以看成可以被客户端远程访问的各种方法(业务逻辑、复杂运算、资源数据等);
(图P31 1-19)
image.png
image.png
HTTP的请求参数(如输入的username=admin)
对于GET请求方式,发送的HTTP报文中,放在HTTP的第一行URI里面;
(P35图)
对应的HTML中的输入框(method=”GET” action=”servlet/HelloServlet”)
(P34图)
image.png
对于POST方法,请求参数放在HTTP正文部分(相对安全);
(P36图)
image.png