HTTP(超文本传输协议),应用层协议

HTTP 往往是基于传输层的 TCP 协议实现的.

HTTP1(主流版本)基于TCP

HTTP2(支持不多)基于TCP,引入的是安全性(相当于HTTPS的加强版)

HTTP3(建设中) 基于UDP,提高效率

所谓 "超文本" 的含义, 就是传输的内容不仅仅是文本(比如 html, css 这个就是文本), 还可以是一些其他的资源, 比如图片, 视频, 音频等二进制的数据

 

理解应用层协议

下四层保证数据传输;

将传输的数据流解析,提取数据;

应用层:信息+格式(关注数据本身)

请求都要传输那些信息,按照何种格式传输;响应都要包含那些信息,按照何种格式传输

应用层协议工作方式

客户端vs服务端

请求 vs 响应

客户端服务端之间的多种模型

一发一收:(http)

一次请求一次响应

多发一收:(大文件传输)

多次请求,一次响应

一发多收:(直播)

一次请求,多次响应

多发多收:(串流,Steam支持这个功能)

多个请求,多个响应

HTTP协议格式

HTTP是一个文本协议,

传输二进制数据,通过对文本数据编码实现(body)

思考问题: 为什么 HTTP 报文中要存在 "空行"?

因为 HTTP 协议并没有规定报头部分的键值对有多少个. 空行就相当于是 "报头的结束标记", 或者 是 "报头和正文之间的分隔符".

HTTP 在传输层依赖 TCP 协议, TCP 是面向字节流的. 如果没有这个空行, 就会出现 "粘包问题".

 

HTTP请求(Request)

认识URL

URL不止给HTTP服务,还可以给很多服务提供服务

HTTP、FTP、jdbc:mysql://

URL基本格式

