一、关于HTTP的协议版本

HTTP的全称是Hyper Text Transfer Protocol的缩写,即超级文本传输协议。HTTP协议用于定义浏览器与服务器之间交换数据的过程以及数据本身的格式。


HTTP是无状态协议,对于事务处理没有记录能力。因此后续处理如果需要前面的信息,则它必须重传,这会导致每次连接传送的数据量增大.


HTTP目前主要有三个版本:1.0、1.1、2.0。


http1.0的交互过程如下图所示:

HTTP及会话技术解析:大魏Java记4_java


  • 缺陷:浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接(TCP连接的新建成本很高,因为需要客户端和服务器三次握手),服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求;

  • 解决方案:

    • 添加头信息——非标准的Connection字段Connection: keep-alive



http1.1:

HTTP及会话技术解析:大魏Java记4_java_02

  • 改进点:

  1. 持久连接

  • 引入了持久连接,即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive(对于同一个域名,大多数浏览器允许同时建立6个持久连接)

管道机制

  • 即在同一个TCP连接里面,客户端可以同时发送多个请求。

分块传输编码

  • 即服务端没产生一块数据,就发送一块,采用”流模式”而取代”缓存模式”。

新增请求方式

  • PUT:请求服务器存储一个资源;

  • DELETE:请求服务器删除标识的资源;

  • OPTIONS:请求查询服务器的性能,或者查询与资源相关的选项和需求;

  • TRACE:请求服务器回送收到的请求信息,主要用于测试或诊断;

  • CONNECT:保留将来使用

  • 缺点:

    • 虽然允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个请求,才会接着处理下一个请求。如果前面的处理特别慢,后面就会有许多请求排队等着。这将导致“队头堵塞”

    • 避免方式:一是减少请求数,二是同时多开持久连接

HTTP/2.0

HTTP 2.0协议是在1.x基础上的升级而不是重写,1.x协议的方法,状态及api在2.0协议里是一样的2.0协议重点是对终端用户的感知延迟、网络及服务器资源的使用等性能的优化。

  • 特点:

  1. 采用二进制格式而非文本格式;

  2. 完全多路复用,而非有序并阻塞的、只需一个连接即可实现并行;

  3. 使用报头压缩,降低开销

  4. 服务器推送

1. 二进制协议

  • HTTP/1.1 版的头信息肯定是文本(ASCII编码),数据体可以是文本,也可以是二进制。HTTP/2 则是一个彻底的二进制协议,头信息和数据体都是二进制,并且统称为”帧”:头信息帧和数据帧。

  • 二进制协议解析起来更高效、“线上”更紧凑,更重要的是错误更少。

  • http2.0之所以能够突破http1.X标准的性能限制,改进传输性能,实现低延迟和高吞吐量,就是因为其新增了二进制分帧层。

  • 在二进制分帧层上,http2.0会将所有传输信息分割为更小的消息和帧,并对它们采用二进制格式的编码将其封装,新增的二进制分帧层同时也能够保证http的各种动词,方法,首部都不受影响,兼容上一代http标准。其中,http1.X中的首部信息header封装到Headers帧中,而request body将被封装到Data帧中。如下图所示:

HTTP及会话技术解析:大魏Java记4_java_03


2. 完全多路复用

  • HTTP/2 复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了”队头堵塞”。

3. 报头压缩

  • HTTP 协议是没有状态,导致每次请求都必须附上所有信息。所以,请求的很多头字段都是重复的,比如Cookie,一样的内容每次请求都必须附带,这会浪费很多带宽,也影响速度。

  • 对于相同的头部,不必再通过请求发送,只需发送一次;

  • HTTP/2 对这一点做了优化,引入了头信息压缩机制;

  • 一方面,头信息使用gzip或compress压缩后再发送;

  • 另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,产生一个索引号,之后就不发送同样字段了,只需发送索引号。

4. 服务器推送

  • HTTP/2 允许服务器未经请求,主动向客户端发送资源;

  • 通过推送那些服务器任务客户端将会需要的内容到客户端的缓存中,避免往返的延迟。



二、HTTP的请求方式

HTTP主要有8种请求方式,如下所示:

在这8种HTTP请求方式中,GET和POST是最常用的。网页上的form表单的默认提交方式是GET(在form表单的method属性不设置时)。


在实际的开发中,我们通常都会使用POST方式发送请求,而不是GET,原因有两个:

(1)POST传输数据大小无限制

(2)POST比GET请求方法更安全:GET请求的参数会在URL地址栏铭文显示,而POST请求方式传递的参数隐藏在实体内容中,用户看不到。


、HTTP的请求消息与响应消息

HTTP的请求消息和响应消息是相对应的,都包含三大部分:

HTTP消息除了请求头和响应头外,还有一些通用头字段,如下所示:


三、会话技术

在Web开发中,服务器跟踪用户信息的技术成为会话技术。

会话技术有两种实现:

(1)Cookie:将会话的过程数据保存到用户浏览器上。

(2)Session: 将会话数据保存到服务器端。


Cookie技术

Cookie通过将会话过程的数据保存到用户的浏览器上,使浏览器和服务器可以更好地进行数据交互。

