目录
[4.1 简单了解http请求](#4.1 简单了解http请求)
[4.2 URL](#4.2 URL)
[4.3 url encode](#4.3 url encode)
[4.4 认识方法](#4.4 认识方法)
[4.4 请求报头(header)](#4.4 请求报头(header))
[4.4.1 Host](#4.4.1 Host)
[4.4.2 Content-Length](#4.4.2 Content-Length)
[4.4.3 Content-Type](#4.4.3 Content-Type)
[4.4.4 User-Agent (简称UA)](#4.4.4 User-Agent (简称UA))
[4.4.5 Referer(参照页)](#4.4.5 Referer(参照页))
[4.4.6 Cookie(数据)](#4.4.6 Cookie(数据))
[5.1 简单了解http响应](#5.1 简单了解http响应)
[5.2 状态码](#5.2 状态码)
[5.3 响应报头](#5.3 响应报头)
[5.4 响应正文](#5.4 响应正文)
[5.5 通过Java socket构造http请求](#5.5 通过Java socket构造http请求)
[5.6 利用Postman来构造请求](#5.6 利用Postman来构造请求)
[6. https是什么](#6. https是什么)
[7. 什么是加密](#7. 什么是加密)
[8. https的工作过程](#8. https的工作过程)
[8.1 对称加密](#8.1 对称加密)
[8.2 非对称加密](#8.2 非对称加密)
[8.3 中间人攻击](#8.3 中间人攻击)
[8.4 证书](#8.4 证书)
[8.5 数字签名](#8.5 数字签名)
1.如何自定义协议
自定义协议分为两个阶段:明确需要传输那些信息,确定好信息组织的格式。
明确需要传输那些信息,这个是根据具体的业务来确定需要传递那些信息,比如一个外卖平台用户点外卖:
客户端传:用户的位置,用户的id等信息。
服务端传:商家的点名,菜名,图片,评分等信息。
确定好信息的组织方式:
方法一:行文本的方式
每个请求或者响应包含多行,行与行之间按照符号分开。
方法二:通过xml的格式来约定请求和响应数据
xml和html类似,都是通过成对的标签组成键值对的方式来保存数据,但是xml的标签可以自定义,比如:
<request> </request>
<response> </response>
上面两种方法虽然可读性很好,但是数据冗余度太高,网络传输中,消耗很多带宽。
方法三:json方法
类似于下面格式来组织数据:
{
"uesrid":100,
"position":"90E70N"
}
这种方法可读性好,冗余度较少,但是还是存在冗余信息。
方法四:protobuf方法
这种是基于二进制格式组织数据,标识那几个二进制数据连在一起表示一个数据。
这种方法可读性差,但是冗余度最低。
2.http是什么
http(超文本传输协议)是一种应用非常广泛的应用层协议,http是网站用来传输数据的重要协议,基于传输层的TCP协议来实现的。
比如说:我们访问一个搜狗网站,浏览器向搜狗服务器发送一个http请求,搜狗服务器给浏览器返回一个http响应,这个响应就是我们看到的网页。
这里的请求和响应的传输会存在很多条。
3.http协议的格式
http是一个文本格式的协议,我们可以使用抓包软件来查看http的请求和响应的细节。
这里我们使用Fiddler这个软件进行抓包,这个软件是专门用来抓http请求和响应的。
通过上面官网进行下载,下载完之后打开会出现下面页面:

左边区域是所有的http/https的请求和响应,可以点击其中一个查看。
右上区域是http请求的报文内容,切换到Raw可以查看详细信息。
右下角区域是http响应的报文内容,切换到Raw可以查看详细信息。
请求和响应的详细数据可以通过view in notepad来在记事本里面打开。
4.http请求
4.1 简单了解http请求
下面是访问搜狗主页时候的请求报文的内容,使用记事本打开后如下:
**首行:**第一行的内容,包含 请求方法 + url + 版本号。
**请求头(header):**从第二行开始到最后,里面都是键值对的结构,键 + :+ 空格 + 值的结构,里面都是请求的数据,每个键值对之间通过 \n 隔开,以空格表示header结束。
**空行:**header下面的一行空格表示header结束了。
**正文(body):**正文可以为空字符串,如果正文存在,header里面会有一个Content-Length属性来表示正文的长度。

4.2 URL
URL也就是唯一资源定位符,描述了网络上唯一资源的位置,我们可以通过URL来访问唯一资源。
下面是URL的完全写法:

**协议方案名:**也就是协议名字,使用的什么协议。
**登录信息:**现在的网站不再使用该登录信息了,一般都会省略。
**服务器地址:**这个相当于服务器的IP地址或者域名。域名和IP地址之间可以相互转换,通过DNS域名解析系统来进行转换,DNS不仅是应用层协议,还是一套服务器系统。
**服务器端口号:**这个是服务器对应的端口号,用来区分服务器的那个应用程序,http协议的默认端口号是80,https协议的默认的端口号是 443。
**带有层次的文件路径:**这个是想访问服务器某个应用程序管理的某个资源,可能包含子文件。
**查询字符串(Query string):**这个是键值对的结构,键和值之间使用 = 分开,键值对之间使用&分开,这里面包含的是查询资源时候的一些条件。
**片段标识符:**用来区分当前页面的那部分内容。
4.3 url encode
这里的URL里面存在一些特殊符号: ? #之类的,我们在定义query string时候,是存在用户输入的内容的,如果用户输入的内容中使用了这些特殊符号就会造成URL错误,此时我们就需要想个办法解决这个问题。
这里我们可以对用户输入的这些字符进行转义,转义规则使用的是URL encode规则,原理就是将查询字符串里面的值转为二进制形式,然后再转换为十六进制形式,每两个十六进制前面加上%分割开。
比如:我们使用搜狗搜索一个你好,然后请求会显示如下:

我们通过浏览器页面看到的是query=你好,但是实际抓包显示出来的是query=%E4%BD%A0%E5%A5%BD,显示出来你好是为了给用户容易去看,实际发送的请求已经通过url encode转换为十六进制数据了。
4.4 认识方法

上面这些方法表示该请求是要干什么的,最常用的也就是get 和 post 方法。
get请求:
一般get请求是获取服务器上的某个资源的, 获取HTML,获取JS,获取css都是get请求。
浏览器从服务器通过网络获取数据时候,会存在缓存机制,一些不会改变的数据都会存储在本地硬盘里面,还会存储在各地的CDN服务器上面,当为我们第一次获取数据时候,会从服务器或者CDN服务器中访问数据,后面再次访问数据就会现在本地缓存里面找,找不到的再从CDN服务器或者官方服务器里面传输数据。这样可以提高数据传输效率。
get请求一般是没有body的,如果想发送一些数据给服务器,就会将这些数据存储在query string里面发送给服务器,get请求的URL里面的query string部分可以为空,body部分也可以为空,
post请求:
post请求一般用来提交用户的数据给服务器的,主要的应用场景:
登录,登录时候会给服务器传账号和密码等数据。
上传,上传数据时候,会把数据发送给服务器,这些数据存储在body里面。
特点:
首行为post,URL里面的query string 可以为空,header里面存储着键值对,body里面保存着要发送的数据,header里面的Content-Type属性栏里面存储着body里面的数据的存储格式,Content-Length属性存储着body的长度。
get和post的区别:
语义不同:get一般用于获取数据,post一般用于发送数据。
get请求的body通常为空,如果要传输数据通过URL里面的query string来传输数据,post请求的query string一般为空,传输数据通过body来传输。
get请求一般是幂等的,post的请求一般是不幂等的,(多次请求的数据都一样,说明这个请求是幂等的)。
get请求可以进行缓存,post请求不能进行缓存。
补充:
get请求也可以用于发送数据,post请求也可以用于获取数据。
标准上建议get请求是幂等的,但是实际开发中,也可以使用不幂等的get请求。
其他方法:
delete请求用于删除服务器的某个资源。
put请求和get请求一样都是幂等的,一般用于更新。
4.4 请求报头(header)
请求报头里面都是一些键值对的数据,键和值之间通过 :+ 空格 的方式分开。
4.4.1 Host
这个表示服务器主机的IP和端口号。
4.4.2 Content-Length
该属性表示正文的长度,单位是字节,如果http请求没有正文,header就不会有这个属性。
http协议版本号<=2.0时候,传输层使用的是TCP协议,该协议在传输数据时候,客户端和服务器建立一次连接,可以传很多次数据,如何区分不同的http协议呢?
TCP协议在读取http请求时候,先读取首行,请求头,空格等内容,然后根据请求头里面的Content-Length属性来判断正文需要读取多长。通过这种方法让服务器能够区分不同的请求。
4.4.3 Content-Type
该属性表示正文的数据格式,提示接收方如何解析body中的数据,如果没有正文就不会有该属性。
传送HTML数据:Content-Type: text/html; charset=utf-8
传送CSS数据:Content-Type: text/css;
传送JS数据:Content-Type: application/javascript;
传送JSON数据:Content-Type: application/json; charset=utf-8;
传送图片:Content-Type: application/x-protobuf;
4.4.4 User-Agent (简称UA)
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0
这个属性是用来告诉服务器,当前客户端使用的什么浏览器,什么设备,什么系统。
Mozilla/5.0:是沿用早起浏览器的习惯。
Windows NT 10.0; Win64; x64;:操作系统是Windows10,系统是64位。
rv:145.0:引擎版本号
Gecko/20100101 :引擎名字。
Firefox/145.0:使用那个浏览器,及版本号。
可以根据浏览器版本和操作系统版本,来返回相应的网页。
可以根据使用设备的操作系统,返回不同大小的网页。
4.4.5 Referer(参照页)
表示这个页面是从那个页面跳转过来的。
如果我们直接搜索一个网址是没有Referer的,只有通过一个网页跳转到另外一个网页,新的网页才有。
4.4.6 Cookie(数据)
cookie是浏览器允许网页在本地硬盘中存储一些数据的机制,这里不是让网页直接访问本地硬盘,而是通过cookie来去存储数据,cookie定义键值对的数据存储方式,网页将数据以键值对的形式交给cookie,然后cookie再存储到硬盘中。


cookie这里存储的数据从服务器传过来,让发出请求时候又会将这些数据放到http请求的header中的cookie属性里面发送给服务器,让服务器知道用户的身份,相当于唯一标识。
cookie的应用场景:
登录和用户认证:
用户输入账号和密码之后,然后将http请求发送给服务器,服务器在数据库中查找该用户的信息,找到之后,生成一个sessionId,生成session对象,把用户的关键信息保存到session对象里面,把sessionId和session对象通过键值对的形式保存到内存中,然后将sessionId通过响应里面的 Set-Cookie属性传输给客户端,保存在本地硬盘中。
后续客户端再发送登录请求时候,服务器直接根据sessionId属性来判断是哪个用户,用户不需要再重新登陆了,服务器可以设置cookie里面存储键值对的过期时间的,如果过期了,再次登录就需要重新登陆了。
5.http响应
5.1 简单了解http响应
下面是打开搜狗主页时候的响应报文的内容,通过记事本打开如下:
**首行:**版本号 + 状态码 + 状态码解释。
**响应头(header):**键值对结构,使用\n分割键值对。
**空行:**使用空行来表示header结束。
**正文:**正文内容可以为空字符串,如果正文内容存在,则在响应头里面会包含一个Content-Length属性来表示正文的长度。如果响应返回的是一个HTML页面,此时正文内容就是HTML的数据。

5.2 状态码
状态码是访问一个网页的结果,成功或者失败或者其他的情况。

常用的状态码:
200:表示成功。
404:表示访问的资源没找到,URL存在问题,URL包括 IP, 端口号,然后是资源路径,这个资源路径的资源不存在就会404.
403:访问被拒绝,没有权限访问该资源。
405:请求的方法,和服务器声明的注解的方法不匹配。
500:服务器内部出现错误,可能是服务器出现异常没有捕获到,或者服务器崩溃等原因。
504:服务器负荷比较大,处理单条请求时间会很长,长时间后就会显示504。
302:临时重定向,访问旧的域名会跳转到新的域名,旧地址未来可能还会用
301:永久重定向,当浏览器收到这种请求后,以后再访问这个地址就会定位的新的地址,旧地址不会再使用。
状态码总结:

5.3 响应报头
响应报头跟请求报头类似。
Content-Type:
该属性表示正文的数据格式,如果没有正文就不会有该属性。
传送HTML数据:Content-Type: text/html; charset=utf-8
传送CSS数据:Content-Type: text/css;
传送JS数据:Content-Type: application/javascript;
传送JSON数据:Content-Type: application/json; charset=utf-8;
传送图片:Content-Type: application/x-protobuf;
5.4 响应正文
正文的格式取决于Content-Type,不同的格式对应不同的正文。
5.5 通过Java socket构造http请求
发送请求是按照http的格式构造的字符串,传入到TCP的socket里面。
接收请求是从TCP的socket里面拿到请求,按照http的格式来解析。
构造一个简单的客户端程序来发送各种类型的http请求:
java
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* Created with IntelliJ IDEA.
* Description:
* User: 87111
* Date: 2025-11-26
* Time: 10:06
*/
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 {
StringBuilder request = new StringBuilder();
//构造首行
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[] response = new byte[1024 * 1024];
int n = inputStream.read(response);
return new String(response, 0, n, "utf-8");
}
public String post(String url, String body) throws IOException {
StringBuilder request = new StringBuilder();
//构造首行
request.append("Post " + url + " HTTP/1.1\n");
//构造header
request.append("Host: " + ip + ":" + port + "\n");
request.append("Conten-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[] response = new byte[1024 * 1024];
int n = inputStream.read(response);
return new String(response, 0, n, "utf-8");
}
public static void main(String[] args) throws IOException {
HttpClient httpClient = new HttpClient("127.0.0.1",8080);
String getResp = httpClient.get("/AjaxMockSServer/info");
System.out.println(getResp);
String postResp = httpClient.post("/AjaxMockSServer/info", "this is body");
System.out.println(postResp);
}
}
5.6 利用Postman来构造请求
官方网址:https://www.postman.com/downloads/
下载后就可以使用这个软件来构造请求。
6. https是什么
https也是一个应用层协议,实在http协议的基础上,引入了一个加密层,http协议是明文传输的,经过的服务器都是可以更改这个协议内容的,这样不安全,而https是1对这样的明文内容进行加密处理。
7. 什么是加密
加密就是把明文内容经过一系列的变化为密文。
解密就是把密文通过一系列的变化成明文。
8. https的工作过程
加密的方式有很多种,对称加密和非对称加密。
8.1 对称加密
对称加密:
就是通过一个密钥,把明文加密成密文,把密文解密成明文,比如说:按位异或就是一个对称加密。
同一个服务器需要给多个客户端提供服务,因此每个客户端的秘钥都是不相同的。而我们需要告诉服务器这个秘钥,通过请求传输,但是这样黑客就知道你的秘钥了,就可以解密,这样还是不安全,那就需要对秘钥进行加密,这样就陷入了先有鸡,还是先有蛋的问题了。

8.2 非对称加密
非对称加密:
非对称秘钥要用到两个秘钥:一个是公钥,一个是私钥。
公钥和秘钥是配对的,我们可以使用:
使用公钥来加密数据。使用私钥来解密数据。
或者
使用私钥来加密数据。使用公钥来解密数据。
但是非对称秘钥运算速度慢,开销大,对大量数据加密操作复杂,所以,一般都是对称秘钥和非对称秘钥混合使用。

客户端在本地生成一个对称秘钥,然后把数据和对称秘钥通过公钥加密,传输给服务器。
服务器接收到密文后,通过私钥对密文进行解析,然后得到内容和对称秘钥。
服务器后续再跟客户端发送数据,通过对称秘钥来进行加密。
对于上述加密过程,黑客还可以通过中间人攻击来破解。
8.3 中间人攻击
对于上述的加密过程,还是存在安全隐患的。
假设服务器有公钥S,私钥S1,黑客有公钥M,私钥M1。
此时客户端给服务器发起请求,请求获取公钥S,然后服务器发出响应包含公钥S。
黑客劫持到响应,保存公钥S,然后将响应中的公钥替换成M,发送给客户端。
客户端拿到公钥M后,就会利用公钥M来加密自己设置的对称密钥K发出请求。
黑客截取到请求后,利用私钥M1解密,获取到对称密钥K,再利用公钥S对K加密,发送给服务器。
服务器接收到后,使用私钥S1解密,获取到对称密钥K。
然后客户端和服务器后续就会使用对称密钥K来进行加密通信,但是此时黑客已经拿到对称密钥K了。
通过这种中间人攻击的方法,可以实现数据的破解。
上面的问题主要原因还是客户端不能区别传过来的公钥是服务器的,还是黑客的,此时就引出了证书的概念。
8.4 证书
服务器在使用https协议时候,需要向第三方机构申请一份电子证书,这份证书包括了服务器的公钥,客户端可以直接从证书里面获取到服务器的公钥。
证书里面的公钥为什么是安全的呢?
证书里面包括:证书的颁发机构,证书的有效期,服务器的公钥,服务器的域名,数字签名等信息。
这里的关键信息就是这个数字签名:
8.5 数字签名
数字签名相当于是一个加密的校验和,校验和就是通过证书里面除了数字签名之外的内容,通过一种计算规则来计算出来的一个数值,然后第三方机构对这个证书有一个公钥P1 和私钥 P2,利用私钥P2对这个校验和进行加密得到数字签名。
此时客户端会先向服务器要证书,得到证书后通过同样的算法计算出校验和1。
然后服务器再利用第三方机构提供的公钥对数字签名进行解码得到校验和2。
服务器通过比较这两个校验和是否相同来判断数据是否被篡改过。
这里的第三方机构的公钥不是通过网络传输获取的,而是内置在操作系统中的。
这样就实现了通过证书来解决中间人攻击问题:
