http.ListenAndServe
创建WWW服务实现HTTP通信大致可分为两个阶段:注册路由、监听启动
- 服务端创建Socket监听指定端口,等待客户端请求到来。
- 监听Socket接受客户端请求并建立连接以获取客户端Socket,服务端通过客户端Socket与之通信。
- 服务端处理客户端请求并返回响应
Go标准库net/http
提供http.Server
可用以实现Web服务器
例如:使用单个处理程序创建HTTP服务
请求流程
- 客户端通过指定的URL将请求发送给服务端
- 服务端将请求指向到对应的处理器进行处理
- 处理器处理请求执行必要的动作
- 处理器将结果返回给客户端
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服务器所需参数,最基本的两个参数是Addr
和Handler
。
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.Request
的Body
体中读取数据,另外不能并发调用已完成的ServeHTTP()
。 -
http.Handler
应该线读取请求体再写入http.ResponseWriter
,一旦开始向http.ResponseWriter
写数据就不能再从请求体中读取数据。 -
http.Handler
只能用来读取http.Request
的Body
,不能修改已取得的请求,因为参数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