Web工作原理
首先,我们来了解一次网页请求的过程。当我们使用浏览器打开一个网页时:
1.浏览器通过自身的DNS缓存进行查找是否存在未过期的DNS记录
2.如果没有查找到的话会会向操作系统自身的DNS缓存查找记录,如果仍未查找到会从本地的HOST文件中查找。
3.如果上述三个地方都没有查找到DNS缓存,浏览器会以系统的名义向本地宽带运营商的DNS服务器发起一个域名解析的请求:在这次请求中本地DNS服务器会检查缓存是否存在该记录,如果不存在则会进行迭代或轮询的DNS解析请求。最后返回给浏览器IP地址。
4.浏览器通过IP地址进行TCP连接三次握手
5.建立连接后发送HTTP请求
6.服务器收到请求后根据路径参数等以及一些处理将结果返回给浏览器
7.浏览器拿到代码进行解析,渲染。
net/http包
执行流程
1.创建Listen socket,监听指定端口,等待客户请求
2.接收客户请求,得到Client socket,接下来通过Client socket与客户端进行通信
3.处理客户请求,从Client socket读取HTTP请求的协议头,如果是POST,还要读取客户端请求的数据,然后交给响应的handler处理请求,handler处理完毕通过Client socket将数据发送给客户端

ListenAndServe函数
这个函数初始化了一个server对象,然后调用了net.Listen('tcp", addr)搭建了TCP协议的一个服务,然后监控我们设置的端口。
然后调用serve函数,在这个函数中无限循环accept,当接收到请求后创建一个Conn,最后单独开了一个goroutine对这个连接进行服务(go c.serve())。
server的serve函数
func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
var tempDelay time.Duration // how long to sleep on accept failure
for {
rw, e := l.Accept()
if e != nil {
if ne, ok := e.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay = 0
c, err := srv.newConn(rw)
if err != nil {
continue
}
go c.serve()
}
}
c.serve()处理请求
1.解析request:c.readRequest()
2.获取响应的handler(ListenAndServe函数的第二个参数是一个路由器,如果这个参数为nil则默认获取DefaultServeMux,这个路由器可以根据url跳转到响应的handler)
3.使用handler处理
http. HandleFunc("/", hello)
这个函数用来设置路由器规则,例如当匹配了/则使用hello函数
函数流程:
1.调用了DefaultServeMux的HandleFunc
2.调用了DefaultServeMux的Handle
3.往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则
ServeMux自定义
type ServeMux struct {
mu sync.RWMutex //锁,由于请求涉及到并发处理,因此这里需要一个锁机制
m map[string]muxEntry // 路由规则,一个string对应一个mux实体,这里的string就是注册的路由表达式
hosts bool // 是否在任意的规则中带有host信息
}
type muxEntry struct {
explicit bool // 是否精确匹配
h Handler // 这个路由表达式对应哪个handler
pattern string //匹配字符串
}
如果自定义了ServeMux则会根据路由器选择响应的handler,如果没有定义,即ListenAndServe的第二个参数为nil,但是HandlerFunc函数会生成一个HandlerFunc类型,这个类型实现了ServeHTTP接口
简易服务器
package main
import (
"fmt"
"net/http"
)
type MyMux struct {
}
func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
sayhelloName(w, r)
return
}
http.NotFound(w, r)
return
}
func sayhelloName(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello myroute!")
}
func main() {
mux := &MyMux{}
http.ListenAndServe(":9090", mux)
}
执行http请求
GET请求
1.直接使用net/http包内的函数请求
resp, err := http.Get("https://www.baidu.com")
2.利用http.client结构体来请求
clt := http.Client{}
resp, err := clt.Get("http://wwww.baidu.com")
两种请求的本质都是
req, err := http.NewRequest("GET", "http://www.baidu.com", nil)//生成一个request实体
resp, err := http.DefaultClient.Do(req)
POST请求
1.使用net/http包自带的函数
data := url.Values{"start":{"0"}, "offset":{"xxxx"}}
body := strings.NewReader(data.Encode())
resp, err := http.Post("xxxxxxx", "application/x-www-form-urlencoded", body)
或者
data := url.Values{"start":{"0"}, "offset":{"xxxx"}}
http.PostForm("xxxx", data)
2.使用http.client来请求
data := url.Values{"start":{"0"}, "offset":{"xxxx"}}
body := strings.NewReader(data.Encode())
clt := http.Client{}
resp, err := clt.Post("xxxxxxx", "application/x-www-form-urlencoded", body)
两种请求的本质也是使用NewRequest创建一个request结构体
data := url.Values{"start":{"0"}, "offset":{"xxxx"}}
body := strings.NewReader(data.Encode())
req, err := http.NewRequest("POST", "xxxxx", body)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
clt := http.Client{}
clt.Do(req)
如果要添加header,则还是用NewRequest方法吧,并且调用req.Header.Set(key, value)
request/response结构体
request
type Request struct {
Method string //HTTP method (GET, POST, PUT, etc.)
URL *url.URL // 见下面URL定义
Proto string // http协议类型,如 HTTP/1.1,HTTP/2
ProtoMajor int // 1
ProtoMinor int // 0
Header Header // 请求头,map[string][]string
Body io.ReadCloser // http请求body post请求的内容就放在这里
GetBody func() (io.ReadCloser, error) // 获取body的方法
ContentLength int64 // 描述HTTP消息实体的传输长度
TransferEncoding []string //传输编码, 如chunk
Close bool
Host string // 主机地址,IP或域名
Form url.Values //URL和参数的map
PostForm url.Values //POST, PATCH, or PUT body 参数
MultipartForm *multipart.Form
Trailer Header
RemoteAddr string
RequestURI string //通常就是URL
TLS *tls.ConnectionState
Cancel <-chan struct{}
Response *Response
ctx context.Context
}
----------------------------------------------
URL定义:
type URL struct {
Scheme string
Opaque string // encoded opaque data
User *Userinfo // username and password information
Host string // host or host:port
Path string // path (relative paths may omit leading slash)
RawPath string // encoded path hint (see EscapedPath method)
ForceQuery bool // append a query ('?') even if RawQuery is empty
RawQuery string // encoded query values, without '?'
Fragment string // fragment for references, without '#'
}
resposne
type Response struct {
Status string // e.g. "200 OK"
StatusCode int // e.g. 200
Proto string // e.g. "HTTP/1.0"
ProtoMajor int // e.g. 1
ProtoMinor int // e.g. 0
Header Header //响应头
Body io.ReadCloser //响应内容
ContentLength int64 //HTTP消息实体的传输长度
TransferEncoding []string
Close bool
Uncompressed bool
Trailer Header
Request *Request
TLS *tls.ConnectionState
}
net包
socket编程
TCP server
package main
import (
"fmt"
"net"
"os"
"time"
)
func main() {
service := ":9093"
tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
conn, err := listener.Accept()
if err != nil {
continue
}
go handleClient(conn)
}
}
func handleClient(conn net.Conn) {
defer conn.Close()
daytime := time.Now().String()
b := make([]byte, 1024)
conn.Read(b)
conn.Write([]byte(daytime)) // don't care about return value
// we're finished with this client
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)
}
}
TCP client
package main
import (
"fmt"
"io/ioutil"
"net"
"os"
)
func main() {
if len(os.Args) != 3 {
fmt.Fprintf(os.Stderr, "Usage: %s host:port ", os.Args[0])
os.Exit(1)
}
service := os.Args[1]
tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
checkError(err)
conn, err := net.DialTCP("tcp", nil, tcpAddr)
checkError(err)
_, err = conn.Write([]byte(os.Args[2]))
checkError(err)
result, err := ioutil.ReadAll(conn)
checkError(err)
fmt.Println(string(result))
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
os.Exit(1)
}
}