HTTP协议概述
1. HTTP协议基础
- HTTP: Hypertext Transfer Protocol-超文本传输协议
- HTTP 是一种无状态、应用层协议,基于TCP/IP,默认端口为80(HTTPS为443)
- 无连接 每次请求/响应后连接通常关闭,HTTP/1.0默认如此,HTTP/1.1支持长久连接
- 无状态 服务器不保留客户端请求状态,如需保存状态可通过Cookie, Session等机制维持状态
| HTTP(应用层) |
|---|
| SSL/TLS |
| TCP |
| IP |
HTTP + 加密 + 认证 + 完整性保护 = HTTPS(HTTP Secure)
2. HTTP请求结构
一个HTTP请求由三部分组成:
- 请求行
- 请求头
- 请求体
POST /login HTTP/1.1 //请求行
Content-Type: application/json //请求头
Cookie: name1=value1; name2=value2 //Cookie
{"username":"admin","password":"123456"} //请求体
用一个表格展示组成结构
2.1 请求方法
| 方法名称 | 概述 |
|---|---|
| GET | 请求Request-URI所标识的资源 |
| POST | 向URI提交数据(如提交表单或上传数据) |
| HEAD | 类似GET,但返回响应中没有具体内容,用于获取报头 |
| PUT | 对服务器上已存在的资源进行更新 |
| DELETE | 请求服务器删除指定页面 |
| CONNECT | HTTP/1.1预留,能够将连接改为管道方式的代理服务器 |
| OPTIONS | 查看服务端性能 |
| TRACE | 回显服务器收到请求,主要用于测试或诊断 |
| PATCH | 类似PUT,只对资源一部分更新,如果资源不存在会创建 |
2.2 URL
http://wwww.example.com:8080/news/tech/12345.html?id=111&name=aaa#pic
| | | | | | |
协议 域名 端口 路径 文件名 参数 锚点
2.3 请求头
| Header | 概述 | 示例 |
|---|---|---|
| Accept | 指定客户端能够接收的内容类型 | Accept:text/plain, text/html |
| Accept-Charset | 客户端可以接受的字符编码集 | Accept-Charset:iso-8859-5 |
| Accept-Encoding | 客户端可支持的压缩编码类型 | Accept-Encoding:compress, gzip |
| Accept-Language | 客户端可接受的语言 | Accept-Language:en, zh |
| Authorization | HTTP授权的证书 | Authorization: Basic YWRtaW46MTIzNDU2 |
| Cache-Control | 指定请求和响应的缓存机制 | Cache-Control:no-cache |
| Connection | 判定是否需要持久连接(HTTP1.1默认进行持久连接) | Connection:close |
| Cookie | HTTP请求发送时,保存在该请求域名下的所有cookie值一起发送给服务器 | Cookie:$Version=1;Skin=new; |
| Content-Length | 请求内容长度 | Content-Length:348 |
| Content-Type | 指定请求体的数据格式 | Content-Type:application/x-www-form-urlencoded |
| User-Agent | 浏览器信息 | Chrome/3.0(Windows NT 6.1;Win 64;x64) |
2.4 请求体
POST/post?id=aaa&page=12 HTTP/1.1
Content-Type:application/x-www-form-urlencoded
name=LiMing&message=how_are_you //请求体
Content-Type
| 类型 | 概述 | 示例 |
|---|---|---|
| appplication/x-www-form-urlencoded | 浏览器原生fom表单,不过不设置Content-Type,默认以该类型输出 | name=LiMing&message=how_are_you |
| multipart/form-data | 上传文件时使用,支持多种文件格式 | name="test"name="file";filename="Edge.png"Content-type:img/png...content of Edge.png |
| application/json | 发的请求体(body)是一段JSON文本,按JSON的格式去解析它 | {"title":"test", "sub":[1,2,3,4,5]} |
| text/xml | 发的请求体(body)是xml,请用xml解析器处理 |
3. HTTP响应结构
一个HTTP响应结构由三部分组成:
- 响应行
- 响应头
- 响应体
HTTP/1.1 200 OK //响应行
Content-Type: application/json; charset=utf-8 响应头
{"status":"OK","data":{"orderId":"ORD-20251108-001","amount":299.99,"currency":"CNY"}} //响应体
3.1 状态及话术
| 状态类别 | 状态码 | 状态名称 | 适用场景 | 标准话术模板 |
|---|---|---|---|---|
| 信息响应 | 100 | Continue | 客户端应继续发送请求 | "请求已接收,请继续发送剩余数据。" |
| 101 | Switching Protocols | 协议切换 | "正在切换协议,请稍候。" | |
| 成功响应 | 200 | OK | 请求成功处理 | "您的请求已成功处理,结果如下:[数据/信息]" |
| 201 | Created | 资源创建成功 | "资源创建成功,新资源已生成。" | |
| 204 | No Content | 请求成功但无返回内容 | "请求已成功处理,无需返回内容。" | |
| 重定向 | 301 | Moved Permanently | 资源永久移动 | "资源已永久移动至新地址:[新URL]" |
| 302 | Found | 资源临时移动 | "资源临时移动,请访问:[临时URL]" | |
| 304 | Not Modified | 资源未修改 | "资源未更新,可使用缓存版本。" | |
| 客户端错误 | 400 | Bad Request | 请求语法错误 | "请求格式有误,请检查输入参数后重试。" |
| 401 | Unauthorized | 未授权访问 | "请先登录或获取访问权限后再尝试。" | |
| 403 | Forbidden | 服务器拒绝访问 | "您无权访问此资源,请联系管理员。" | |
| 404 | Not Found | 资源不存在 | "抱歉,未找到您请求的资源,请确认URL是否正确。" | |
| 429 | Too Many Requests | 请求过于频繁 | "您的请求过于频繁,请稍后再试。" | |
| 服务器错误 | 500 | Internal Server Error | 服务器内部错误 | "服务器暂时无法处理请求,请稍后再试或联系技术支持。" |
| 502 | Bad Gateway | 网关错误 | "网络连接异常,请检查网络设置或稍后重试。" | |
| 503 | Service Unavailable | 服务器不可用 | "服务暂时不可用,正在紧急修复中,请稍后再试。" | |
3.2 响应头
| Header | 概述 | 示例 |
|---|---|---|
| Cache-Control | 控制缓存策略 | Cache-Control: max-age=3600, must-revalidate |
| Content-Type | 响应体 MIME 类型及编码 | Content-Type: application/json; charset=utf-8 |
| Content-Length | 响应体字节长度 | Content-Length: 4213 |
| Content-Encoding | 响应体压缩算法 | Content-Encoding: gzip |
| Location | 重定向目标地址 | Location: example.com/new-path |
| Access-Control-Allow-Origin | CORS 允许源 | Access-Control-Allow-Origin: app.example.com |
| Strict-Transport-Security | 强制 HTTPS(HSTS) | Strict-Transport-Security: max-age=63072000; includeSubDomains |
| X-Content-Type-Options | 禁用 MIME 嗅探 | X-Content-Type-Options: nosniff |
Go语言中HTTP请求处理的底层实现
1. 工作流程
客户端发起HTTP请求,通过Go语言实现服务器监听、接收、处理并返回响应。底层工作流程如下:
a. 创建Listen Socket, 监听指定端口,等待客户端请求;
b. Listen Socket接收客户端的请求, 得到Client Socket,通过Client Socket与客户端通信;
c. 处理客户端请求,读取Client Socket的请求头,如果是POST方法,则读取客户端提交的数据,然后交给Handler(处理器)处理,Handler处理完毕后转载完客户端需要的数据,通过Client Socket返回给客户端;
d. 生成响应并传回给客户端;
f. 关闭连接。
2. 创建Listen Socket监听端口
使用Golang的net包,用Listen函数创建。示例代码如下:
package main
import (
"fmt"
"net"
)
func handleConnection(conn net.Conn) {
defer conn.Close()
// 处理连接请求
}
func main(){
listen, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("Failed to listen", err)
}
defer listen.Close()
fmt.Println("Listening 8080")
for {
conn, err := listen.Accept()
if err != nil {
fmt.Println("Failed to accept connection:", err)
continue
}
go handleConnection(conn)
}
}
在这段代码里,我们使用net.Listen函数创建了一个TCP的Listen Socket, 监听端口为8080。如果监听失败,则会打印错误信息退出。for循环中,通过listen.Accept接收客户端连接,并将连接交给handleConnection函数进行处理。
3. 接收客户端请求并建立连接
当Listen Socket监听到客户端的连接请求后,通过istener.Accept方法与客户端建立连接。
func handleConnection(conn net.Conn) {
defer conn.Close()
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
fmt.Println("Failed to read request:", err)
return
}
request := string(buffer[:n])
fmt.Println("Received request:", request)
// 处理请求
}
通过conn.Read方法从连接中读取请求数据,并将其转换为字符串。
4. 处理客户端请求并返回响应
接上段代码
//处理请求
response := "HTTP/1.1 200 OK\r\nContent-Length:12\r\nHello, World!"
_, err = conn.Write([]byte(response))
if err != nil {
fmt.Println("Failed to response:", err)
return
}
fmt.Println("Sent response:", response)
}
通过conn.Write方法将响应发送给客户端。具体响应格式可以翻看前面 ## 3.3响应结构
完整代码展示
package main
import (
"fmt"
"net"
)
func handleConnection(conn net.Conn) {
defer conn.Close()
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
fmt.Println("Failed to read request:", err)
return
}
request := string(buffer[:n])
fmt.Println("Received request:", request)
// 处理请求
response := "HTTP/1.1 200 OK\r\nContent-Length:12\r\nHello, World!"
_, err := conn.Write([]byte(response))
err != nil {
fmt.Println("Failed to response:", err)
return
}
fmt.Println("Sent response:", response)
}
func main() {
listen, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("Failed to listen", err)
}
defer listen.Close()
fmt.Println("Listening 8080")
for {
conn, err := listen.Accept()
if err != nil {
fmt.Println("Failed to accept connection:", err)
continue
}
go handleConnection(conn)
}
}
在这个示例中,我们创建了一个Listen Socket来监听端口8080, 通过for循环不断接受客户端连接。每当接收到连接时,会创建一个goroutine来处理请求。处理完请求之后,生成响应并返回给客户端。