服务端

使用

创建一个Hello World服务

package mainimport ("io""net/http"
)func hello(w http.ResponseWriter, r *http.Request) {io.WriteString(w, "hello world\n")
}func main() {http.HandleFunc("/hello", hello)http.ListenAndServe(":8080", nil)
}

使用下面命令启动

go run main.go

这时候我们打开浏览器访问localhost:8080/hello,就可以看到hello world
我们还有另外一种添加方式:

type helloHandler struct {}func (h *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {io.WriteString(w, "hello world\n")
}func main() {http.Handle("/hello", &helloHandler{})http.ListenAndServe(":8080", nil)
}

默认Handler

http.HandleFunc(pattern string, handler func(ResponseWriter, *Request))第二个参数是一个Handler, net/http库提供了一些常用handler的默认实现,可以进行直接调用。
比如文件服务器:

http.ListenAndServe(":12345", http.FileServer(http.Dir(".")))

路由

在实际开发中一个HTTP接口会有许多的URL对应的Handler,这时候就需要路由的参与。net/http库中ServeMux(mux是multiplexor缩写)帮我们实现了这个功能。

package mainimport ("io""net/http"
)func header(w http.ResponseWriter, r *http.Request) {encoding := r.Header["Accept-Encoding"]var encodingStr []bytefor _, value := range encoding {encodingStr = append(encodingStr, []byte(value)...)}w.Write(encodingStr)
}func hello(w http.ResponseWriter, r *http.Request) {io.WriteString(w, "hello world\n")
}func main() {mux := http.NewServeMux()mux.HandleFunc("/header", header)mux.HandleFunc("/hello", hello)http.ListenAndServe(":8080", mux)
}

先通过http.NewServeMux()返回一个ServeMux结构体,然后调用mux.HandleFunc()绑定Handler到对应的URL上。
其实http.HandleFunc()内部调用的是DefaultServeMux.HandleFunc(pattern, handler),这是一个默认的ServeMux

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMuxvar defaultServeMux ServeMux

源码解析

http.HandleFunc

http.HandleFunc()看起

// 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)) {if handler == nil {panic("http: nil handler")}mux.Handle(pattern, HandlerFunc(handler))
}

进入这个函数看,调用了mux.Handle(pattern, HandlerFunc(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()if pattern == "" {panic("http: invalid pattern")}if handler == nil {panic("http: nil handler")}if _, exist := mux.m[pattern]; exist {panic("http: multiple registrations for " + pattern)}if mux.m == nil {mux.m = make(map[string]muxEntry)}e := muxEntry{h: handler, pattern: pattern}mux.m[pattern] = eif pattern[len(pattern)-1] == '/' {mux.es = appendSorted(mux.es, e)}if pattern[0] != '/' {mux.hosts = true}
}

这里需要参考一下ServeMux结构体

type ServeMux struct {mu    sync.RWMutexm     map[string]muxEntryes    []muxEntry // slice of entries sorted from longest to shortest.hosts bool       // whether any patterns contain hostnames
}type muxEntry struct {h       Handlerpattern string
}

mux.Handle(pattern, handler)主要作用是将路由URL与Handler绑定,然后存到一个map中,URL作为map的键。

if pattern[len(pattern)-1] == '/' {mux.es = appendSorted(mux.es, e)
}

判断pattern是否是以 '/'结尾,如果是以 '/'结尾后面可以跟其他子路径,如果不是表示的是一个叶子节点。

func appendSorted(es []muxEntry, e muxEntry) []muxEntry {n := len(es)i := sort.Search(n, func(i int) bool {return len(es[i].pattern) < len(e.pattern)})if i == n {return append(es, e)}// we now know that i points at where we want to insertes = append(es, muxEntry{}) // try to grow the slice in place, any entry works.copy(es[i+1:], es[i:])      // Move shorter entries downes[i] = ereturn es
}

sort.Search()找es中是否有比e.pattern中长度更短的,如果找到返回第一次出现的下标,没找到返回n;如果没有找到就添加到muxEntry中返回,如果有就将e.pattern插入找到的i的位置。

es是ServeMux用来进行路由匹配的,采用的是最长匹配原则,也就是说如果有多个匹配项匹配最长的。所以es存的时候将路径长度长的放在前面,这样检索起来效率比较高,我觉得这是我们在写程序的时候值得学习的。

http.ListenAndServe

func ListenAndServe(addr string, handler Handler) error {server := &Server{Addr: addr, Handler: handler}return server.ListenAndServe()
}// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
//
// If srv.Addr is blank, ":http" is used.
//
// ListenAndServe always returns a non-nil error. After Shutdown or Close,
// the returned error is ErrServerClosed.
func (srv *Server) ListenAndServe() error {if srv.shuttingDown() {return ErrServerClosed}addr := srv.Addrif addr == "" {addr = ":http"}ln, err := net.Listen("tcp", addr)if err != nil {return err}return srv.Serve(ln)
}

注释上写的很清楚,ListenAndServe()就是用来监听TCP,然后调用Serve来处理这次请求的。

// Serve accepts incoming connections on the Listener l, creating a
// new service goroutine for each. The service goroutines read requests and
// then call srv.Handler to reply to them.
//
// HTTP/2 support is only enabled if the Listener returns *tls.Conn
// connections and they were configured with "h2" in the TLS
// Config.NextProtos.
//
// Serve always returns a non-nil error and closes l.
// After Shutdown or Close, the returned error is ErrServerClosed.
func (srv *Server) Serve(l net.Listener) error {...origListener := ll = &onceCloseListener{Listener: l}defer l.Close()...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 ErrServerCloseddefault:}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 := ctxif cc := srv.ConnContext; cc != nil {connCtx = cc(connCtx, rw)if connCtx == nil {panic("ConnContext returned nil")}}tempDelay = 0c := srv.newConn(rw)c.setState(c.rwc, StateNew) // before Serve can returngo c.serve(connCtx)}
}

Serve()主要完成接收TCP请求,然后通过go c.serve(connCtx)处理连接。
进入c.serve()最重要的是下面这行代码,可以很清楚的看到调用了ServeHTTP,想想我们一开始编写的Hello World,其中有一种方法就是编写ServeHTTP

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

其实这个ServeHTTP是调用handler.ServeHTTP(rw, req)

// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {srv *Server
}func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {handler := sh.srv.Handlerif handler == nil {handler = DefaultServeMux}if req.RequestURI == "*" && req.Method == "OPTIONS" {handler = globalOptionsHandler{}}handler.ServeHTTP(rw, req)
}

最后一行的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)
}// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {f(w, r)
}func (rh *redirectHandler) ServeHTTP(w ResponseWriter, r *Request) {Redirect(w, r, rh.url, rh.code)
}

h, _ := mux.Handler(r)这个是完成对路径的匹配,返回相应的Handler,下面就是相关函数,调用关系从上到下,最后调用match去确定URL对应的Handler。

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {// CONNECT requests are not canonicalized.if r.Method == "CONNECT" {// If r.URL.Path is /tree and its handler is not registered,// the /tree -> /tree/ redirect applies to CONNECT requests// but the path canonicalization does not.if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {return RedirectHandler(u.String(), StatusMovedPermanently), u.Path}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 the given path is /tree and its handler is not registered,// redirect for /tree/.if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {return RedirectHandler(u.String(), StatusMovedPermanently), u.Path}if path != r.URL.Path {_, pattern = mux.handler(host, path)url := *r.URLurl.Path = pathreturn RedirectHandler(url.String(), StatusMovedPermanently), pattern}return 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 onesif mux.hosts {h, pattern = mux.match(host + path)}if h == nil {h, pattern = mux.match(path)}if h == nil {h, pattern = NotFoundHandler(), ""}return
}// 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}// Check for longest valid match.  mux.es contains all patterns// that end in / sorted from longest to shortest.for _, e := range mux.es {if strings.HasPrefix(path, e.pattern) {return e.h, e.pattern}}return nil, ""
}

客户端

net/http不仅提供服务端的实现,还提供客户端的实现。
发起请求:

resp, err := http.Get("http://example.com/")
...
resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)
...
resp, err := http.PostForm("http://example.com/form",url.Values{"key": {"Value"}, "id": {"123"}})

读响应:

resp, err := http.Get("http://example.com/")
if err != nil {// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
// ...

注意这里的resp.Body的读取不能使用Read([]byte)
如果想要详细的设置请求的头部等,可以使用client{}

client := &http.Client{CheckRedirect: redirectPolicyFunc,
}resp, err := client.Get("http://example.com")
// ...req, err := http.NewRequest("GET", "http://example.com", nil)
// ...
req.Header.Add("If-None-Match", `W/"wyzzy"`)
resp, err := client.Do(req)

http.NewRequest(method, url string, body io.Reader)第三个参数是请求体
下面是几个例子:

func get() {apiURL := "http://192.168.101.3:8080/hello"data := url.Values{}data.Set("name", "abc")data.Set("age", "18")u, err := url.ParseRequestURI(apiURL)u.RawQuery = data.Encode()resp, err := http.Get(u.String())if err != nil {fmt.Println("get failed, err:", err)return}defer resp.Body.Close()body, err := ioutil.ReadAll(resp.Body)if err != nil {fmt.Println("read from resp.Body failed,err:", err)return}fmt.Print(string(body))
}func post() {url := "http://192.168.101.3:8080/post"// 表单数据//contentType := "application/x-www-form-urlencoded"//data := "name=小王子&age=18"// jsoncontentType := "application/json"data := `{"name":"小王子","age":18}`resp, err := http.Post(url, contentType, strings.NewReader(data))if err != nil {fmt.Printf("post failed, err:%v\n", err)return}defer resp.Body.Close()b, err := ioutil.ReadAll(resp.Body)if err != nil {fmt.Printf("get resp failed,err:%v\n", err)return}fmt.Println(string(b))
}

Go的http库详解相关推荐

  1. matlab中sinks,MATLAB Simulink模块库详解(二)Sinks篇

    MATLAB Simulink模块库详解(二)Sinks篇 Simulink模块库概述 1.Sources模块库,为仿真提供各种信号源 2.Sinks模块库,为仿真提供输出设备元件 3.Continu ...

  2. STM32 HAL库详解 及 手动移植

    源: STM32 HAL库详解 及 手动移植

  3. stm32 IOT_基于STM32平台的cubeMX和HAL库详解

    课程简介: <朱有鹏老师单片机完全学习系列课程>总共5季,其中第1.2季是51单片机学习,第3.4.5季是STM32单片机与RTOS学习.整个课程时长约250小时,是一套零基础.全面系统. ...

  4. 爬虫笔记:Requests库详解

    什么是Requests 之前讲解了爬虫笔记:Urllib库详解发现确实有不方便的地方,比如加一个代理,cookie,发送post请求比较繁琐. Request库能用几句话实现这些. Requests ...

  5. python爬虫之urllib库详解

    python爬虫之urllib库详解 前言 一.urllib库是什么? 二.urllib库的使用 urllib.request模块 urllib.parse模块 利用try-except,进行超时处理 ...

  6. Python Urllib库详解

    Urllib库详解 什么是Urllib? Python内置的HTTP请求库 urllib.request 请求模块 urllib.error 异常处理模块 urllib.parse url解析模块 u ...

  7. mysql5.6+master+date_MySQL5.6的4个自带库详解

    1.information_schema详细介绍: information_schema数据库是MySQL自带的,它提供了访问数据库元数据的方式.什么是元数据呢?元数据是关于数据的数据,如数据库名或表 ...

  8. 爬虫入门之urllib库详解(二)

    爬虫入门之urllib库详解(二) 1 urllib模块 urllib模块是一个运用于URL的包 urllib.request用于访问和读取URLS urllib.error包括了所有urllib.r ...

  9. python gpu加速库比matlab快吗_Python之Unittest和Requests库详解

    1.按类来执行 import unittest class f1(unittest.TestCase): def setUp(self): pass def tearDown(self): pass ...

  10. chrono是一个time library,C++11 std::chrono库详解

    C++11 std::chrono库详解 chrono是一个time library, 源于boost,现在已经是C++标准.话说今年似乎又要出新标准了,好期待啊! 要使用chrono库,需要#inc ...

最新文章

  1. http://blog.csdn.net/xingfuzhijianxia/article/details/6433918
  2. 枚举类型是怎样定义的?有什么用途?_新型合金材料——什么是液态金属、液态金属的定义、发展以及用途...
  3. C++bidirectional dijkstra双向最短路径算法(附完整源码)
  4. sync.Map低层工作原理详解
  5. SQL Azure Reporting CTP
  6. activiti 设置可选处理人_新品速递|高端系列!慧明DF系列线性相位处理专业音箱处理器...
  7. 新来的前端小姐姐问:Vue路由history模式刷新页面出现404问题
  8. 用JS控制下拉菜单效果
  9. iPhone 12s新功能曝光:支持天体摄影+息屏显示
  10. 【Leetcode】【Regular Expression Matching】【正则表达式匹配】【C++】
  11. 我的实用小软件(持续更新)
  12. ZEMAX知识点:坐标间断面(coordinate break)
  13. No3 jQuery
  14. 思科路由器、交换机的远程登录配置
  15. java递归获取树结构的指定层级、指定层级之上(向上递归(包含父集所有、爷爷集所有...)(父集单独、爷爷集单独...))、指定层级之下所有的(子集、孙子集...)和list集合转Tree树结构
  16. MATLAB画曲线图
  17. 抢先服显示服务器关闭是什么意思,王者荣耀抢先服是什么意思 抢先服和正式服的区别...
  18. openWRT 无线使用3g上网
  19. xoom 真机开发
  20. 程序员的电脑从来就没有广告弹窗,不用下软件,原来是用这1招!

热门文章

  1. 导出oracle11g的空表,轻松解决oracle11g 空表不能 exp 导出 的问题。
  2. python中等于列表的某一个值为真,python – 获取值等于特定值的列表中的所有元素...
  3. l2-004 这是二叉搜索树吗?_MySQL索引为何选择B+树
  4. 大学计算机知识考试题,大学计算机基础重点知识考试试题
  5. cmd杀死MySQL进程命令
  6. 前端传递数据超过2M不能传给后台
  7. 如何查找MySQL中查询慢的SQL语句
  8. Android开发笔记(一百七十一)使用Glide加载网络图片
  9. AIoT催使AI深度场景化,“AI赋能,智联万物——开发者沙龙·南京站”圆满落幕...
  10. python3.5+tesseract+adb实现西瓜视频或头脑王者辅助答题