您的位置:首页 > 理论基础 > 计算机网络

go语言 通过http包搭建简单web服务器 对http包源码的略微分析

2017-11-03 20:48 926 查看
使用go的http包搭建简单的服务器

package main

import (
"fmt"
"log"
"net/http"
"strings"
)

func sayhelloName(w http.ResponseWriter, r *http.Request) {
r.ParseForm()       //解析参数,默认是不会解析的
fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["friend"])
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
fmt.Fprintf(w, "Hello my friend!") //这个写入到w的是输出到客户端的
}

func main() {
http.HandleFunc("/", sayhelloName)       //设置访问的路由
err := http.ListenAndServe(":8080", nil) //设置监听的端口
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}


go run 运行,这个时候其实已经在8080端口监听http请求了。

浏览器中输入http://localhost:8080/就可以看到一行英文:

“Hello my friend!”了。

下面看一下http包的源码,了解 web 服务器处理 http 协议的过程.

主要分析main()中的两个函数:http.HandleFunc(“/”, sayhelloName)和http.ListenAndServe(“:8080”, nil)

先看http.HandleFunc(“/”, sayhelloName)的定义。该函数给DefaultServeMux中相应的路由注册处理函数。

// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}


继续追踪方法DefaultServeMux.HandleFunc(pattern, handler)

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler))
}


其中mux.Handle声明如下

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler)


我们发现方法DefaultServeMux.HandleFunc的参数handler的类型是func(ResponseWriter, *Request),然而方法mux.Handle的第二个参数应该是Handler接口类型。这意味着HandlerFunc(handler)使得HandleFunc方法的第二个参数handler实现了Handler接口,接下来就看看HandlerFunc()怎么实现这一点。

Handler接口:

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


HandlerFunc类型的定义

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}


个人认为,mux.Handle(pattern, HandlerFunc(handler))中的

HandlerFunc(handler)是一个类型转换,把对应函数转换成HandlerFunc类型,而HandlerFunc类型实现了ServeHTTP接口,这样就满足了mux.Handle方法的第二个参数需要实现Handler接口的要求。

通过阅读源码,我们学习到了把传入的函数变成相应接口的方法。

接下来分析怎么实现相应路由处理函数的注册:

ServeMux结构如下:

type ServeMux struct {
mu    sync.RWMutex
m     map[string]muxEntry
hosts bool // whether any patterns contain hostnames
}


mu是锁,由于请求涉及并发处理,这里需要锁机制。

m则是路由规则,一个string(注册处理函数的路由表达式)对应一个muxEntry

下面看一下muxEntry:

type muxEntry struct {
explicit bool
h        Handler
pattern  string
}


h就是我们之前提到的Handler接口,就是对应的处理函数。

func (mux *ServeMux) Handle(pattern string, handler Handler)的定义:

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
mux.mu.Lock()
defer mux.mu.Unlock()
//锁机制
...//处理错误情况,如路由不合理,handler为nil,重复注册等
if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}//这句代码实现了对应处理函数的注册
//后面代码涉及重定向就不贴出来了
}


分析完了http.HandleFunc(“/”, sayhelloName),接下来分析http.ListenAndServe(“:8080”, nil)。

跟踪http.ListenAndServe(“:8080”, nil)定义一直到

func (srv *Server) Serve(l net.Listener) error的定义。监听的套接字使用accept函数接受客户端发送的连接请求后,处理代码如下:

c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)


Go使用了goroutines来处理Conn的读写事件,这样每个请求都能保持独立,相互不会阻塞。

客户端的每次请求都会创建一个Conn。

上面代码的第三行c.serve(ctx),该方法的定义中的serverHandler{c.server}.ServeHTTP(w, w.req)用于处理请求,参数w是*response类型,response是结构体,代表着服务端的一个HTTP response。w.req是请求。根据serverHandler结构体中的*Server来确定是哪个server处理该请求。

type serverHandler struct {
srv *Server
}


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)
}


如果之前调用的http.ListenAndServe(“:8080”, nil)的第二个参数是nil,则ServeHTTP会使用DefaultServeMux,默认处理逻辑来处理请求。

DefaultServeMux实现的ServeHTTP如下:

// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}


调用mux.Handler(r),返回请求路由对应的处理Handler,然后执行h.ServeHTTP(w,r)

mux.Handler(r)代码如下:

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {

// CONNECT requests are not canonicalized.
if r.Method == "CONNECT" {
return mux.handler(r.Host, r.URL.Path)
}

// All other requests have any port stripped and path cleaned
// before passing to mux.handler.
host := stripHostPort(r.Host)
path := cleanPath(r.URL.Path)
if path != r.URL.Path {
_, pattern = mux.handler(host, path)
url := *r.URL
url.Path = path
return RedirectHandler(url.String(), StatusMovedPermanently), pattern
}
//前面代码进行相应处理,得到host和URL.Path
return mux.handler(host, r.URL.Path)
}


上面代码第19行的mux.handler(host, r.URL.Path)代码如下:

// handler is the main implementation of Handler.
// The path is known to be in canonical form, except for CONNECT methods.
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()

// Host-specific pattern takes precedence over generic ones
if mux.hosts {
h, pattern = mux.match(host + path)
}
if h == nil {
h, pattern = mux.match(path)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}


mux.match代码如下:

// Find a handler on a handler map given a path string.
// Most-specific (longest) pattern wins.
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
// Check for exact match first.
v, ok := mux.m[path]
if ok {
return v.h, v.pattern
}
...//后面是没有匹配的处理函数时的处理方法
}


根据用户请求的URL和mux储存的map去匹配,匹配到的话返回储存的handler,调用它的ServeHTTP接口就可以执行处理函数,处理请求了。

同时,我们也可以自己定义一个路由管理器,而不使用默认的DefaultServeMux。http.ListenAndServe的第二个参数是用来设置自定义路由管理器的,它是一个Handler接口,只要实现了Handler接口就可以完成路由管理器的处理规则的设置。我们可以在自己实现的路由管理器的,由Handler接口定义的ServeHTTP方法里面实现自定义路由功能。栗子如下:

package main

import (
"fmt"
"log"
"net/http"
)

type MyServeMux struct {
}

func (mux *MyServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL.Path)
if r.URL.Path == "/" {
sayhelloName(w, r)
return
}
if r.URL.Path == "/abc" {
saypagePath(w, r)
return
}
http.NotFound(w, r)
return
}

func sayhelloName(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello my friend!")
}

func saypagePath(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "the path of this page is /abc")
}

func main() {
mymux := &MyServeMux{}
err := http.ListenAndServe(":8080", mymux)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}


浏览器输入http://localhost:8080/会显示Hello my friend!

输入http://localhost:8080/abc会显示the path of this page is /abc
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  go语言 web服务器