什么是HTTP协议?
HTTP是hypertext transfer protocol(超文本传输协议)的简写,它是TCP/IP协议的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的过程。客户端连上WEB服务器后,若想获得WEB服务器中的某个WEB资源,需遵守一定的通讯格式,HTTP协议用于定义客户端与WEB服务器通迅的格式。
HTTP协议是学习JavaWeb开发的基石,不深入了解HTTP协议,就不能说掌握了WEB开发,更无法管理和维护一些复杂的WEB站点。
HTTP协议的版本
HTTP协议的版本有HTTP/1.0和HTTP/1.1。
HTTP1.0和HTTP1.1的区别
在HTTP1.0协议中,客户端与WEB服务器建立连接后,只能获得一个WEB资源;而HTTP1.1协议,允许客户端与WEB服务器建立连接后,在一个连接上获取多个WEB资源。
例如,有这样一个问题:一个web页面中,使用img标签引用了三幅图片,当客户端访问服务器中的这个web页面时,客户端总共会访问几次服务器,即向服务器发送了几次HTTP请求?服务器中的web页面为index.html,内容如下:
aaaaaaaaaaaaaaaaaaa
<img src="1.jpg" />
<img src="2.jpg" />
<img src="3.jpg" />
此时通过Chrome浏览器访问服务器中的index.html。
可发现客户端向服务器发送了4次HTTP请求,第一次获取到整个页面资源。第二次浏览器向服务器要1.jpg,然后依次向服务器要2.jpg、3.jpg。为了减少客户端向浏览器发送的请求数,减少服务器的压力,通常会利用css技术将多张图片归并为一张图片,请求时,只打开这张图片的一小部分。
HTTP请求
HTTP请求包括的内容
客户端连上服务器后,向服务器请求某个WEB资源,称之为客户端向服务器发送了一个HTTP请求。
例如,
WEB服务器通过客户端发送过来的这些请求信息,就可以确定向请求者回送什么资源,以及根据客户端的环境信息采用什么方式进行回送等。
HTTP请求的细节——请求行
请求行中的GET称之为请求方式,请求方式有以下几种:
不管POST或GET,都用于向服务器请求某个WEB资源,这两种方式的区别主要表现在数据传递上。
HTTP请求的细节——请求头
例如:
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, */*
Referer: http://localhost:8080/examples/servlets/servlet/RequestParamExample
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: localhost:8080
Content-Length: 31
Connection: Keep-Alive
Cache-Control: no-cache
HTTP响应
HTTP响应包括的内容
一个HTTP响应代表服务器向客户端回送的数据。
例如,
在一个HTTP响应中,WEB服务器通过响应头向WEB客户端描述客户端的请求成功与否,以及它所发送的数据类型等一些信息,客户端通过这些信息,进而可以知道如何对数据进行处理。
HTTP响应的细节——状态行
状态行格式为HTTP版本号 状态码 原因叙述<CRLF>
。例,HTTP/1.1 200 OK
。状态码用于表示服务器对请求的处理结果,它是一个三位的十进制数。响应状态码分为5类,如下表所示:
这里还是细说几个常见的状态码。
HTTP响应细节——常用响应头
在服务端设置响应头来控制客户端浏览器的行为
新建一个动态web项目——day04,该项目的结构图如下:
设置Location响应头,实现请求重定向
当在Chrome浏览器中使用URL地址http://localhost:8080/day04/ServletDemo1
访问ServletDemo1时,就可以看到服务器作出响应后发送到浏览器的状态码和响应头信息,如下图所示:
服务器返回一个302状态码告诉浏览器,你要的资源我没有,但是我通过location响应头告诉你哪里有,而浏览器解析响应头location后知道要跳转到/day04/1.html页面,所以就会自动跳转到1.html,如下图所示:
设置Content-Encoding响应头,告诉浏览器数据的压缩格式
怎么实现数据的压缩?那就需要使用GZIPOutputStream流来压缩数据了。
package cn.liayun.web.servlet;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/ServletDemo1")
public class ServletDemo1 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
test2(response);
}
// 压缩数据并输出
private void test2(HttpServletResponse response) throws IOException {
String data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
System.out.println("原始数据大小:" + data.getBytes().length);
ByteArrayOutputStream bout = new ByteArrayOutputStream();// 内存里面的一个字节数组
GZIPOutputStream gout = new GZIPOutputStream(bout);
gout.write(data.getBytes());
gout.close();
byte[] gzip = bout.toByteArray();// 得到压缩后的数据
System.out.println("压缩后的大小:" + gzip.length);
// 通知浏览器,数据采用了压缩格式
response.setHeader("Content-Encoding", "gzip");
response.setHeader("Content-Length", gzip.length + "");
response.getOutputStream().write(gzip);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
服务器发给浏览器的响应信息如下:
此时Eclipse控制台打印如下信息:
设置content-type响应头,控制浏览器以哪种方式处理数据
package cn.liayun.web.servlet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/ServletDemo1")
public class ServletDemo1 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
test3(response);
}
// 通过content-type头字段,控制浏览器以那种方式处理数据
private void test3(HttpServletResponse response) throws IOException {
response.setHeader("content-type", "image/jpeg");
InputStream in = this.getServletContext().getResourceAsStream("/1.jpeg");
int len = 0;
byte[] buffer = new byte[1024];
OutputStream out = response.getOutputStream();
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
提示:Content-Type头字段对应的值可通过Tomcat服务器下conf/web.xml文件查找。
服务器发给浏览器的响应信息和ServletDemo1的运行结果(在浏览器中显示出了图片)如下图所示:
设置refresh响应头,让浏览器定时刷新
package cn.liayun.web.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/ServletDemo1")
public class ServletDemo1 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
test4(response);
}
// 定时刷新
private void test4(HttpServletResponse response) throws IOException {
response.setHeader("refresh", "3;url='http://www.sina.com.cn'");
String data = "aaaaaaaaaa";
response.getOutputStream().write(data.getBytes());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
浏览器隔了3秒钟请求一次后,将会刷新到新浪上去。
设置content-disposition响应头,让浏览器下载文件
package cn.liayun.web.servlet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/ServletDemo1")
public class ServletDemo1 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
test5(response);
}
private void test5(HttpServletResponse response) throws IOException {
response.setHeader("content-disposition", "attachment;filename=2.jpg");
InputStream in = this.getServletContext().getResourceAsStream("/2.jpg");
int len = 0;
byte[] buffer = new byte[1024];
OutputStream out = response.getOutputStream();
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
在Chrome浏览器中访问ServletDemo1就会弹出文件下载框,如下图所示:
点击在文件夹中显示,可以发现服务器端的2.jpg文件已经被下载到了本机中。
HTTP的一个实用头字段——Range
HTTP请求头字段
Range头指示服务器只传输一部分WEB资源。这个头可以用来实现断点续传功能。
例如,WEB服务器有一个资源,比如是a.txt,内容如下:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
有一个用户下载了一部分数据,并存储到D盘中的a.txt中,内容为:
aaaaa
此时该用户想接着下载完剩下的数据,该怎么做呢?此时他就不能通过浏览器去访问了,而应该自己写程序去访问指定的资源。
package cn.liayun.ie;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class RangeDemo {
public static void main(String[] args) throws Exception {
URL url = new URL("http://localhost:8080/day04/a.txt");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Range", "bytes=5-");
InputStream in = conn.getInputStream();
int len = 0;
byte[] buffer = new byte[1024];
FileOutputStream out = new FileOutputStream("d:\\a.txt", true);
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
out.close();
}
}
运行以上程序,该用户就下载完剩下的数据了。
HTTP响应头字段
需要说明的是HTTP响应头字段不常用。
- Accept-Ranges:这个字段说明WEB服务器是否支持Range,支持则返回
Accept-Ranges: bytes
;如果不支持,则返回Accept-Ranges: none
; - Content-Range:指定了返回的WEB资源的字节范围。这个字段值的格式类似
Content-Range: 1000-3000/5000
这样。