【go语言 socket编程系列】一个简单的HTTP服务器及func (srv *Server) Serve(l net.Listener) 方法

【简单的HTTP服务器】

源文件server.go中 ListenAndServe()函数的注释中有个简单的HTTP服务实现代码,如下

package main
 
import (
	"io"
	"log"
	"net/http"
)
 
func HelloServer(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "hello ,this is from HelloServer func ")
}
 
func main() {
	http.HandleFunc("/hello", HelloServer)
	log.Fatal(http.ListenAndServe(":12345", nil))
}

【func (srv *Server) Serve(l net.Listener) 方法】

之前有梳理过http.ListenAndServe() 最终会调用 Server类型的Serve()方法。再看下其源码如下  net/http/server.go 中

2678 func (srv *Server) Serve(l net.Listener) error {
2679         defer l.Close()
2680         if fn := testHookServerServe; fn != nil {
2681                 fn(srv, l)
2682         }
2683         var tempDelay time.Duration // how long to sleep on accept failure
2684 
2685         if err := srv.setupHTTP2_Serve(); err != nil {
2686                 return err
2687         }
2688 
2689         srv.trackListener(l, true)
2690         defer srv.trackListener(l, false)
2691 
2692         baseCtx := context.Background() // base is always background, per Issue 16220
2693         ctx := context.WithValue(baseCtx, ServerContextKey, srv)
2694         for {
2695                 rw, e := l.Accept()
2696                 if e != nil {
2697                         select {
2698                         case <-srv.getDoneChan():
2699                                 return ErrServerClosed
2700                         default:
2701                         }
2702                         if ne, ok := e.(net.Error); ok && ne.Temporary() {
2703                                 if tempDelay == 0 {
2704                                         tempDelay = 5 * time.Millisecond
2705                                 } else {
2706                                         tempDelay *= 2
2707                                 }
2708                                 if max := 1 * time.Second; tempDelay > max {
2709                                         tempDelay = max
2710                                 }
2711                                 srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
2712                                 time.Sleep(tempDelay)
2713                                 continue
2714                         }
2715                         return e
2716                 }
2717                 tempDelay = 0
2718                 c := srv.newConn(rw)
2719                 c.setState(c.rwc, StateNew) // before Serve can return
2720                 go c.serve(ctx)
2721         }
2722 }

第2683行定义了一个 time.Duration 类型的局部变量,用于控制 accept失败后的等待时间。后续的select语句会用到。

 var tempDelay time.Duration  // how long to sleep on accept failure
 

2689         srv.trackListener(l, true)
2690         defer srv.trackListener(l, false)

源码如下,调用了Server类型的trackListener()方法,私有函数,用于对 *Server 数据的并发保护,s.listeners为Server类型的成员变量,类型是一个map,key为 net.Listener ,value为 Struce{}。 Server 类型可以是零值的,所以在使用listeners 成员的时候 要做个nil的判断,通过make()内置函数初始化。

2767 func (s *Server) trackListener(ln net.Listener, add bool) {
2768         s.mu.Lock()
2769         defer s.mu.Unlock()
2770         if s.listeners == nil {
2771                 s.listeners = make(map[net.Listener]struct{})
2772         }
2773         if add {
2774                 // If the *Server is being reused after a previous
2775                 // Close or Shutdown, reset its doneChan:
2776                 if len(s.listeners) == 0 && len(s.activeConn) == 0 {
2777                         s.doneChan = nil
2778                 }
2779                 s.listeners[ln] = struct{}{}
2780         } else {
2781                 delete(s.listeners, ln)
2782         }
2783 }

接下来的for循环,源码如下

2694         for {
2695                 rw, e := l.Accept()
2696                 if e != nil {
2697                         select {
2698                         case <-srv.getDoneChan():
2699                                 return ErrServerClosed
2700                         default:
2701                         }
2702                         if ne, ok := e.(net.Error); ok && ne.Temporary() {
2703                                 if tempDelay == 0 {
2704                                         tempDelay = 5 * time.Millisecond
2705                                 } else {
2706                                         tempDelay *= 2
2707                                 }
2708                                 if max := 1 * time.Second; tempDelay > max {
2709                                         tempDelay = max
2710                                 }
2711                                 srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
2712                                 time.Sleep(tempDelay)
2713                                 continue
2714                         }
2715                         return e
2716                 }
2717                 tempDelay = 0
2718                 c := srv.newConn(rw)
2719                 c.setState(c.rwc, StateNew) // before Serve can return
2720                 go c.serve(ctx)

主要做了5件事情

  • 通过Accept() 监听请求
  • Accpet错误处理
  • 通过Server类型的newConn方法构建 conn类型数据(小写c)
  • 设置conn状态
  • 并发执行conn

1、l.Accept() 默认是执行net.Listener 接口的 Accept() 方法,即: Accept() (Conn, error)

     之前梳理 ListenAndServe的时候,有个tcpKeepAliveListener 类型的数据,重新实现了Accept方法,增加了超时时间的设置。

2、通过select语句实现错误等待。case阻塞后,会执行default语句。通过Temporary()简单判断 

3、 c := srv.newConn(rw) 中 newConn()方法代码如下,构造conn类型。

func (srv *Server) newConn(rwc net.Conn) *conn {
        c := &conn{
                server: srv, 
                rwc:    rwc, 
        }    
        if debugServerConnections {
                c.rwc = newLoggingConn("server", c.rwc)
        }    
        return c
}
  conn类型为server.go 中的私有类型,较复杂,需要了解的直接看源码

4、c.setState(c.rwc, StateNew) 设置conn的状态,setState()源码如下,根据参数state的值来设置状态

1634 func (c *conn) setState(nc net.Conn, state ConnState) {
1635         srv := c.server
1636         switch state {
1637         case StateNew:
1638                 srv.trackConn(c, true)
1639         case StateHijacked, StateClosed:
1640                 srv.trackConn(c, false)
1641         }
1642         c.curState.Store(connStateInterface[state])
1643         if hook := srv.ConnState; hook != nil {
1644                 hook(nc, state)
1645         }
1646 }

参数state的数值如下:

var stateName = map[ConnState]string{
        StateNew:      "new",
        StateActive:   "active",
        StateIdle:     "idle",
        StateHijacked: "hijacked",
        StateClosed:   "closed",

5、go c.serve(ctx)并发执行 请求。其原型如下 func (c *conn) serve(ctx context.Context)。源码逻辑较复杂,我们值关注下面这行,即 ServeHTTP(w, w.req)。

1794                 // HTTP cannot have multiple simultaneous active requests.[*]
1795                 // Until the server replies to this request, it can't read another,
1796                 // so we might as well run the handler in this goroutine.
1797                 // [*] Not strictly true: HTTP pipelining. We could let them all process
1798                 // in parallel even if their responses need to be serialized.
1799                 // But we're not going to implement HTTP pipelining because it
1800                 // was never deployed in the wild and the answer is HTTP/2.
1801                 serverHandler{c.server}.ServeHTTP(w, w.req)

回到开始的例子,HelloServer(w http.ResponseWriter, r *http.Request) 的签名与 ServeHTTP(w, w.req)是一致的.即 ListerAndServe → Server.Serve() → conn.serve  最后执行  ServeHTTP(w, w.req) 签名格式的逻辑,在本例中就是HelloServer自定义函数。最终HTTP的通信通过Request 和Response 来实现。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值