俗称的 "网址" 其实就是说的 URL (Uniform Resource Locator 统一资源定位符

互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它URL 的详细规则由 因特网标准RFC1738 进行了约定. (https://datatracker.ietf.org/doc/html/rfc1738)

网络很多协议,都有相关的标准文档描述--RFC系列文档

协议方案名 登录信息 服务器地址 服务器端口号 带层次的文件路径 查询字符串 片段表示符

协议方案名:方案名 后的 :// 是固定的登录信息:如今网站身份认证一般不通过url进行(一般省略)

服务器地址:可以是域名(域名通过DNS解析为ip地址)、ip地址端口号:表示要访问的服务器端口,若省略,浏览器会根据协议给定一个默认的端口号带层次的文件路径:表示访问服务器上的不同的资源查询字符串(query string):浏览器给服务器传递的信息参数可以有,可以无,键值意义,由程序员约定参数通过键值对方式组织;

键值对之间,使用 & 分隔键和值之间,使用 = 分隔片段标识:定位到页面的某个部分

URL的作用:区分一个网络上的唯一资源

先通过 服务器地址 ,定位到一个具体的服务器再通过 端口号 定位到一个具体的应用程序再通过 层次路径 定位到这个应用程序管理的一个具体资源再通过 查询字符串 对这个具体资源的要求做出进一步解释最后通过 片段标识 确定定位到这个资源的某个部分

URL encode

URL中存在用户自定义的东西,典型的为query string;

而自定义的query string中可能存在特殊字符

: / ? = & .

若包含如上等特殊字符(除此之外,汉字之类),就可能导致浏览器/服务器解析URL时出错,

需要针对这些字符转义(浏览器可以自动进行)

解决方法:

URL encode : 把特殊字符,转成转义字符

URL decode :将转义字符,还原成原来的字符

为何要url encode 及基本规则?

不必手动实现url encode函数;

虽然会有浏览器自动对特殊字符处理进行encode,但是有时需要手动构造url ,不一定自动encode,

所以需要手动构造URL时,且带有特殊字符时,一定手动encode ,否则可能请求发送失败;

认识方法(method)

HTTP1版本的方法

GET方法

常用于获取服务器的某个资源

使用GET给服务器提交数据,完全可以

使用GET给服务器删除数据,完全可以

是不常见的用法

那些方式会出发HTTP请求:

1.直接在浏览器中输入URL,会触发一个HTTP请求

2.HTML中特殊标签,link,img,script,a,会触发HTTP的GET请求

双击打开本地文件不会走网络请求

3.使用 JavaScript 中的 ajax 也能构造 GET 请求.

4.form标单

5.使用java代码、其他的库

6.通过Linux下的wget、curl

7.第三方工具postman等

GET 请求的特点

首行的第一部分为 GETURL 的 query string 可以为空, 也可以不为空.header 部分有若干个键值对结构.body 部分 为空(通常情况下,也可以不为空).

关于GET请求的URL问题

HTTP 协议由 RFC 2616 标准定义, 标准原文中明确说明: "Hypertext Transfer Protocol --

HTTP/1.1," does not specify any requirement for URL length

即RFC标准文档中,没有对URL做出长度限制;

只是部分浏览器,不遵守标准;当前的Chrome支持长度很长;

POST方法

常用于向服务器提交数据(如登录页面)

构造POST请求的方式

1.form

2.ajax

3.第三方工具

POST请求的特点

首行第一部分为POSTURL的query string一般为空(也可不为空)header部分若干键值对body部分一般不为空(也可以为空)

body 内的数据格式通过 header 中的 Content-Type 指定.body 的长度由header 中的 Content-Length 指定.内容由程序员决定

GET和POST的区别

(HTTP协议的各种方法间,尤其GET,POST)没有本质区别;

数据位置:GET把自定义数据放到query string,POST把自定义数据放到body语义区别:GET一般用于获取数据,POST一般用于提交数据幂等性:GET请求一般设计为幂等的,POST一般不要求设计成幂等

幂等性:某个请求,执行一次和多次没有区别;(查询账户余额-幂等)

可缓存:GET请求一般能被缓存,POST一般不能被缓存

进行复杂计算得到结果,过程开销大,就将结果保存下来,下次直接使用,避免重复计算;

根据业务场景,具体分析;(例如搜索广告,每个请求要实时计算)

以上四点,都取决于程序员设计,为常用用法,规则可打破,

其他方法

PUT 与 POST 相似,只是具有幂等特性,一般用于更新DELETE 删除服务器指定资源OPTIONS 返回服务器所支持的请求方法HEAD 类似于GET,只不过响应体不返回,只返回响应头TRACE 回显服务器端收到的请求,测试的时候会用到这个CONNECT 预留,暂无使用

这些方法的 HTTP 请求可以使用 ajax 来构造. (也可以通过一些第三方工具)

任何一个能进行网络编程的语言都可以构造 HTTP 请求. 本质上就是通过 TCP socket 写入一个符 合 HTTP 协议规则的字符串

认识请求报头(header)

请求报头中的键值对有很多

有些是用户自定义;

有些是标准规定,具有特殊意义(此处列举典型)

Host

描述主机地址/端口号

地址可以是域名,也可以是IP;

一般来说同URL中表示信息重叠,但也不是绝对

Content-Type

表示body中数据格式类型

常见选项:

application/x-www-form-urlencoded

表单提交的数据格式.

此时 body 的格式形如:这种格式和query string 差不多

multipart/form-data

表单提交的数据格式(在 form 标签中加上enctyped="multipart/form-data" . 通常用于提交图片/文件. 

application/json

数据为 json 格式.

User-Agent(UA)

表示浏览器/操作系统的版本信息的属性

Referer

表示当前页面是从哪个页面跳转而来;

直接在浏览器输入URL/点击收藏夹 打开的页面没有Referer

Cookie

Cookie 中存储了一个字符串, 这个数据可能是客户端(网页)自行通过 JS 写入的, 也可能来自于服务器(服 务器在 HTTP 响应的 header 中通过 Set-Cookie 字段给浏览器返回数据).相当于浏览器进行本地存储的一种机制Cookie能够 在后续请求服务器时,自动将之前保存的值带上

认识请求正文(body)

正文中的内容格式和 header 中的 Content-Type 密切相关.

http文本协议,正文数据若传输二进制文件要encode;

HTTP响应

认识状态码(status code)

状态码表示访问一个页面的结果.

常见状态码:

200 OK :访问成功

成功访问

404 Not Found :没有找到资源

url资源找不到

403 Forbidden :拒绝访问

访问被服务器拒绝,一般为权限不够,输入有误

405 Method Not Allowed :方法不允许

服务器不接受该类HTTP方法

500 Internal Server Error :服务器崩溃

服务器内部崩溃

302 Move temporarily : 临时重定向

访问页面,跳转至其他页面

在登陆页面中经常会见到 302. 用于实现登陆成功后自动跳转到主页.

响应报文的 header 部分会包含一个 Location 字段, 表示要跳转到哪个页面

301 Moved Permanently:永久重定向

当浏览器收到这种响应时, 后续的请求都会被自动改成新的地址.

301 也是通过 Location 字段来表示要重定向到的新地址

 

认识响应报头(header)

响应报头的基本格式和请求报头的格式基本一致.

类似于 Content-Type , Content-Length 等属性的含义也和请求中的含义一致.

Content-Type

响应中的 Content-Type 常见取值有以下几种:

text/html : body 数据格式是 HTMLtext/css : body 数据格式是 CSSapplication/javascript : body 数据格式是 JavaScriptapplication/json : body 数据格式是 JSON

F5:刷新,仍会利用缓存数据

ctrl+F5:强制刷新,清除缓存数据

浏览器会将以页面的依赖文件下载至本地,以提高后续访问的速度

认识响应正文(body)

正文的具体格式取决于 Content-Type.

观察上面几个抓包结果中的响应部分

text/html

html格式text/css

css格式application/javascript

javascript格式application/json

json格式

HTTP构造请求的方式

浏览器地址栏,输入url,构造form表单构造;ajax构造各种请求

通过form表单构造HTTP请求

form (表单) 是 HTML 中的一个常用标签. 可以用于给服务器发送 GET 或者 POST 请求

form 的重要参数:

action: 构造的 HTTP 请求的 URL 是什么.method: 构造的 HTTP 请求的 方法 是 GET 还是 POST (form 只支持 GET 和 POST).

input 的重要参数:

type: 表示输入框的类型. text 表示文本, password 表示密码, submit 表示提交按钮.name: 表示构造出的 HTTP 请求的 query string 的 key. query string 的 value,就是输入框的用户 输入的内容.value: input 标签的值. 对于 type 为 submit 类型来说, value 就对应了按钮上显示的文本

form发送GET请求

通过ajax构造HTTP请求(主流方式)

ajax 全称 Asynchronous(异步) Javascript And XML(如今很少用xml文件), 是 2005 年提出的一种 JavaScript 给服务器发送 HTTP 请求的方式

同步异步,体现在等待模式的不同;

同步:发出通知后,不做其他事,时时刻刻,监视等待;

不同于多线程中的同步:多线程中的同步实际更像是互斥;

异步:发出通知后,就做其他事,等到对方发出通知;

网络编程中,数通过网络传输,主要分为两个阶段:

等待;(耗时)数据拷贝;

使用js原生带入的ajax

// 1.创建XMLHttpRequest 对象

let httpRequest = new XMLHttpRequest();

// 4.调用回调函数,接收响应

httpRequest.onreadystatechange = function(){

    // readState 表示当前的状态.

    // 0: 请求未初始化

    // 1: 服务器连接已建立

    // 2: 请求已接收

    // 3: 请求处理中

    // 4: 请求已完成,且响应已就绪

    if(httpRequest.readyState == 4){

        //响应已经获取

        //status 属性获取http响应状态码

        console.log(httpRequest.status);

        //responseText 属性获取 HTTP 响应 body

        console.log(httpRequest.responseText);

    }

}

//-----------get---------------

// 2.调用open方法设置 http方法 访问的url

httpRequest.open('GET',' http://42.192.83.143:8080/AjaxMockServer/info')

// 3.调用send方法发送http请求

httpRequest.send();

//-----------post---------------

//form 数据

// 2.1 调用 open 方法设置要访问的 url

httpRequest.open('POST', 'http://42.192.83.143:8080/AjaxMockServer/info');

// 2.2 调用 setRequestHeader 设置请求头

httpRequest.setRequestHeader('Content-Type', 'application/x-www-formurlencoded');

//3 发送http请求

httpRequest.send('name=zhangsan&age=18');

//json 数据

// 4. 调用 setRequestHeader 设置请求头

httpRequest.setRequestHeader('Content-Type', 'application/json');

// 5. 调用 send 方法发送 http 请求

httpRequest.send(JSON.stringify({

name: 'zhangsan',

age: 18

}));

ajax中跨域问题

ajax为了安全性,要求发起ajax请求的页面,和接受ajax请求的服务器,应在同一域名/地址下;

跨域:如果发起请求的页面对应的域名(域名1),和接受ajax请求的服务器的域名(域名2),如果域名1和域名2不相同,认为是一次跨域请求。

ajax默认情况下,不允许跨域访问跨域限制,由服务器方,通过代码解除

通过Java socket构造HTTP请求

所谓的 "发送 HTTP 请求",

本质上就是按照 HTTP 的格式往 TCP Socket 中写入一个字符串.

所谓的 "接受 HTTP 响应",

本质上就是从 TCP Socket 中读取一个字符串, 再按照 HTTP 的格式来解析.

我们基于 Socket 的知识, 完全可以构造出一个简单的 HTTP 客户端程序, 用来发送各种类型的 HTTP 请 求

public class HTTPClient {

private Socket socket;

private String ip;

private int port;

public HTTPClient(String ip,int port) throws IOException {

this.ip = ip;

this.port = port;

socket = new Socket(ip,port);

}

public String get(String url) throws IOException {

//构建请求

StringBuffer request = new StringBuffer();

//1.构建首行 方法 url 版本号

request.append("GET "+url+" HTTP/1.1\n");

//构建 header

request.append("Host: "+ip+":"+port+"\n");

//构建 空行

request.append("\n");

// 发送数据

OutputStream outputStream = socket.getOutputStream();

outputStream.write(request.toString().getBytes());

//读取响应数据

InputStream inputStream = socket.getInputStream();

//构建缓冲区

byte[] buffer = new byte[1024*1024];

int n = inputStream.read(buffer);

return new String(buffer,0,n,"utf-8");

}

public String post(String url,String body) throws IOException {

//构建请求

StringBuffer request = new StringBuffer();

//构造首行

request.append("POST "+url+" HTTP/1.1\n");

//构造header

request.append("Host: " + ip + ":" + port + "\n");

request.append("Content-Length: " + body.getBytes().length + "\n");

request.append("Content-Type: text/plain\n");

// 构造 空行

request.append("\n");

// 构造 body

request.append(body);

// 发送数据

OutputStream outputStream = socket.getOutputStream();

outputStream.write(request.toString().getBytes());

// 读取响应数据

InputStream inputStream = socket.getInputStream();

byte[] buffer = new byte[1024 * 1024];

int n = inputStream.read(buffer);

return new String(buffer, 0, n, "utf-8");

}

public static void main(String[] args) throws IOException {

HTTPClient httpClient = new HTTPClient("www.sogou.com", 80);

String getResp = httpClient.get("/index.html");

System.out.println(getResp);

String postResp = httpClient.post("/index.html", "this is body");

System.out.println(postResp);

}

}

文章来源

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: