美文网首页
http.ListenAndServe

http.ListenAndServe

作者: JunChow520 | 来源:发表于2021-12-29 17:45 被阅读0次

    创建WWW服务实现HTTP通信大致可分为两个阶段:注册路由、监听启动

    1. 服务端创建Socket监听指定端口,等待客户端请求到来。
    2. 监听Socket接受客户端请求并建立连接以获取客户端Socket,服务端通过客户端Socket与之通信。
    3. 服务端处理客户端请求并返回响应

    Go标准库net/http提供http.Server可用以实现Web服务器

    例如:使用单个处理程序创建HTTP服务

    请求流程

    1. 客户端通过指定的URL将请求发送给服务端
    2. 服务端将请求指向到对应的处理器进行处理
    3. 处理器处理请求执行必要的动作
    4. 处理器将结果返回给客户端
    type Handler struct{}
    
    func (Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "hello world")
    }
    
    func main() {
        //实例化http.Server
        srv := http.Server{}
        //设置服务监听端口
        srv.Addr = ":80"
        //设置服务的处理程序
        srv.Handler = Handler{}
        //监听TCP连接并处理客户端请求
        if err := srv.ListenAndServe(); err != nil {
            panic(err)
        }
    }
    
    运行流程

    创建服务 http.Server

    http.Server定义了运行一个HTTP服务器所需参数,最基本的两个参数是AddrHandler

    type Server struct {
        Addr string
        Handler Handler 
        ...
    }
    
    参数 类型 描述
    Addr string 用于指定服务器的TCP地址,形式为host:port。为空则默认使用80端口,默认地址可省略。
    Handler http.Handler 处理器,默认为http.DefaultServeMux

    处理器 http.Handler

    http.Handler是一个接口,只提供了一个方法签名ServeHTTP()ServeHTTP()方法签名会接受两个参数分别是http.ResponseWriter接口和http.Request指针。任何实现ServerHTTP()接口方法的都是一个自定义的处理器。

    type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
    }
    

    使用注意

    • http.Handler用于响应一个HTTP请求
    • ServeHTTP()接口方法用来将响应头和数据写入到http.ResponseWriter中后结束请求,结束后不能再继续使用这个http.ResponseWriter,也不能再从http.RequestBody体中读取数据,另外不能并发调用已完成的ServeHTTP()
    • http.Handler应该线读取请求体再写入http.ResponseWriter,一旦开始向http.ResponseWriter写数据就不能再从请求体中读取数据。
    • http.Handler只能用来读取http.RequestBody,不能修改已取得的请求,因为参数http.Request是指针类型的。

    响应 http.ResponseWriter

    http.ResponseWriter接口的作用是用来构建HTTP响应,且明确指定Handler.ServeHTTP()方法返回后就不能再继续使用http.ResponseWriter

    type ResponseWriter interface {
        Header() Header
        Write([]byte) (int, error)
        WriteHeader(statusCode int)
    }
    

    http.ResponseWriter接口提供了三个方法

    接口方法 描述
    Header() 用来构建响应头
    Write() 用于向网络连接中写入响应数据
    WriteHeader() 将给定的响应状态码和响应头一起通过网络连接发送给客户端

    Write()方法会返回一个http.Header类型的对象来构建响应体,http.Header对象会被WriteHeader()响应出去。

    type Header map[string][]string
    

    监听服务 http.ListenAndServe

    http.ListenAndServe()用于在指定的TCP网络地址进行监听,然后调用服务端处理程序来处理传入的请求。

    func ListenAndServe(addr string, handler Handler) error {
        server := &Server{Addr: addr, Handler: handler}
        return server.ListenAndServe()
    }
    

    http.ListenAndServe()用于设置监听TCP地址并启动服务,监听启动实际上会实例化一个http.Server对象,通过它调用ListenAndServe()开启对客户端的监听。

    func (srv *Server) ListenAndServe() error {
        if srv.shuttingDown() {
            return ErrServerClosed
        }
        addr := srv.Addr
        if addr == "" {
            addr = ":http"
        }
        ln, err := net.Listen("tcp", addr)
        if err != nil {
            return err
        }
        return srv.Serve(ln)
    }
    

    调用Server实例的ListenAndServe()会调用底层的net.Listen("tcp", addr),基于TCP协议创建监听Socket,通过传入的主机地址和端口号,在指定端口上监听客户端请求。

    func Listen(network, address string) (Listener, error) {
        var lc ListenConfig
        return lc.Listen(context.Background(), network, address)
    }
    

    创建监听Socket成功后,会调用Server实例的Serve(net.Listener)用于接受并处理客户端请求。Serve(net.Listener)内部会开启一个for死循环,循环体内通过net.Listener实例(即Listen Socket)的Accept方法来接受客户端请求。当接收到请求后会根据请求会创建net.Conn连接实例(即Client Socket)。为了处理并发请求,会单独为每个连接实例开启一个goroutine去服务,请求的具体逻辑处理都会在serve()方法内完成。

    func (srv *Server) Serve(l net.Listener) error {
        if fn := testHookServerServe; fn != nil {
            fn(srv, l) // call hook with unwrapped listener
        }
    
        origListener := l
        l = &onceCloseListener{Listener: l}
        defer l.Close()
    
        if err := srv.setupHTTP2_Serve(); err != nil {
            return err
        }
    
        if !srv.trackListener(&l, true) {
            return ErrServerClosed
        }
        defer srv.trackListener(&l, false)
    
        baseCtx := context.Background()
        if srv.BaseContext != nil {
            baseCtx = srv.BaseContext(origListener)
            if baseCtx == nil {
                panic("BaseContext returned a nil context")
            }
        }
    
        var tempDelay time.Duration // how long to sleep on accept failure
    
        ctx := context.WithValue(baseCtx, ServerContextKey, srv)
        for {
            rw, err := l.Accept()
            if err != nil {
                select {
                case <-srv.getDoneChan():
                    return ErrServerClosed
                default:
                }
                if ne, ok := err.(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", err, tempDelay)
                    time.Sleep(tempDelay)
                    continue
                }
                return err
            }
            connCtx := ctx
            if cc := srv.ConnContext; cc != nil {
                connCtx = cc(connCtx, rw)
                if connCtx == nil {
                    panic("ConnContext returned nil")
                }
            }
            tempDelay = 0
            c := srv.newConn(rw)
            c.setState(c.rwc, StateNew, runHooks) // before Serve can return
            go c.serve(connCtx)
        }
    }
    

    客户端请求的处理集中在连接实例的serve()方法内,serve()方法主要实现将HTTP请求分配给指定的处理器函数来进行处理。

    func (c *conn) serve(ctx context.Context) {
      // ...
    }
    

    首先从客户端Socket中读取HTTP请求的协议头,判断请求方法若是POST则需读取客户端提交的数据,然后交给对应的Handler来处理请求,Handler处理完毕后准备后客户端所需数据,再通过客户端Socket写给客户端。

    连接实例通过readRequest()方法解析请求,然后再通过serverHandler{c.server}.ServeHTTP(w, w.req)中的ServeHTTP()方法获取请求对应的处理器。

    http.ListenAndSerTLS()

    http.ListenAndSerTLS()方法用于处理HTTPS请求

    func http.ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) error
    

    路由注册 http.ServeMux

    http.ListenAndServe()启动时若入参handler处理器默认为nil,则表示服务端会调用包变量http.DefaultServeMux作为默认处理器,此时服务端编写的业务逻辑处理程序http.Handler()http.HandleFunc()会默认注入http.DefaultServeMux中。

    例如:采用默认服务复用器启动服务

    http.HandleFunc("/", func(rw http.ResponseWriter, rq *http.Request) {
        n, err := rw.Write([]byte(rq.RemoteAddr))
        if err != nil || n <= 0 {
            panic(err)
        }
    })
    
    err := http.ListenAndServe(":3000", nil)
    if err != nil {
        panic(err)
    }
    

    若不想采用默认的的http.DefaultServeMux可使用http.NewServeMux()创建自定义的http.ServeMux

    func NewServeMux() *ServeMux
    

    相关文章

      网友评论

          本文标题:http.ListenAndServe

          本文链接:https://www.haomeiwen.com/subject/htzmqrtx.html