比如我们经常会有这样的代码开始一个网络端口监听:
err := http.ListenAndServe(listenAddr, nil)
http包中这个函数的实现是:
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
server.ListenAndServe() 函数的实现如下:
func (srv *Server) ListenAndServe() error {
addr := srv.Addr
if addr == "" {
addr = ":http"
}
l, e := net.Listen("tcp", addr)
if e != nil {
return e
}
return srv.Serve(l)
}
srv.Serve( 函数的实现如下,注意看到有网络连接产生后 c, err := srv.newConn(rw) ,我们开了一个协程。
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
if srv.ReadTimeout != 0 {
rw.SetReadDeadline(time.Now().Add(srv.ReadTimeout))
}
if srv.WriteTimeout != 0 {
rw.SetWriteDeadline(time.Now().Add(srv.WriteTimeout))
}
c, err := srv.newConn(rw)
if err != nil {
continue
}
go c.serve()
}
panic("not reached")
}
从上面可以看到,每个客户端请求连接,都是开始了一个协程。
每次请求的实体类包含的信息如下:
type conn struct {
remoteAddr string // network address of remote side
server *Server // the Server on which the connection arrived
rwc net.Conn // i/o connection
lr *io.LimitedReader // io.LimitReader(rwc)
buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->rwc
hijacked bool // connection has been hijacked by handler
tlsState *tls.ConnectionState // or nil when not using TLS
body []byte
}
c.serve( 方法包含整个连接从打开到关闭的整个生命周期,即,一个连接是一个协程。
// Serve a new connection.
func (c *conn) serve() {
defer func() {
err := recover()
if err == nil {
return
}
var buf bytes.Buffer
fmt.Fprintf(&buf, "http: panic serving %v: %v\n", c.remoteAddr, err)
buf.Write(debug.Stack())
log.Print(buf.String())
if c.rwc !=nil { // may be nil if connection hijacked
c.rwc.Close()
}
}()
if tlsConn, ok := c.rwc.(*tls.Conn); ok {
if err := tlsConn.Handshake(); err != nil {
c.close()
return
}
c.tlsState = new(tls.ConnectionState)
*c.tlsState = tlsConn.ConnectionState()
}
for {
w, err := c.readRequest()
if err != nil {
msg := "400 Bad Request"
if err == errTooLarge {
// Their HTTP client may or may not be
// able to read this if we're
// responding to them and hanging up
// while they're still writing their
// request. Undefined behavior.
msg = "413 Request Entity Too Large"
} else if err == io.EOF {
break // Don't reply
} else if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
break // Don't reply
}
fmt.Fprintf(c.rwc, "HTTP/1.1 %s\r\n\r\n", msg)
break
}
// Expect 100 Continue support
req := w.req
if req.expectsContinue() {
if req.ProtoAtLeast(1, 1) {
// Wrap the Body reader with one that replies on the connection
req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
}
if req.ContentLength == 0 {
w.Header().Set("Connection", "close")
w.WriteHeader(StatusBadRequest)
w.finishRequest()
break
}
req.Header.Del("Expect")
} else if req.Header.Get("Expect") != "" {
// TODO(bradfitz): let ServeHTTP handlers handle
// requests with non-standard expectation[s]? Seems
// theoretical at best, and doesn't fit into the
// current ServeHTTP model anyway. We'd need to
// make the ResponseWriter an optional
// "ExpectReplier" interface or something.
//
// For now we'll just obey RFC 2616 14.20 which says
// "If a server receives a request containing an
// Expect field that includes an expectation-
// extension that it does not support, it MUST
// respond with a 417 (Expectation Failed) status."
w.Header().Set("Connection", "close")
w.WriteHeader(StatusExpectationFailed)
w.finishRequest()
break
}
handler := c.server.Handler
if handler == nil {
handler = DefaultServeMux
}
// HTTP cannot have multiple simultaneous active requests.[*]
// Until the server replies to this request, it can't read another,
// so we might as well run the handler in this goroutine.
// [*] Not strictly true: HTTP pipelining. We could let them all process
// in parallel even if their responses need to be serialized.
handler.ServeHTTP(w, w.req)
if c.hijacked {
return
}
w.finishRequest()
if w.closeAfterReply {
break
}
}
c.close()
}