我们可以形象的将Cookie理解成我们在商场办的会员卡。卡上记录了个人信息、消费额度和积分额度等。以后每次去商场,商场根据会员就能很快了解到顾客的会员信息。

在Cookie模式下,当用户通过浏览器访问Web服务器时,web服务器会给客户发送一些信息,这些信息都保存在Cookie中。这样,当浏览器再次访问服务器时,都会在请求头中将Cookie发送给服务器,Web服务器端可以分辨出当前请求是由哪个用户发出的,方便服务器对浏览器做出正确的响应,如下图所示:

HTTP及会话技术解析:大魏Java记4_java_04

为了封装Cookie信息,前端开发语言必要要有对应的方式。传统Servlet API会通过javax.servlet.http.Cookie类来实现。里面包含生成Cookie信息和提取Cookie信息的方法。由于现在Servlet已经不再被大量使用,因此我们不展开说明。


JavaScript Cookie的详细内容,可以看这个链接:

https://github.com/js-cookie/js-cookie

需要注意的是,无论浏览器是否支持Cookie,用户第一次访问程序时,由于服务器不知道客户浏览器是否支持Cookie,在第一次响应的页面中都会对URL地址进行重写。如果浏览器支持,那么后续的访问中都会使用Cookie的请求投资端将Session的标识号传递给服务器。由此,服务器判断浏览器支持Cookie,后续不再对URL进行重写。否则后续每个请求的URL都需要进行重写。


Session技术

Cookie的技术可以将用户的信息保存在各自的浏览器中,并且可以在多次请求下实现数据的共享。但如果传递的信息比较多,使用Cookie技术会增大服务器端的程序处理难度。这时,可以使用Session实现,它是一种将会话数据保存到服务器上的技术。


如果形象点说,Session技术就好比医院发给病人的就医卡。就医卡上只有卡号,没有其他信息,病人去看病时,主要出示就医卡,医院就可以根据卡号查到病历档案的过程。


当浏览器访问Web服务器时,Servlet容器(还以此举例)会创建一个Session对象和ID属性。Session对象相当于病例档案,ID就相当于就医卡号。客户端后续访问服务器时,只要将标识号传递给服务器,服务器就能根据请求是哪个客户发的,从而选择与之对应的Session对象为其服务。


需要注意的是:由于客户端要接受、记录和发送Session对象的ID,而Session是借助Cookie技术来传递ID属性的。


Session保存用户信息如下图所示:

HTTP及会话技术解析:大魏Java记4_java_04


为了方便理解,我们通过两端代码分别验证Cookie和Session的功能。



需求1:设计一个类,使用Cookie技术实现显示用户上次访问时间的功能。

package cn.itcast.chapter06.cookie.example;

import java.io.IOException;

import java.util.Date;

import javax.servlet.ServletException;

import javax.servlet.http.Cookie;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class LastAccessServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

response.setContentType("text/html;charset=utf-8");

/*

* 设定一个 cookie 的name : lastAccessTime

* 读取客户端发送cookie 获得用户上次的访问时间显示*/

String lastAccessTime = null;

// 获取所有的cookie,并将这些cookie存放在数组中

Cookie[] cookies = request.getCookies();

for (int i = 0; cookies != null && i < cookies.length; i++) {

if ("lastAccess".equals(cookies[i].getName())) {

// 如果cookie的名称为lastAccess,则获取该cookie的值

lastAccessTime = cookies[i].getValue();

break;

}

}

// 判断是否存在名称为lastAccess的cookie

if (lastAccessTime == null) {

response.getWriter().print("您是首次访问本站!!!");

} else {

response.getWriter().print("您上次的访问时间"+lastAccessTime);

}

// 创建cookie,将当前时间作为cookie的值发送给客户端

Cookie cookie = new Cookie("lastAccess",new Date().toLocaleString());

cookie.setMaxAge(60*60);//保存1小时

//访问chapter06下资源时回送cookie

cookie.setPath("/chapter06");

// 发送 cookie

response.addCookie(cookie);

}

}



需求2::设计一个类,使用Session技术实现购物车功能。

public class PurchaseServlet extends HttpServlet {

public void doGet(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {

// 获得用户购买的商品

String id = req.getParameter("id");

if (id == null) {

// 如果id为null,重定向到ListBookServlet页面

String url = "/chapter06/ListBookServlet";

resp.sendRedirect(url);

return;

}

Book book = BookDB.getBook(id);

// 创建或者获得用户的Session对象

HttpSession session = req.getSession();

// 从Session对象中获得用户的购物车

List<Book> cart = (List)

session.getAttribute("cart");

if (cart == null) {

// 首次购买,为用户创建一个购物车(List集合模拟购物车)

cart = new ArrayList<Book>();

// 将购物城存入Session对象

session.setAttribute("cart", cart);

}

// 将商品放入购物车

cart.add(book);

// 创建Cookie存放Session的标识号

Cookie cookie = new Cookie("JSESSIONID", session.getId());

cookie.setMaxAge(60 * 30);

cookie.setPath("/chapter06");

resp.addCookie(cookie);

// 重定向到购物车页面

String url = "/chapter06/CartServlet";

resp.sendRedirect(url);

}

}