超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协做式和超媒体信息系统的应用层协议。HTTP是万维网的数据通讯的基础。javascript
HTTP的发展是由蒂姆·伯纳斯-李于1989年在欧洲核子研究组织(CERN)所发起。HTTP的标准制定由万维网协会(World Wide Web Consortium,W3C)和互联网工程任务组(Internet Engineering Task Force,IETF)进行协调,最终发布了一系列的RFC,其中最著名的是1999年6月公布的 RFC 2616,定义了HTTP协议中现今普遍使用的一个版本——HTTP 1.1。php
2014年12月,互联网工程任务组(IETF)的Hypertext Transfer Protocol Bis(httpbis)工做小组将HTTP/2标准提议递交至IESG进行讨论,于2015年2月17日被批准。 HTTP/2标准于2015年5月以RFC 7540正式发表,取代HTTP 1.1成为HTTP的实现标准。html
HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TCP)。经过使用网页浏览器、网络爬虫或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)。咱们称这个客户端为用户代理程序(user agent)。应答的服务器上存储着一些资源,好比HTML文件和图像。咱们称这个应答服务器为源服务器(origin server)。在用户代理和源服务器中间可能存在多个“中间层”,好比代理服务器、网关或者隧道(tunnel)。java
尽管TCP/IP协议是互联网上最流行的应用,HTTP协议中,并无规定必须使用它或它支持的层。事实上,HTTP能够在任何互联网协议上,或其余网络上实现。HTTP假定其下层协议提供可靠的传输。所以,任何可以提供这种保证的协议均可以被其使用。所以也就是其在TCP/IP协议族使用TCP做为其传输层。windows
一般,由HTTP客户端发起一个请求,建立一个到服务器指定端口(默认是80端口)的TCP链接。HTTP服务器则在那个端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态,好比"HTTP/1.1 200 OK",以及返回的内容,如请求的文件、错误消息、或者其它信息。后端
HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行做为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。浏览器
如下是 HTTP 请求/响应的步骤:缓存
\1. 客户端链接到Web服务器
一个HTTP客户端,一般是浏览器,与Web服务器的HTTP端口(默认为80)创建一个TCP套接字链接。服务器
\2. 发送HTTP请求
经过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。网络
\3. 服务器接受请求并返回HTTP响应
Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。
\4. 释放链接TCP链接
若connection 模式为close,则服务器主动关闭TCP链接,客户端被动关闭链接,释放TCP链接;若connection 模式为keepalive,则该链接会保持一段时间,在该时间内能够继续接收请求;
\5. 客户端浏览器解析HTML内容
客户端浏览器首先解析状态行,查看代表请求是否成功的状态代码。而后解析每个响应头,响应头告知如下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。
例如:在浏览器地址栏键入URL,按下回车以后会经历如下流程:
http协议是基于TCP/IP协议之上的应用层协议。
基于 请求-响应 的模式
HTTP协议规定,请求从客户端发出,最后服务器端响应该请求并 返回。换句话说,确定是先从客户端开始创建通讯的,服务器端在没有 接收到请求以前不会发送响应
无状态保存
HTTP是一种不保存状态,即无状态(stateless)协议。HTTP协议 自身不对请求和响应之间的通讯状态进行保存。也就是说在HTTP这个 级别,协议对于发送过的请求或响应都不作持久化处理。
使用HTTP协议,每当有新的请求发送时,就会有对应的新响应产 生。协议自己并不保留以前一切的请求或响应报文的信息。这是为了更快地处理大量事务,确保协议的可伸缩性,而特地把HTTP协议设计成 如此简单的。但是,随着Web的不断发展,因无状态而致使业务处理变得棘手 的状况增多了。好比,用户登陆到一家购物网站,即便他跳转到该站的 其余页面后,也须要能继续保持登陆状态。针对这个实例,网站为了能 够掌握是谁送出的请求,须要保存用户的状态。HTTP/1.1虽然是无状态协议,但为了实现指望的保持状态功能, 因而引入了Cookie技术。有了Cookie再用HTTP协议通讯,就能够管 理状态了。有关Cookie的详细内容稍后讲解。
无链接
无链接的含义是限制每次链接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开链接。采用这种方式能够节省传输时间,而且能够提升并发性能,不能和每一个用户创建长久的链接,请求一次相应一次,服务端和客户端就中断了。可是无链接有两种方式,早期的http协议是一个请求一个响应以后,直接就断开了,可是如今的http协议1.1版本不是直接就断开了,而是等几秒钟,这几秒钟是等什么呢,等着用户有后续的操做,若是用户在这几秒钟以内有新的请求,那么仍是经过以前的链接通道来收发消息,若是过了这几秒钟用户没有发送新的请求,那么就会断开链接,这样能够提升效率,减小短期内创建链接的次数,由于创建链接也是耗时的,默认的好像是3秒中如今,可是这个时间是能够经过我们后端的代码来调整的,本身网站根据本身网站用户的行为来分析统计出一个最优的等待时间。
HTTP遵循请求(Request)/应答(Response)模型,Web浏览器向Web服务器发送请求时,Web服务器处理请求并返回适当的应答。
POST /test.php HTTP/1.1 //请求行 HOST:www.test.com //请求头 User-Agent:Mozilla/5.0 (windows NT 6.1;rv:15.0)Gecko/20100101 Firefox/15.0 //空白行,表明请求头结束 Username=admin&password=admin //请求正文 1234
HTTP请求包括三部分,分别是请求行(请求方法)、请求头(消息报头)和请求正文。
HTTP请求第一行为请求行,由三部分组成,第一部分说明了该请求时POST请求,第二部分是一个斜杠(/login.php),用来讲明请求是该域名根目录下的login.php,第三部分说明使用的是HTTP1.1版本。
HTTP请求第二行至空白行为请求头(也被称为消息头)。其中,HOST表明请求主机地址,User-Agent表明浏览器的标识,请求头由客户端自行设定。
HTTP请求第三行为请求正文,请求正文是可选的,它最常出如今POST请求方式中。
根据 HTTP 标准,HTTP 请求可使用多种请求方法。
HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD方法。
HTTP1.1 新增了五种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。
序号 | 方法 | 描述 |
---|---|---|
1 | GET | 请求指定的页面信息,并返回实体主体。 |
2 | HEAD | 相似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头 |
3 | POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会致使新的资源的创建和/或已有资源的修改。 |
4 | PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
5 | DELETE | 请求服务器删除指定的页面。 |
6 | CONNECT | HTTP/1.1 协议中预留给可以将链接改成管道方式的代理服务器。 |
7 | OPTIONS | 容许客户端查看服务器的性能。 |
8 | TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
8 | PATCH | 是对 PUT 方法的补充,用来对已知资源进行局部更新 。 |
GET、POST、HEAD、PUT请求:
HTTP/1.1 200 OK //响应行 Date: Sun, 15 Nov 2015 11:02:04 GMT //响应头 Server: bfe/1.0.8.9 Content-Length: 2605 Content-Type: application/javascript Cache-Control: max-age=315360000 Expires: Fri, 13 Jun 2025 09:54:00 GMT Content-Encoding: gzip Set-Cookie: H_PS_PSSID=2022_1438_1944_1788; path=/; domain=test.com Connection: keep-alive //空白行,表明响应头结束 <html> <head><title> Index.html </title></head> //响应正文消息主题 1234567891011121314
HTTP响应的第一行为响应行,其中有HTTP版本(HTTP/1.1)、状态码(200)以及消息“OK”。
第二行至末尾的空白行为响应头,由服务器向客户端发送。
消息头以后是响应正文,是服务器向客户端发送的HTML数据。
请求头:请求头只出如今HTTP请求中,请求报头容许客户端向服务端传递请求的附加信息和客户端自身信息。
响应头:响应头是服务器根据请求向客户端发送的HTTP头。
应答头 | 说明 |
---|---|
Allow | 服务器支持哪些请求方法(如GET、POST等)。 |
Content-Encoding | 文档的编码(Encode)方法。只有在解码以后才能够获得Content-Type头指定的内容类型。利用gzip压缩文档可以显著地减小HTML文档的下载时间。 |
Content-Length | 表示内容长度。 |
Content-Type | 表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但一般须要显式地指定为text/html。 |
Date | 当前的GMT时间。 |
Expires | 应该在何时认为文档已通过期,从而再也不缓存它? |
Last-Modified | 文档的最后改动时间。客户能够经过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档才会返回,不然返回一个304(Not Modified)状态。 |
Location | 表示客户应当到哪里去提取文档。Location一般不是直接设置的,而是经过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302。 |
Refresh | 表示浏览器应该在多少时间以后刷新文档,以秒计。注意Refresh头不属于HTTP 1.1正式规范的一部分,而是一个扩展,但Netscape和IE都支持它。 |
Server | 服务器名字。Servlet通常不设置这个值,而是由Web服务器本身设置。 |
Set-Cookie | 设置和页面关联的Cookie。 |
WWW-Authenticate | 客户应该在Authorization头中提供什么类型的受权信息?在包含401(Unauthorized)状态行的应答中这个头是必需的。 |
当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头(server header)用以响应浏览器的请求。
HTTP状态码的英文为HTTP Status Code。
五种状态码:
常见的状态码描述以下:
WEB应用中的会话是指一个客户端浏览器与WEB服务器之间连续发生的一系列请求和响应过程。
WEB应用的会话状态是指WEB服务器与浏览器在会话过程当中产生的状态信息,借助会话状态,WEB服务器可以把属于同一会话中的一系列的请求和响应过程关联起来。
某个用户从网站的登陆页面登入后,在进入购物页面购物时,负责处理购物请求的服务器程序必须知道处理上一次请求的程序所获得的用户信息。
HTTP协议是一种无状态的协议,WEB服务器自己不能识别出哪些请求是同一个浏览器发出的 ,浏览器的每一次请求都是彻底孤立的。
WEB服务器端程序要能从大量的请求消息中区分出哪些请求消息属于同一个会话,即能识别出来自同一个浏览器的访问请求,这须要浏览器对其发出的每一个请求消息都进行标识,属于同一个会话中的请求消息都附带一样的标识号,而属于不一样会话的请求消息老是附带不一样的标识号,这个标识号就称之为会话ID(SessionID)。
会话ID能够经过一种称之为Cookie的技术在请求消息中进行传递,也能够做为请求URL的附加参数进行传递。会话ID是WEB服务器为每客户端浏览器分配的一个惟一代号,它一般是在WEB服务器接收到某个浏览器的第一次访问时产生,而且随同响应消息一道发送给浏览器。
会话过程由WEB服务器端的程序开启,一旦开启了一个会话,服务器端程序就要为这个会话建立一个独立的存储结构来保存该会话的状态信息,同一个会话中的访问请求均可以且只能访问属于该会话的存储结构中的状态信息。
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.util.Iterator; public class HttpServer { public static void main(String[] args) throws Exception { // 建立ServerSocketChannel,监听8080端口 ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.socket().bind(new InetSocketAddress(8080)); // 设置为非阻塞模式 ssc.configureBlocking(false); // 为ssc注册选择器 Selector selector = Selector.open(); ssc.register(selector, SelectionKey.OP_ACCEPT); // 建立处理器 while (true) { // 等待请求,每次等待阻塞3s,超过3s后线程继续向下运行,若是传入0或者不传参数将一直阻塞 if (selector.select(3000) == 0) { continue; } // 获取待处理的SelectionKey Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator(); while (keyIter.hasNext()) { SelectionKey key = keyIter.next(); // 启动新线程处理SelectionKey new Thread(new HttpHandler(key)).run(); // 处理完后,从待处理的SelectionKey迭代器中移除当前所使用的key keyIter.remove(); } } } private static class HttpHandler implements Runnable { private int bufferSize = 1024; private String localCharset = "UTF-8"; private SelectionKey key; public HttpHandler(SelectionKey key) { this.key = key; } public void handleAccept() throws IOException { SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept(); clientChannel.configureBlocking(false); clientChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize)); } public void handleRead() throws IOException { // 获取channel SocketChannel sc = (SocketChannel) key.channel(); // 获取buffer并重置 ByteBuffer buffer = (ByteBuffer) key.attachment(); buffer.clear(); // 没有读到内容则关闭 if (sc.read(buffer) == -1) { sc.close(); } else { // 接收请求数据 buffer.flip(); String receivedString = Charset.forName(localCharset).newDecoder().decode(buffer).toString(); // 控制台打印请求报文头 String[] requestMessage = receivedString.split("\r\n"); for (String s : requestMessage) { System.out.println(s); // 遇到空行说明报文头已经打印完 if (s.isEmpty()) { break; } } // 控制台打印首行信息 String[] firstLine = requestMessage[0].split(" "); System.out.println(); System.out.println("Method:\t" + firstLine[0]); System.out.println("url:\t" + firstLine[1]); System.out.println("HTTP Version:\t" + firstLine[2]); System.out.println(); // 返回客户端 StringBuilder sendString = new StringBuilder(); sendString.append("HTTP/1.1 200 OK\r\n");//响应报文首行,200表示处理成功 sendString.append("Content-Type:text/html;charset=" + localCharset + "\r\n"); sendString.append("\r\n");// 报文头结束后加一个空行 sendString.append("<!DOCTYPE html>"); sendString.append("<html lang=\"en\">"); sendString.append("<head>"); sendString.append(" <meta charset=\"UTF-8\">"); sendString.append(" <title>Title</title>"); sendString.append("</head>"); sendString.append("<body>"); sendString.append(" <h4>hello world! </h4>"); sendString.append("</body>"); sendString.append("</html>"); buffer = ByteBuffer.wrap(sendString.toString().getBytes(localCharset)); sc.write(buffer); sc.close(); } } @Override public void run() { try { // 接收到链接请求时 if (key.isAcceptable()) { handleAccept(); } // 读数据 if (key.isReadable()) { handleRead(); } } catch (IOException ex) { ex.printStackTrace(); } } } }