HTTP是hypertext transfer protocol(超文本传输协议)的简写,它是TCP/IP协议的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的过程。客户端连上web服务器后,若想获得web服务器中的某个web资源,需遵守一定的通讯格式,HTTP协议用于定义客户端与web服务器通迅的格式。
HTTP协议是学习JavaWeb开发的基石,不深入了解HTTP协议,就不能说掌握了WEB开发,更无法管理和维护一些复杂的WEB站点。
HTTP协议的版本:HTTP/1.0、HTTP/1.1。
在HTTP1.0协议中,客户端与web服务器建立连接后,只能获得一个web资源。
HTTP1.1协议,允许客户端与web服务器建立连接后,在一个连接上获取多个web资源。
一个问题:一个web页面中,使用img标签引用了三幅图片,当客户端访问服务器中的这个web页面时,客户端总共会访问几次服务器,即向服务器发送了几次HTTP请求?
例,服务器中的web页面为1.html,内容为:
aaaaaaaaaaaaaaaaaaa
<img src="1.jpg" />
<img src="2.jpg" />
<img src="3.jpg" />
此时通过浏览器访问服务器中的1.html。
可发现客户端向服务器发送了4次http请求,第一次获取到整个页面资源。第二次浏览器向服务器要1.jpg,然后依此向服务器要2.jpg、3.jpg。为了减少客户端向浏览器发送的请求数,减少服务器的压力,通常会利用css技术将多张图片归并为一张图片,请求时,只打开这张图片的一小部分。
客户端连上服务器后,向服务器请求某个web资源,称之为客户端向服务器发送了一个HTTP请求。
一个完整的HTTP请求包括如下内容:
范例:
Web服务器通过客户端发送过来的这些请求信息,就可以确定向请求者回送什么资源,以及根据客户端的环境信息采用什么方式进行回送等。
请求行中的GET称之为请求方式,请求方式有:
不管POST或GET,都用于向服务器请求某个WEB资源,这两种方式的区别主要表现在数据传递上:
GET /mail/1.html?name=abc&password=xyz HTTP/1.1
。 HTTP请求中的常用消息头。
例,
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
If-Modified-Since: Thu, 07 Jul 2016 02:57:42 GMT
If-None-Match: W/"19-1467860262148"
一个HTTP响应代表服务器向客户端回送的数据,它包括:
范例:
在一个HTTP响应中,WEB服务器通过响应头向web客户端描述客户端的请求成功与否,以及它所发送的数据类型等一些信息,客户端通过这些信息,进而可以知道如何对数据进行处理。
状态行格式:HTTP版本号 状态码 原因叙述<CRLF>
。例:HTTP/1.1 200 OK
。
状态码用于表示服务器对请求的处理结果,它是一个三位的十进制数。响应状态码分为5类,如下所示:
这里还是细说几个常见的状态码:
HTTP响应中的常用响应头(消息头):
新建一个动态web项目——day04,该项目的结构图如下:
@WebServlet("/ServletDemo1")
public class ServletDemo1 extends HttpServlet {
// 用location和302实现请求重定向
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setStatus(302);
response.setHeader("location", "/day04/1.html");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
当在浏览器中使用URL地址http://localhost:8080/day04/ServletDemo1
访问ServletDemo1时,就可以看到服务器作出响应后发送到浏览器的状态码和响应头信息,如下图所示:
服务器返回一个302状态码告诉浏览器,你要的资源我没有,但是我通过Location响应头告诉你哪里有,而浏览器解析响应头Location后知道要跳转到/day04/1.html页面,所以就会自动跳转到1.html,如下图所示:
压缩数据的好处:
怎么实现数据的压缩?需要使用GZIPOutputStream流来压缩数据。
@WebServlet("/ServletDemo1")
public class ServletDemo1 extends HttpServlet {
// 压缩数据输出
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
System.out.println("原始数据大小:"+data.getBytes().length);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
GZIPOutputStream gout = new GZIPOutputStream(bout);
gout.write(data.getBytes());
/* * GZIPOutputStream:包装流,一般都会有一个缓冲, * 如果调用其write()方法在写数据时, * 如果数据量没有把包装流的缓冲写满,它是不会往底层流写的。 */
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 {
doGet(request, response);
}
}
服务器发给浏览器的响应信息如下:
此时Eclipse控制台打印如下信息:
@WebServlet("/ServletDemo1")
public class ServletDemo1 extends HttpServlet {
// 通过Content-Type头字段,控制浏览器以哪种方式处理数据
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("Content-Type", "image/jpeg");
// 读取位于项目根目录下的Krystal.jpg这张图片,返回一个输入流
InputStream in = this.getServletContext().getResourceAsStream("/Krystal.jpg");
int len = 0;
byte[] buffer = new byte[1024];
OutputStream out = response.getOutputStream();
while((len=in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
提示:Content-Type头字段对应的值可通过tomcat服务器下conf/web.xml文件查找。
服务器发给浏览器的响应信息如下:
ServletDemo1的运行结果如下图所示:
在浏览器中显示出了图片。
@WebServlet("/ServletDemo1")
public class ServletDemo1 extends HttpServlet {
// 定时刷新
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// response.setHeader("Refresh", "3"); // 浏览器每隔3秒钟请求一次
response.setHeader("Refresh", "3;url='http://www.sina.com'"); // 浏览器隔3秒钟请求一次后,并刷新到新浪上去
String data = "aaaaaaaaaaaaaaaaaaaaaaaa";
response.getOutputStream().write(data.getBytes());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
@WebServlet("/ServletDemo1")
public class ServletDemo1 extends HttpServlet {
// 定时刷新
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("Content-Disposition", "attachment;filename=Krystal.jpg");
InputStream in = this.getServletContext().getResourceAsStream("/Krystal(4).jpg");
int len = 0;
byte[] buffer = new byte[1024];
OutputStream out = response.getOutputStream();
while((len=in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
在浏览器中访问ServletDemo1就会弹出文件下载框,如下图所示:
可保存到桌面上。
Range头指示服务器只传输一部分web资源。这个头可以用来实现断点续传功能。Range字段可以通过三种格式设置要传输的字节范围:
例,web服务器有一个资源,比如是a.txt,内容为:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
有一个用户下载了一部分数据,并存储到C盘中的a.txt,内容为:
aaaaa
此时该用户想接着下载完剩下的数据,该怎么做呢?此时他就不能通过浏览器去访问了,而应该自己写程序去访问指定的资源。
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("c:\\a.txt", true);
while((len=in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
in.close();
out.close();
}
}
运行以上程序,该用户就下载完剩下的数据了。
Accept-Ranges:这个字段说明web服务器是否支持Range,支持则返回Accept-Ranges: bytes
;如果不支持,则返回Accept-Ranges: none
。
Content-Range:指定了返回的web资源的字节范围。这个字段值的格式是——例,Content-Range: 1000-3000/5000
。
注意:HTTP响应头字段不常用。