一、HTTP协议简介
提起HTTP协议我想大家都不陌生,我们经常上网访问网页就是使用它,它是hypertext transfer protocol(超文本传输协议)的简写,它是TCP/IP协议的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的过程。HTTP是WEB开发必备知识,对于管理和维护WEB站点很有用处。HTTP协议的版本分为:HTTP/1.0、HTTP/1.1。
HTTP1.0协议:客户端与web服务器建立连接后,只能获得一个web资源,在获取资源之后就断开连接。
HTTP1.1协议:允许客户端与web服务器建立连接后,在一个连接上获取多个web资源都不会断开连接,除非是访问出错和手动断开连接。
在Socket编程中,当我们设计一个通信协议时,“消息头/消息体”的分割方式是很常用的,消息头告诉对方这个消息是干什么的,消息体告诉对方怎么干。HTTP协议传输的消息也是这样规定的,每一个HTTP包都分为HTTP头和HTTP体两部分,消息体是可选的,而消息头是必须的。每当我们打开一个网页,在上面点击右键,选择“查看源文件”,这时看到的HTML代码就是HTTP的消息体,而消息头可以通过浏览器的开发工具或者插件可以看到,比如火狐的Firebug,IE的Httpwatch,接下来我们分析一下HTTP请求和相应的详细过程。
二、HTTP请求格式
客户端通过发送 HTTP 请求向服务器请求对资源的访问。 它向服务器传递了一个数据块,也就是请求信息,HTTP 请求由三部分组成:请求行、 请求头和请求正文。
(1) 请求行
请求行用于描述客户端的请求方式、请求的资源名称以及使用的HTTP协议版本号。根据HTTP标准,HTTP请求可以使用多种请求方法。例如:HTTP1.1支持7种请求方法:GET、POST、HEAD、OPTIONS、PUT、DELETE和TARCE。在Internet应用中,最常用的方法是GET和POST。
用户如果没有设置,默认情况下浏览器向服务器发送的都是get请求,例如在浏览器直接输地址访问,点超链接访问等都是get,用户如想把请求方式改为post,可通过更改表单的提交方式实现。不管POST或GET,都用于向服务器请求某个WEB资源,这两种方式的区别主要表现在数据传递上:如果请求方式为GET方式,则可以在请求的URL地址后以?的形式带上交给服务器的数据,多个数据之间以&进行分隔,例如:GET /test/test.html?name=aaa&password=aaa HTTP/1.1,GET方式的特点:在URL地址后附带的参数是有限制的,其数据容量通常不能超过1K。如果请求方式为POST方式,则可以在请求的实体内容中向服务器发送数据,Post方式的特点:传送的数据量无限制。
HTTP1.1 中的请求方式:
(2) 请求头
每个头域由一个域名,冒号(:)和域值三部分组成。域名是大小写无关的,域值前可以添加任何数量的空格符,头域可以被扩展为多行,在每行开始处,使用至少一个空格或制表符。
HTTP请求中的常用消息头
accept:浏览器通过这个头告诉服务器,它所支持的数据类型
Accept-Charset: 浏览器通过这个头告诉服务器,它支持哪种字符集
Accept-Encoding:浏览器通过这个头告诉服务器,支持的压缩格式
Accept-Language:浏览器通过这个头告诉服务器,它的语言环境
Host:浏览器通过这个头告诉服务器,想访问哪台主机
If-Modified-Since: 浏览器通过这个头告诉服务器,缓存数据的时间
Referer:浏览器通过这个头告诉服务器,客户机是哪个页面来的 防盗链
Connection:浏览器通过这个头告诉服务器,请求完后是断开链接还是保持链接
三、HTTP响应格式
在接收和解释请求消息后,服务器会返回一个 HTTP 响应消息。与 HTTP 请求类似,HTTP 响应也是由三个部分组成,分别是:状态行、消息报头和响应正文。
(1) 状态行
状态行由协议版本、数字形式的状态代码,及相应的状态描述组成,各元素之间以空格分隔。HTTP版本号 状态码 原因叙述<CRLF>
例如:HTTP/1.1 200 OK
状态代码由 3 位数字组成, 表示请求是否被理解或被满足,状态描述给出了关于状态码的简短的文字描述。状态码的第一个数字定义了响应类别,后面两位数字没有具体分类。第一个数字有 5 种取值,如下所示。
1xx:指示信息——表示请求已经接受,继续处理
2xx:成功——表示请求已经被成功接收、理解、接受。
3xx:重定向——要完成请求必须进行更进一步的操作
4xx:客户端错误——请求有语法错误或请求无法实现
5xx:服务器端错误——服务器未能实现合法的请求。
常见状态代码、状态描述、说明:
(2) 响应头信息
HTTP响应中的常用响应头(消息头)
Location: 服务器通过这个头,来告诉浏览器跳到哪里
Server:服务器通过这个头,告诉浏览器服务器的型号
Content-Encoding:服务器通过这个头,告诉浏览器,数据的压缩格式
Content-Length: 服务器通过这个头,告诉浏览器回送数据的长度
Content-Language: 服务器通过这个头,告诉浏览器语言环境
Content-Type:服务器通过这个头,告诉浏览器回送数据的类型
Refresh:服务器通过这个头,告诉浏览器定时刷新
Content-Disposition: 服务器通过这个头,告诉浏览器以下载方式打数据
Transfer-Encoding:服务器通过这个头,告诉浏览器数据是以分块方式回送的
Expires: -1 控制浏览器不要缓存
Cache-Control: no-cache
Pragma: no-cache
四、在服务端设置响应头来控制客户端浏览器
(1) 设置Location响应头,实现请求重定向获取WEB应用的初始化参数
package bobo.servlet.study;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author DaveBobo
*
*/
public class ServletDemo01 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setStatus(302);//设置服务器的响应状态码
/**
*设置响应头,服务器通过 Location这个头,来告诉浏览器跳到哪里,这就是所谓的请求重定向
*/
response.setHeader("Location", "/JavaWeb_Servlet001/1.jsp");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
当在浏览器中使用URL地址"http://localhost:8080/JavaWeb_Servlet001/servlet/ServletDemo01"访问ServletDemo01时,就可以看到服务器作出响应后发送到浏览器的状态码和响应头信息,如下图所示:
服务器返回一个302状态码告诉浏览器,你要的资源我没有,但是我通过Location响应头告诉你哪里有,而浏览器解析响应头Location后知道要跳转到/JavaWeb_Servlet001/1.jsp页面,所以就会自动跳转到1.jsp,如下图所示:
(2) 设置Content-Encoding响应头,告诉浏览器数据的压缩格式
package bobo.servlet.study;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author DaveBobo
*这个小程序是用来演示以下两个小知识点
*1、使用GZIPOutputStream流来压缩数据
*2、设置响应头Content-Encoding来告诉浏览器,服务器发送回来的数据压缩后的格式
*/
public class ServletDemo02 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String data = "abcdabcdabcdabcdabcdabcdab" +
"cdabcdabcdabcdabcdabcdabcdabcdabc" +
"dabcdabcdabcdabcdabcdabcdabcdabc" +
"dabcdabcdabcdabcdabcdabcdabcdabcdab" +
"cdabcdabcdabcdabcdabcdabcdabcdabcdab" +
"cdabcdabcdabcdabcdabcdabcdabcdabcdab" +
"cdabcdabcdabcdabcdabcdabcdabcdabcdab" +
"cdabcdabcdabcdabcdabcdabcdabcdabcdabcd";
System.out.println("原始数据的大小为:" + data.getBytes().length);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
GZIPOutputStream gout = new GZIPOutputStream(bout); //buffer
gout.write(data.getBytes());
gout.close();
//得到压缩后的数据
byte g[] = bout.toByteArray();
response.setHeader("Content-Encoding", "gzip");
response.setHeader("Content-Length",g.length +"");
response.getOutputStream().write(g);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
(3) 设置content-type响应头,指定回送数据类型
package bobo.servlet.study;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletDemo03 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 浏览器能接收(Accept)的数据类型有:
* application/x-ms-application,
* image/jpeg,
* application/xaml+xml,
* image/gif,
* image/pjpeg,
* application/x-ms-xbap,
* application/vnd.ms-excel,
* application/vnd.ms-powerpoint,
* application/msword,
*/
response.setHeader("content-type", "image/jpeg");//使用content-type响应头指定发送给浏览器的数据类型为"image/jpeg"
//读取位于项目根目录下的img文件夹里面的head.jpg这张图片,返回一个输入流
InputStream in = this.getServletContext().getResourceAsStream("/img/head.jpg");
byte buffer[] = new byte[1024];
int len = 0;
OutputStream out = response.getOutputStream();//得到输出流
while ((len = in.read(buffer)) > 0) {//读取输入流(in)里面的内容存储到缓冲区(buffer)
out.write(buffer, 0, len);//将缓冲区里面的内容输出到浏览器
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
服务器发给浏览器的响应信息如下:
(4) 设置refresh响应头,让浏览器定时刷新
package bobo.servlet.study;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletDemo04 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 设置refresh响应头,让浏览器每隔3秒定时刷新
*/
// response.setHeader("refresh", "3");
/**
* 设置refresh响应头,让浏览器3秒后跳转到http://www.baidu.com
*/
response.setHeader("refresh", "3;url='http://www.baidu.com'");
response.getWriter().write("DaveBobo");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
(5)设置content-disposition响应头,让浏览器下载文件
package bobo.servlet.study;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletDemo05 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 设置content-disposition响应头,让浏览器下载文件
*/
response.setHeader("content-disposition", "attachment;filename=xxx.jpg");
InputStream in = this.getServletContext().getResourceAsStream("/img/head.jpg");
byte buffer[] = new byte[1024];
int len = 0;
OutputStream out = response.getOutputStream();
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}<a target=_blank target="_blank" name="top"></a>