golang标准包分析—http


服务端

服务端是用来接收连接的,因此需要一个监听的逻辑

// 函数ListenAndServe用来开启服务并且监听,而这个
// 函数的核心就是tcp连接,通过net.Listen(param1,param2)得到listener
// 其中param1是连接类型,可以有tcp,tcp4,tcp6......,param2则是服务地址:ip:port
// 返回的listener是一个interface{}类型
type Listener interface {
    // Accept waits for and returns the next connection to the listener.
    Accept() (Conn, error)

    // Close closes the listener.
    // Any blocked Accept operations will be unblocked and return errors.
    Close() error

    // Addr returns the listener's network address.
    Addr() Addr
}
只要实现了Listener的三个函数的结构体,都能作为返回参数,以param1=tcp为例子,将会返回TCPListener

// TCPListener is a TCP network listener. Clients should typically
// use variables of type Listener instead of assuming TCP.
type TCPListener struct {
    fd *netFD
}
// Network file descriptor.
type netFD struct {
    pfd poll.FD

    // immutable until Close
    family      int
    sotype      int
    isConnected bool // handshake completed or use of association with peer
    net         string
    laddr       Addr
    raddr       Addr
}

// family 地址系列、地址族

    AF_UNSPEC  = 0
    AF_UNIX    = 1
    AF_INET    = 2
    AF_INET6   = 23
    AF_NETBIOS = 17

// sotype 套接字类型

    SOCK_STREAM    = 1
    SOCK_DGRAM     = 2
    SOCK_RAW       = 3
    SOCK_SEQPACKET = 5

// net 连接类型

// laddr localaddress本地地址
// raddr remoteaddress远程地址
// 通过newFD函数返回TCPListenner的需要的子结构fd

func newFD(sysfd syscall.Handle, family, sotype int, net string) (*netFD, error) {
    ret := &netFD{
        pfd: poll.FD{
            Sysfd:         sysfd,
            IsStream:      sotype == syscall.SOCK_STREAM,
            ZeroReadIsEOF: sotype != syscall.SOCK_DGRAM && sotype != syscall.SOCK_RAW,
        },
        family: family,
        sotype: sotype,
        net:    net,
    }
    return ret, nil
}
// laddr与raddr在listenstream中set进去

func (fd *netFD) listenStream(laddr sockaddr, backlog int, ctrlFn func(string, string, syscall.RawConn) error) error {
    var err error
    if err = setDefaultListenerSockopts(fd.pfd.Sysfd); err != nil {
        return err
    }
    var lsa syscall.Sockaddr
    if lsa, err = laddr.sockaddr(fd.family); err != nil {
        return err
    }
    if ctrlFn != nil {
        c, err := newRawConn(fd)
        if err != nil {
            return err
        }
        if err := ctrlFn(fd.ctrlNetwork(), laddr.String(), c); err != nil {
            return err
        }
    }
    if err = syscall.Bind(fd.pfd.Sysfd, lsa); err != nil {
        return os.NewSyscallError("bind", err)
    }
    if err = listenFunc(fd.pfd.Sysfd, backlog); err != nil {
        return os.NewSyscallError("listen", err)
    }
    if err = fd.init(); err != nil {
        return err
    }
    lsa, _ = syscall.Getsockname(fd.pfd.Sysfd)
    fd.setAddr(fd.addrFunc()(lsa), nil)
    return nil
}

func (fd *netFD) setAddr(laddr, raddr Addr) {
    fd.laddr = laddr
    fd.raddr = raddr
    runtime.SetFinalizer(fd, (*netFD).Close)
}

通过以上操作将会得到一个参数齐全的tcpListener,并且以listener接口类型返回给使用者, 这里值得提的是代码中三个实现的函数并不都是用tcpListener的实现 其中Addr()函数用的是tcpListener的具体实现 Accept()用的是tcpKeepAliveListener的具体实现 Close()用的是onceCloseListener的具体实现 tcpKeepAliveListener是通过将tcplistener作为自己的子结构,再次实现Accept函数 onceCloseListener是通过将tcpKeepAliveListener作为自己的子结构,再次实现Close函数

