http包详解
http包运行机制
基础概念:
- Request:用户请求的信息,用来解析用户的请求信息,包括post、get、cookie、url等信息
- Response:服务器需要反馈给客户端的信息
- Conn:用户的每次请求链接
- Handler:处理请求和生成返回信息的处理逻辑
工作流程:
- 创建Listen Socket, 监听指定的端口, 等待客户端请求到来。
- Listen Socket接受客户端的请求, 得到Client Socket, 接下来通过Client Socket与客户端通信。
- 处理客户端的请求, 首先从Client Socket读取HTTP请求的协议头, 如果是POST方法, 还可能要读取客户端提交的数据, 然后交给相应的handler处理请求, handler处理完毕准备好客户端需要的数据, 通过Client Socket写给客户端。
监听端口
初始化一个server对象,然后调用了net.Listen("tcp", addr)
,底层用TCP协议搭建服务,监控设置的端口。
1 | func (srv *Server) Serve(l net.Listener) error { |
接收请求
调用了srv.Serve(net.Listener)
函数,这个函数就是处理接收客户端的请求信息。这个函数里面起了一个for{}
,首先通过Listener接收请求,其次创建一个Conn,最后单独开了一个goroutine,把这个请求的数据当做参数扔给这个conn去服务:go c.serve()
。
分配handler
conn首先会解析request:c.readRequest()
,然后获取相应的handler:handler := c.server.Handler
,也就是在调用函数ListenAndServe
时候的第二个参数,传递的是nil,也就是为空,那么默认获取handler = DefaultServeMux
,这个变量就是一个路由器,它用来匹配url跳转到其相应的handle函数,调用http.HandleFunc("/", sayhelloName)
,这个作用就是注册了请求/
的路由规则,当请求uri为”/“,路由就会转到函数sayhelloName,DefaultServeMux会调用ServeHTTP方法,这个方法内部其实就是调用sayhelloName本身,最后通过写入response的信息反馈到客户端。
流程
客户端的每次请求都会创建一个Conn,这个Conn里面保存了该次请求的信息,然后再传递到对应的handler,该handler中便可以读取到相应的header信息,这样保证了每个请求的独立性。
路由器
1 | type ServeMux struct { |
muxEntry:
1 | type muxEntry struct { |
Handler的定义:
1 | type Handler interface { |
http包里面还定义了一个类型HandlerFunc
,定义的函数sayhelloName
就是这个HandlerFunc调用之后的结果,这个类型默认就实现了ServeHTTP这个接口,即调用HandlerFunc(f),强制类型转换f成为HandlerFunc类型,这样f就拥有了ServeHTTP方法。
1 | type HandlerFunc func(ResponseWriter, *Request) |
请求分发
默认的路由器实现了ServeHTTP
:
1 | func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { |
调用mux.Handler(r)
返回对应设置路由的处理Handler,然后执行h.ServeHTTP(w, r)
也就是调用对应路由的handler的ServerHTTP接口。
mux.Handler(r)返回过程:
1 | func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { |
根据用户请求的URL和路由器里面存储的map去匹配的,当匹配到之后返回存储的handler,调用这个handler的ServeHTTP接口就可以执行到相应的函数。
总结
首先调用Http.HandleFunc()
1 调用了DefaultServeMux的HandleFunc
2 调用了DefaultServeMux的Handle
3 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则
调用http.ListenAndServe()
1 实例化Server
2 调用Server的ListenAndServe()
3 调用net.Listen(“tcp”, addr)监听端口
4 启动一个for循环,在循环体中Accept请求
5 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()
6 读取每个请求的内容w, err := c.readRequest()
7 判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux
8 调用handler的ServeHttp