Go Web编程(三——Web基础)

客户机通过 TCP/IP 协议建立到服务器的 TCP 连接

客户端向服务器发送 HTTP 协议请求包,请求服务器里的资源文档

服务器向客户机发送 HTTP 协议应答包,如果请求的资源包含有动态语言的内容,那么服务器会调用动态语言的解释引擎负责处理 “动态内容”,并将处理得到的数据返回给客户端

客户机与服务器断开。由客户端解释 HTML 文档,在客户端屏幕上渲染图形结果

需要注意的是客户机与服务器之间的通信是非持久连接的,也就是当服务器发送了应答后就与客户机断开连接,等待下一次请求。

  • URL和DNS解析

URL是统一资源定位符的缩写。

scheme://host[:port#]/path/.../[?query-string][#anchor]
scheme         指定底层使用的协议(例如:http, https, ftp)
host           HTTP 服务器的 IP 地址或者域名
port#          HTTP 服务器的默认端口是 80,这种情况下端口号可以省略。如果使用了别的端口,必须指明,例如 http://www.cnblogs.com:8080/
path           访问资源的路径
query-string   发送给 http 服务器的数据
anchor         锚

DNS是域名系统的英文缩写,它用于TCP/IP网络,从事将主机名和域名转换为IP地址的工作。

迭代查询:查询的递交者不变(本地DNS服务器)

迭代查询:查询的递交者更替

  • Http协议详解

HTTP 协议是无状态的,同一个客户端的这次请求和上次请求是没有对应关系,对 HTTP 服务器来说,它并不知道这两个请求是否来自同一个客户端。为了解决这个问题, Web 程序引入了 Cookie 机制来维护连接的可持续状态。

HTTP 协议定义了很多与服务器交互的请求方法,最基本的有 4 种,分别是 GET, POST, PUT, DELETE。一个 URL 地址用于描述一个网络上的资源,而 HTTP 中的 GET, POST, PUT, DELETE 就对应着对这个资源的查,增,改,删 4 个操作。

GET用于获取、POST用于更新。

  • Http/net包建立一个简单的Web服务器
package main

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

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["url_long"])
    for k, v := range r.Form {
        fmt.Println("key:", k)
        fmt.Println("val:", strings.Join(v, ""))
    }
    fmt.Fprintf(w, "Hello astaxie!") // 这个写入到 w 的是输出到客户端的
}

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

访问:http://localhost:9090

可以看到浏览器输出了:http://localhost:9090

访问http://localhost:9090/?url_long=111&url_long=222服务端输出:

C:\Users\Administrator\AppData\Local\Temp\___go_build_GOWEB1_go.exe D:/GO/main/GOWEB1.go #gosetup
C:\Users\Administrator\AppData\Local\Temp\___go_build_GOWEB1_go.exe #gosetup
map[]
path /
scheme 
[]
map[url_long:[111 222]]
path /
scheme 
[111 222]
key: url_long
val: 111222
map[url_long:[111 222]]
path /
scheme 
[111 222]
key: url_long
val: 111222
  • Web工作方式的几个概念

    • Request:用户请求的信息,用来解析用户的请求信息,包括 post、get、cookie、url 等信息
    • Response:服务器需要反馈给客户端的信息
    • Conn:用户的每次请求链接
    • Handler:处理请求和生成返回信息的处理逻辑
  • 如何监听端口、接收客户端请求、分配handller?

ListenAndServe 来处理这些事情的,这个底层其实这样处理的:初始化一个 server 对象,然后调用了 net.Listen("tcp", addr),也就是底层用 TCP 协议搭建了一个服务,然后监控我们设置的端口。

HTTP包源码:

func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    var tempDelay time.Duration // how long to sleep on accept failure
    for {
        rw, e := l.Accept()
        if e != nil {
            if ne, ok := e.(net.Error); ok && ne.Temporary() {
                if tempDelay == 0 {
                    tempDelay = 5 * time.Millisecond
                } else {
                    tempDelay *= 2
                }
                if max := 1 * time.Second; tempDelay > max {
                    tempDelay = max
                }
                log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay)
                time.Sleep(tempDelay)
                continue
            }
            return e
        }
        tempDelay = 0
        c, err := srv.newConn(rw)
        if err != nil {
            continue
        }
        go c.serve()
    }
}
  • GO的HTTP包详解

GO的http有两个核心功能:Conn、ServeMux

GO在等待客户端请求里面是这样写的:

c,err := srv.newConn(rw)
if err != nil {
    continue
}
go c.serve()

Handler定义:

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)  // 路由实现器
}

HandlerFunc定义:

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)     //f拥有了ServerHttp方法
}

默认的路由器对Handler接口的实现:

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
    if r.RequestURI == "*" {
        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)
也就是调用对应路由的 handler 的 ServerHTTP 接口。

Handler方法:

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
    if r.Method != "CONNECT" {
        if p := cleanPath(r.URL.Path); p != r.URL.Path {
            _, pattern = mux.handler(r.Host, p)
            return RedirectHandler(p, StatusMovedPermanently), pattern
        }
    }   
    return mux.handler(r.Host, r.URL.Path)
}

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
}

Go 其实支持外部实现的路由器 ListenAndServe 的第二个参数就是用以配置外部路由器的,它是一个 Handler 接口,即外部路由器只要实现了 Handler 接口就可以,我们可以在自己实现的路由器的 ServeHTTP 里面实现自定义路由功能。

package main

import (
    "fmt"
    "net/http"
)

type MyMux struct {
}

func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path == "/" {
        sayhelloName(w, r)
        return
    }
    http.NotFound(w, r)
    return
}

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

func main() {
    mux := &MyMux{}
    http.ListenAndServe(":9090", mux)  //mux自定义的路由
}

main例:

func main() {
    http.HandleFunc("/", sayhelloName) // 设置访问的路由
    //""中为访问的路由,sayhelloName为调用的Handler(自己定义的)
    err := http.ListenAndServe(":9090", nil) // 设置监听的端口
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}
上一篇:linux shell数学函数实现


下一篇:`DefaultServeMux` 与 `gorilla/mux`部分源码阅读