一、HTTP服务器概述
1 - Web工作方式
- 普通的上网过程:浏览器本身是一个客户端,当你输入URL的时候,首先浏览器会去请求DNS服务器,通过DNS获取相应的域名对应的IP,然后通过IP地址找到IP对应的服务器后,要求建立TCP连接,等浏览器发送完HTTP Request(请求)包后,服务器接收到请求包之后才开始处理请求包,服务器调用自身服务,返回HTTP Response(响应)包;客户端收到来自服务器的响应后开始渲染这个Response包里的主体(body),等收到全部的内容随后断开与该服务器之间的TCP连接
- DNS域名服务器(Domain Name Server):进行域名(domain name)和与之相对应的IP地址转换的服务器。DNS中保存了一张域名解析表,解析消息的域名
- HTTP服务器:一个Web服务器也被称为HTTP服务器,它通过HTTP (HyperText Transfer Protocol 超文本传输协议)协议与客户端通信。这个客户端通常指的是Web浏览器(其实手机端客户端内部也是浏览器实现的)
- Web服务器的工作原理
- 客户机通过TCP/IP协议建立到服务器的TCP连接
- 客户端向服务器发送HTTP协议请求包,请求服务器里的资源文档
- 服务器向客户机发送HTTP协议应答包,如果请求的资源包含有动态语言的内容,那么服务器会调用动态语言的解释引擎负责处理“动态内容”,并将处理得到的数据返回给客户端
- 客户机与服务器断开。由客户端解释HTML文档,在客户端屏幕上渲染图形结果
2 - HTTP协议
- HTTP协议:超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议,它详细规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议
- HTTPS:HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS
- 地址(URL):URL全称为Unique Resource Location(统一资源定位符),用来表示网络资源,可以理解为网络文件路径
- 基本URL的结构:包含模式(协议)、服务器名称(IP地址)、路径和文件名。常见的协议/模式如http、https、ftp等。服务器的名称或IP地址后面有时还跟一个冒号和一个端口号。再后面是到达这个文件的路径和文件本身的名称
二、HTTP报文解析
1 - 请求报文格式
package main
import (
"fmt"
"net"
"os"
)
func errFunc(err error, info string) {
if err != nil {
fmt.Println(info, err)
os.Exit(1)
}
}
func main() {
listener, err := net.Listen("tcp", "127.0.0.1:8000")
errFunc(err, "net.Listen err:")
defer listener.Close()
conn, err := listener.Accept()
errFunc(err, "Accpet err:")
defer conn.Close()
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if n == 0 {
return
}
errFunc(err, "conn.Read")
fmt.Printf("|%s|\n", string(buf[:n]))
}
|GET / HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
|
- 请求报文格式说明:HTTP 请求报文由请求行、请求头部、空行、请求包体4个部分组成
- ①.请求方法(空格)请求文件URL(空格)协议版本(\r\n)
- ②.请求头:请求头部为请求报文添加了一些附加信息,由“名/值”对组成,每行一对,名和值之间使用冒号分隔。请求头部通知服务器有关于客户端请求的信息【语法格式 key :value】
- ③.空行:\r\n —— 代表 http请求头结束
- ④.请求包体:请求方法对应的数据内容。 GET方法没有内容!
2 - 简单的HTTP响应服务器
- 创建HTTP响应服务器步骤:借助net/http包,创建一个最简单的服务器,给浏览器回发响应包
- ①.首先注册处理函数http.HandleFunc(),设置回调函数handler
- ②.而后绑定服务器的监听地址http.ListenAndserve()
- 回调函数:本质就是函数指针;通过地址,在某一特定位置,调用函数
- 在程序中,定义一个函数,但不显式调用;当某一条件满足时,该函数由操作系统自动调用
- 测试方法:启动服务器。打开浏览器,在URL中写入127.0.0.1:8000/hello,向服务器发送请求。会在浏览器中看到服务器回发的“hello http”
package main
import (
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello http"))
}
func main() {
http.HandleFunc("/hello", handler)
http.ListenAndServe("127.0.0.1:8000", nil)
}
3 - 测试客户端获取响应包
package main
import (
"fmt"
"net"
"os"
)
func errFunc2(err error, info string) {
if err != nil {
fmt.Println(info, err)
os.Exit(1)
}
}
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:8000")
errFunc2(err, "Dial")
defer conn.Close()
httpRequest := "GET /hello HTTP/1.1\r\nHost:127.0.0.1:8000\r\n\r\n"
conn.Write([]byte(httpRequest))
buf := make([]byte, 4096)
n, _ := conn.Read(buf)
if n == 0 {
return
}
fmt.Printf("|%s|\n", string(buf[:n]))
}
4 - 响应报文格式
- 响应报文组成:HTTP 响应报文由状态行、响应头部、空行、响应包体4个部分组成
- ①.状态行:协议版本号(空格)状态码(空格)状态码描述(\r\n)
- ②.响应头:语法格式 : key :value
- ③.空行:\r\n
- ④.响应包体
- 请求内容存在: 返回请求页面内容
- 请求内容不存在: 返回错误页面描述
- 状态码分类
- 1xx 表示服务器已接收了客户端请求,客户端可继续发送请求
- 2xx 表示服务器已成功接收到请求并进行处理
- 3xx 表示服务器要求客户端重定向
- 4xx 表示客户端的请求有非法内容
- 5xx 表示服务器未能正常处理客户端的请求而出现意外错误
- 常见状态码
三、Go:HTTP服务器
- HTTP服务器实现步骤
- ①.注册回调函数:http.HandleFunc(“/”, myHandler)
- 回调函数:
func myHandler(w http.ResponseWriter, r *http.Request)
- ②.绑定服务器地址结构:http.ListenAndServe(“127.0.0.1:8000”, nil)
- 参2:通常传 ni 。 表示让服务端调用默认的 http.DefaultServeMux 进行处理
- ResponseWriter和Request结构体
type ResponseWriter interface {
Header() Header
Write([]byte) (int, error)
WriteHeader(int)
}
type Request struct {
Method string
URL *url.URL
...
Header Header
Body io.ReadCloser
RemoteAddr string
...
ctx context.Context
}
package main
import (
"fmt"
"net/http"
)
func myHandle(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("this is a Web server"))
fmt.Println("Header:", r.Header)
fmt.Println("URL:", r.URL)
fmt.Println("Method:", r.Method)
fmt.Println("Host:", r.Host)
fmt.Println("RemoteAddr:", r.RemoteAddr)
fmt.Println("Body:", r.Body)
}
func main() {
http.HandleFunc("/", myHandle)
http.ListenAndServe("127.0.0.1:8000", nil)
}
四、Go:HTTP客户端
- HTTP客户端实现步骤
- ①.获取web服务器数据
func Get(url string) (resp *Response, err error)
- ②.defer resp.Body.Close()
- ③.for 循环提取 Body 数据
- Response结构体
type Response struct {
Status string
StatusCode int
Proto string
……
Header Header
Body io.ReadCloser
……
}
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
resp, err := http.Get("http://127.0.0.1:8000/hello")
if err != nil {
fmt.Println("Get err:", err)
return
}
defer resp.Body.Close()
fmt.Println("Status = ", resp.Status)
fmt.Println("StatusCode = ", resp.StatusCode)
fmt.Println("Header = ", resp.Header)
fmt.Println("Body = ", resp.Body)
buf := make([]byte, 4096)
var result string
for {
n, err := resp.Body.Read(buf)
if n == 0 {
fmt.Println("--Read finish!")
break
}
if err != nil && err != io.EOF {
fmt.Println("resp.Body.Read err:", err)
return
}
result += string(buf[:n])
}
fmt.Println("result = ", result)
}