HTTP协议
HTTP协议(Hypertext Transfer Protocol,超文本传输协议),顾名思义,是关于如何在网络上传输超文本(即HTML文档)的协议。HTTP协议规定了Web的基本运作过程,以及浏览器与Web服务器之间的通信细节。HTTP协议采用客户/服务器通信模式, 是目前在Internet上应用最广泛的通信协议之一。
如图所示,在分层的网络体系结构中,HTTP协议位于应用层,建立在TCP/IP协议的基础上。HTTP协议使用可靠的TCP连接,默认端口是80端口
HTTP协议规定Web的基本运作过程基于客户/服务器通信模式,客户端主动发出HTTP请求,服务端接收HTTP请求,再返回相应的HTTP响应结果。客户端与服务器之间的一次信息交换包括以下过程:
(1)客户端与服务器端建立TCP连接
(2)客户端发出HTTP请求。
(3)服务器端发出相应的HTTP响应
(4)客户端与服务器端之间的TCP连接关闭
HTTP协议规定的信息交换过程
HTTP协议特点
基于 请求-响应 的模式
HTTP协议规定,请求从客户端发出,最后服务器端响应该请求并返回。换句话说,肯定是先从客户端开始建立通信的,服务器端在没有接收到请求之前不会发送响应。从浏览器与Web服务器的通信过程中,可以看出浏览器应该具备以下功能:
- 请求与Web服务器建立TCP连接
- 创建并发送HTTP请求
- 接收并解析HTTP响应
- 在窗口中展示HTML文档
Web服务器应该具备以下功能:
- 接收来自浏览器的HTTP请求
- 接收并解析HTTP请求
- 创建并发送HTTP响应
Http客户程序和Http服务器分别由不同的软件开发商提供,目前最常用的HTTP客户程序包括IE、Chrome、Firefox、Opera和Netscape等,最常用的HTTP服务器包括IIS和Apache等。
无状态保存
HTTP是一种不保存状态,即无状态(stateless)协议。HTTP协议自身不对请求和响应之间的通信状态进行保存。也就是说在HTTP这个级别,协议对于发送过的请求或响应都不做持久化处理。使用HTTP协议,每当有新的请求发送时,就会有对应的新响应产 生。协议本身并不保留之前一切的请求或响应报文的信息。这是为了更快地处理大量事务,确保协议的可伸缩性,而特意把HTTP协议设计成 如此简单的。
无连接
无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间,并且可以提高并发性能,不能和每个用户建立长久的连接,请求一次相应一次,服务端和客户端就中断了。但是无连接有两种方式,早期的http协议是一个请求一个响应之后,直接就断开了,但是现在的http协议1.1版本不是直接就断开了,而是等几秒钟,这几秒钟是等什么呢,等着用户有后续的操作,如果用户在这几秒钟之内有新的请求,那么还是通过之前的连接通道来收发消息,如果过了这几秒钟用户没有发送新的请求,那么就会断开连接,这样可以提高效率,减少短时间内建立连接的次数,因为建立连接也是耗时的
简单快速
客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST、PUT、DELETE、TRACE、OPTIONS。每种方法规定了客户与服务器联系的类型不同。 由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
其中get与post请求方式最常用
get与post请求区别
-
GET提交的数据会放在URL之后,也就是请求行里面,以?分割URL和传输数据,参数之间以&相连,如EditBook?name=test1&id=123456.(请求头里面那个content-type做的这种参数形式,后面讲) POST方法是把提交的数据放在HTTP包的请求体中.
-
GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制.
-
GET与POST请求在服务端获取请求数据方式不同,就是我们自己在服务端取请求数据的时候的方式不同了
灵活
HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记【MIME】
请求头
请求头包含许多有关客户端环境和请求正文的有用信息。例如:浏览器的类型、所用的语言、请求正文的类型,以及请求正文的长度等
请求头和请求正文之间必须以空行分割。
响应头
由三部分构成(HTTP协议的版本、状态码和描述;响应头;响应正文)
状态码解释
1×× 保留
2×× 表示请求成功地接收
3×× 为完成请求客户需进一步细化请求
4×× 客户错误(404,405)
5×× 服务器错误
用java套接字创建HTTP客户与服务器程序
package edu.uestc.avatar.socket; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.Executors; public class HttpServer { public static void main(String[] args) throws Exception { //创建服务器端的ServerSocket,通过port进行通信识别 @SuppressWarnings("resource") var server = new ServerSocket(8080); var executor = Executors.newCachedThreadPool(); while(true) { //监听客户端的连接 var socket = server.accept(); System.out.println("建立与客户端新的TCP连接,该客户端的地址:" + socket.getInetAddress()); executor.execute(()->{ try { service(socket); } catch (Exception e) { e.printStackTrace(); } }); } } /** * 响应客户端的HTTP请求 * @param socket 客户端Socket对象 * @throws Exception */ public static void service(Socket socket) throws Exception{ //读取Http请求信息 var input = socket.getInputStream(); /* * available():在读写操作前得知数据流中有多少个字节可以读取 * 该方法用于网络数据读取时,容易出现错误:当使用available读取时,对方发送的数据可能还没有到达,得到的长度为0 */ var size = input.available(); while(size == 0) size = input.available(); var buffer = new byte[size]; input.read(buffer); var request = new String(buffer); System.out.println(request); //解析HTTP请求 var firstLineOfRequest = request.substring(0, request.indexOf("\r\n")); //var method = firstLineOfRequest.split(" ")[0]; var uri = firstLineOfRequest.split(" ")[1]; //决定http响应的正文类型 String contentType; if(uri.indexOf(".html") != -1 || uri.indexOf(".htm") != -1) contentType = "text/html; charset=UTF-8"; else if(uri.indexOf(".jpg") != -1 || uri.indexOf(".jpeg") != -1) contentType = "image/jpeg"; else if(uri.indexOf(".gif") != -1) contentType = "image/gif"; else contentType = "application/octet-stream"; //创建http响应 var path = "public" + uri; var file = new File(path); var output = socket.getOutputStream(); if(file.exists()) { //文件存在,将文件内容响应给客户端 output.write("HTTP/1.1 200 OK\r\n".getBytes()); //响应正文和响应头之间有一空行 output.write(("content-type: " + contentType + "\r\n\r\n").getBytes()); var in = new BufferedInputStream(new FileInputStream(file)); int len = -1; var buff = new byte[1024]; while((len = in.read(buff)) != -1) output.write(buff, 0, len); in.close(); } else { //文件不存在,返回404 output.write("HTTP/1.1 404 NOT FOUND\r\n".getBytes()); output.write(("content-type: text/html; charset=UTF-8\r\n\r\n").getBytes()); output.write("404,你所请求的资源不存在".getBytes()); } socket.close(); } }