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();
    }
}

 

posted @ 2022-05-09 16:15  Tiger-Adan  阅读(212)  评论(0编辑  收藏  举报