http.ListenAndServe

2021-12-29  本文已影响0人  JunChow520

创建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.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
上一篇下一篇

猜你喜欢

热点阅读