type tcpKeepAliveListener struct {
    *net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {
    tc, err := ln.AcceptTCP()
    if err != nil {
        return nil, err
    }
    tc.SetKeepAlive(true)
    tc.SetKeepAlivePeriod(3 * time.Minute)
    return tc, nil
}

// AcceptTCP accepts the next incoming call and returns the new
// connection.
func (l *TCPListener) AcceptTCP() (*TCPConn, error) {
    if !l.ok() {
        return nil, syscall.EINVAL
    }
    c, err := l.accept()
    if err != nil {
        return nil, &OpError{Op: "accept", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
    }
    return c, nil
}
// onceCloseListener wraps a net.Listener, protecting it from
// multiple Close calls.
type onceCloseListener struct {
    net.Listener
    once     sync.Once
    closeErr error
}

func (oc *onceCloseListener) Close() error {
    oc.once.Do(oc.close)
    return oc.closeErr
}

func (oc *onceCloseListener) close() { oc.closeErr = oc.Listener.Close() }

最后实际建立连接的函数,调用的还是netFD的accept(),通过将连接建立成功的local与remote地址存放在netFD中关联起来

func (fd *netFD) accept() (*netFD, error) {
    s, rawsa, rsan, errcall, err := fd.pfd.Accept(func() (syscall.Handle, error) {
        return sysSocket(fd.family, fd.sotype, 0)
    })

    if err != nil {
        if errcall != "" {
            err = wrapSyscallError(errcall, err)
        }
        return nil, err
    }

    // Associate our new socket with IOCP.
    netfd, err := newFD(s, fd.family, fd.sotype, fd.net)
    if err != nil {
        poll.CloseFunc(s)
        return nil, err
    }
    if err := netfd.init(); err != nil {
        fd.Close()
        return nil, err
    }

    // Get local and peer addr out of AcceptEx buffer.
    var lrsa, rrsa *syscall.RawSockaddrAny
    var llen, rlen int32
    syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&rawsa[0])),
        0, rsan, rsan, &lrsa, &llen, &rrsa, &rlen)
    lsa, _ := lrsa.Sockaddr()
    rsa, _ := rrsa.Sockaddr()

    netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
    return netfd, nil
}

具体的accept函数逻辑暂时不去分析,当前只用知道创建监听者成功后, 通过accept函数可以获取远程连接,且将远程连接与本地连接关联起来返回给使用者

注册handle

这里将handle理解为路由,通过函数调用将路由与对应的处理函数以key value的形式存储起来

http.Handle("/hello2", &hello{})
http.Handle("/hello", http.HandlerFunc(Hello))

// 这里实现的函数或者结构体也好,都要实现Handler接口
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

// 注册成功的handle都会存在DefaultServeMux中
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

调用handle

在accept连接成功后,会单开一个协程为连接服务

for {
        rw, e := l.Accept()
        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
        c := srv.newConn(rw)
        c.setState(c.rwc, StateNew) // before Serve can return
        go c.serve(ctx)
    }

在serve函数中会先做一些细节处理,然后读取请求,具体什么不去分析,处理结束后, 读取http客户端请求成功后,会对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.
        // But we're not going to implement HTTP pipelining because it
        // was never deployed in the wild and the answer is HTTP/2.
        serverHandler{c.server}.ServeHTTP(w, w.req)
        w.cancelCtx()

    func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    handler := sh.srv.Handler
    if handler == nil {
        handler = DefaultServeMux
    }
    if req.RequestURI == "*" && req.Method == "OPTIONS" {
        handler = globalOptionsHandler{}
    }
    handler.ServeHTTP(rw, req)
}

以上就是golang自带http服务的基本内容,包括了服务器的启动,监听者Listener的具体结构分析、路由的注册、handle的储存方式、以及handle的调用

稍后会继续分析accept接收成功后的conn对象具体结构,以及读写操作的实现函数分析

See also