这个函数是http里最重要的一个函数,或者说是服务端代码整合的最终灵魂。
httpListenAndServe(port string, handler Handler)
第一个参数是监听的端口、第二个参数是根页面的处理函数,可以为空。
接下来是它做了些什么。
首先看源码吧还是
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
首先创建了一个server对象(但是我们拿不到它的引用),调用了server的ListenAndServe()方法
这个server对象,代表了一个正在跑的HTTP协议的Server程序。
addr代表它监听的端口,handler代表它的根页面使用的handler处理对象
server.ListenAndServe()方法(注意啊,这个是server对象的方法)
func (srv *Server) ListenAndServe() error {
if srv.shuttingDown() {//这儿是指的这个server是不是被关了
return ErrServerClosed
}
addr := srv.Addr//取出server的监听端口号
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)//使用端口号监听端口,监听端口返回了一个listen对象
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
type tcpKeepAliveListener struct {
*net.TCPListener
}
目前已经监听了端口了,并且获得了监听端口之后返回的listen对象。并通过listen对象
创建了一个tcpKeepAliveListener(tcp长时间监听的listener)
然后调用serve的Serve方法
func (srv *Server) Serve(l net.Listener) error {
.............................//忽略了一堆判断的特殊的情况,我们就看下面就好
for {
rw, e := l.Accept()//通过tcplistener(tcp监听器)接受来自客户端的请求,并返回一个Conn
if e != nil {//判断是否发生错误的,忽略,直接跳到最下面
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
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
}
srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay = 0//跳到这儿了,如果e == nil 的话,没有发生错误的情况
c := srv.newConn(rw)//通过listener返回的Conn创建一个http包的Conn
c.setState(c.rwc, StateNew) // 设置状态码
go c.serve(ctx)//通过http的Conn创建一个协程来单独处理这个Conn,ctx是server的信息。在上面定义的
}
}
以下是newConn
func (srv *Server) newConn(rwc net.Conn) *conn {
c := &conn{//通过Server和rwc创建的连接
server: srv,
rwc: rwc,
}
if debugServerConnections {
c.rwc = newLoggingConn("server", c.rwc)
}
return c
}
总:创建Server(代表应用程序),监听接口,获取listener、listener接受客户端发送来的消息并返回一个net包的conn,通过这个conn,serve创建一个http包的conn,并且这个conn内有serve的引用。
然后开启这个conn的协程。在conn的协程里做所有的处理操作
go c.serve(ctx)所创建的协程,里面做了很多操作,比如request、respose、responseWriter
等对象的创建,信息的整合,等等等等,最重要的是,将本次请求的url对应到Serve的Hander的map中,并调用它。
获取Conn内部的url去Serve的Hand的Map集合中去匹配。匹配到的就调用那个处理方法,
我们创建协程是在当listener接收到请求之后才创建的协程,而listener之前的操作都在main协程中操作,但是我们如果不请求的话,程序就会卡再listener.Accept()处,所以我们的http.ListenAndServer应该放在程序的最后。所有的准备操作都应该再ListenerAddServe操作之前执行。
接下来我们看一下路由(可以看作是一个map,每次请求都会到这个map找对应的Handler)的结构
以下是我们程序创建的Serve的其中一个字段:handler的map
http包下的DefaultServeMux//默认的Serve路由集合
http包有DefaultServeMux对象,该对象有一个map[url]Entry,Entry内部放着handler,
serve创建的处理请求的协程会去DefaultServeMux中的map里通过url进行匹配,找到对应的handler处理函数。
var DefaultServeMux = &defaultServeMux
//这是它的字段
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry//url及muxEntry
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // whether any patterns contain hostnames
}
type muxEntry struct {//muxEntry的字段
h Handler,这个Handler就是我们的一个处理函数对象,它被包在了一个muxEntry中,放入ServeMux的map 中
pattern string
}