许世伟的Go语言基础 第五章总结

第5章 网络编程

5.1 socket编程

以往socket编程:

  1. 建立socket:使用socket()函数。
  2. 绑定socket:使用bind()函数。
  3. 监听:使用listen()函数,或连接使用connect()函数。
  4. 接受连接:使用accept()函数。
  5. 接收:使用recevie()函数。或者发送:使用send()函数。

Dial()函数

//TCP连接
conn,err := net.Dial("tcp","ip:port")
//UDP连接
conn.err := net.Dial("udp","ip:port")
//ICMP连接
conn.err := net.Dial("ip4:icmp","ip:port")
//发数据
conn.Write()
//收数据
conn.Read()
由于Read()和Write()方法只接收字节数组,所以只能先写入[]byte 再写入bytes.Buffer。
Read()方法读到末尾也会 

TCP连接示例

func main()  {
    if len(os.Args != 2){
        fmt.Fprint(os.Stderr,"Usage: %s host:port",os.Args[0])
        os.Exit(1)
    }
    inetAddress := os.Args[1]
    conn,err :=net.Dial("tcp",inetAddress)

    result,err := readFully(conn)
    checkError(err)
    fmt.Print(string(result))
    os.Exit(0)//中断程序,并返回自定义的int,一般0指正常退出
}
func checkError(err error)  {
    if err != nil{
        fmt.Fprint(os.Stderr,"Fatal error:%s",err.Error())
        os.Exit(1)
    }
}
func readFully(conn net.Conn)  {
    defer conn.Close()//关连接
    result := bytes.NewBuffer(nil)//双缓存,一缓存一结果
    var buf [512]byte
    for{//循环写入
        n,err := conn.Read(buf[0:])
        result.Write(buf[0:n])
        if err != nil{
            if err == io.EOF{//判尾
                break
            }
            return  nil,err//向上抛出异常
        }
    }
    return result.Bytes(),nil
}

fmt.Fprintf的api:

func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)

Dial()是对DialTCP()、DialUDP()、DialIP()和DialUnix()的封装。

TCP连接示例2

func main()  {
    if(len(os.Args)!=2){
        fmt.Fprint(os.Stderr,"Usage: %s host:port",os.Args[0])
        os.Exit(1)
    }

    inetAddress := os.Args[1]
    tcpAddr,err := net.ResolveTCPAddr("tcp4",inetAddress)
    checkError(err)

    conn,err := net.DialTCP("tcp",nil,tcpAddr)
    _,err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
    checkError(err)

    result,err := ioutil.ReadAll(conn)//有现成的读数的函数
    checkError(err)
    fmt.Println(string(result))
    os.Exit(0)
}
func checkError(err error)  {
    if err != nil{
        fmt.Fprint(os.Stderr,"Fatal error:%s",err.Error())
        os.Exit(1)
    }
}

这个示例仅用于原理的理解,不记。

与示例1不同点:

  • net.ResolveTCPAddr(),用于解析地直和端口号。
  • net.DialTCP(),用于建立连接。

验证ip有效性:

func arse(IP)

创建子网掩码:

func IPv4Mask(a,b,c,d,byte) IPMask

获取默认子网掩码:

func (ip IP) DefaultMask() IPMask

根据域名查找IP代码:

func ResolveIPAddr(net, addr string) (*IPAddr,error)
func LoopupHost(name string)(cname string,addrs []string,err error)

5.2 HTTP编程

基本方法

http.Get(url string)方法等价于http.DefaultClient.Get()方法。

func main() {
    resp,err := http.Get("http://example.com")
    if err!= nil{
        return
    }
    defer resp.Body.Close()
    io.Copy(os.Stdout,resp.Body)//不能用fmt.Print打印,那样只能打印地址。这里用了流的 
}

io.Copy的api:

func Copy(dst Writer, src Reader) (written int64, err error)

http.Post()参数:

  • 请求url

  • 将要postr的数据类型(MIMEType)

  • 数据的比特流([]byte)

    func main() {
    imageDataBuf := []byte
    resp,err := http.Post("http://example.com/upload","image/jpeg",&imageDataBuf)
    if err !=nil{
    return
    }
    if resp.StatusCode != http.StatusOK{
    return
    }
    }

http.PostForm()实现标准编码格式application/x-www-form-urlencoded的表单提交。

func main() {
    resp,err := http.PostForm("http://example.com/posts",url.Values{"title":{"article title"},"content":{"article body"}})
    if err != nil{
        return
    }
    io.Copy(os.Stdout,resp.StatusCode)
}

http.Head(url string)只请求目标的头部信息,不返回body。

需要定制化地发送Get()和Post()请求,如设定User-Agent,传递cookie,使用(http.Client)Do()。

func main()  {
    req,err := http.NewRequest("GET","http://example.com",nil)
    req.Header.Add("User-Agent","Gobook Custom User-Agent")
    client := &http.Client{}
    resp,err := client.Do(req)
}

http.NewRequest

func NewRequest(method, url string, body io.Reader) (*Request, error)

自定义http.Client

type Client struct { 
    // Transport用于确定HTTP请求的创建机制。 
    // 如果为空,将会使用DefaultTransport 
    Transport RoundTripper 
    // CheckRedirect定义重定向策略。 
    // 如果CheckRedirect不为空,客户端将在跟踪HTTP重定向前调用该函数。 
    // 两个参数req和via分别为即将发起的请求和已经发起的所有请求,最早的 /
    // / 已发起请求在最前面。 // 如果CheckRedirect返回错误,客户端将直接返回错误,不会再发起该请求。 
    // 如果CheckRedirect为空,Client将采用一种确认策略,将在10个连续 // 请求后终止 
    CheckRedirect func(req *Request, via []*Request) error 
    // 如果Jar为空,Cookie将不会在请求中发送,并会 
    // 在响应中被忽略 
    Jar CookieJar

}

CheckRedirect函数指定处理重定向策略。发送的请求如果状态码30x,httpClient会在遵循跳转规则之前先调用这个CheckRedirect函数。

CookieJar接口预定义了SetCookies()和Cookies()两个方法。

func main()  {
    client := &http.Client{
        CheckRedirect: redirectPolicyFunc,
    }
    resp,err := client.Get("http://example.com")
    req,err := http.NewRequest("GET","http://example.com",nil)
    req.Header.Add("User-Agent","Our Custom User-Agent")
    req.Header.Add("If-None-Match","W/TheFileEtag")
    resp,err := client.Do(req)
}

自定义http.Transport

type Transport struct { 
    // Proxy指定用于针对特定请求返回代理的函数。 
    // 如果该函数返回一个非空的错误,请求将终止并返回该错误。 
    // 如果Proxy为空或者返回一个空的URL指针,将不使用代理 
    Proxy func(*Request) (*url.URL, error) 
    // Dial指定用于创建TCP连接的dail()函数。 
    // 如果Dial为空,将默认使用net.Dial()函数 
    Dial func(net, addr string) (c net.Conn, err error) 
    // TLSClientConfig指定用于tls.Client的TLS配置。 
    // 如果为空则使用默认配置 
    TLSClientConfig *tls.Config 
    DisableKeepAlives bool 
    DisableCompression bool 
    // 如果MaxIdleConnsPerHost为非零值,它用于控制每个host所需要 
    // 保持的最大空闲连接数。如果该值为空,则使用DefaultMaxIdleConnsPerHost 
    MaxIdleConnsPerHost int 
    // ...
}
  • Proxy方法返回一个代理,如果未指定或者*URL为零值,则不会启动代理。
  • Dial()创建tcp连接。
  • TLSClientConfig为ssl连接专用,指定tls客户端配置,如果不指定则使用默认。
  • DisableKeepAlives是否取消常连接,默认false,即启用常连接。
  • DisableCompression是否取消压缩,默认false,即启用压缩。
  • MaxIdleConnsPerHost指定最大非活跃连接数量。

其它公开的成员方法:

  • func(t *Transport) CloseIdleConnections() 。 该方法用于关闭所有非活跃的
    连接。

  • func(t *Transport) RegisterProtocol(scheme string, rt RoundTripper)。 该方法可用于注册并启用一个新的传输协议,比如 WebSocket 的传输协议标准(ws),或 者 FTP、File 协议等。

  • func(t *Transport) RoundTrip(req *Request) (resp *Response, err error)。 用于实现 http.RoundTripper 接口。

    func main() {
    tr := &http.Transport{
    TLSClientConfig: &tls.Config{RootCAs:pool},
    DisableCompression: true,
    }
    client := &http.Client{Transport:tr}
    resp,err := client.Get("https:/example.com")
    }

Client和Transport在执行多个goroutine的并发过程是线程安全的,但出于性能考虑,应当创建一次后反复使用。

http.RoundTripper接口

type RoundTripper interface { 
    // RoundTrip执行一个单一的HTTP事务,返回相应的响应信息。 
    // RoundTrip函数的实现不应试图去理解响应的内容。如果RoundTrip得到一个响应, 
    // 无论该响应的HTTP状态码如何,都应将返回的err设置为nil。非空的err 
    // 只意味着没有成功获取到响应。 
    // 类似地,RoundTrip也不应试图处理更高级别的协议,比如重定向、认证和Cookie等。 
    // // RoundTrip不应修改请求内容, 除非了是为了理解Body内容。每一个请求 
    // 的URL和Header域都应被正确初始化 
    RoundTrip(*Request) (*Response, error)
}

RoundTrip()方法用于执行独立的HTTP事务。

实现时不应在方法中解析HTTP响应。若响应成功,err值必须为nil,而与响应状态码无关。

类似也不要试图在RoundTrip()方法中处理协议层面的细节,如重定向,认证或是cookie等。

非必要情况下也不应该修改请求体*Request。

type CustomTransport struct {
    Transport http.RoundTripper
}
func (t *CustomTransport) transport() http.RoundTripper {
    if t.Transport != nil{
        return t.Transport
    }
    return http.DefaultTransport
}
func (t *CustomTransport) RoundTrip(req *http.Request) (*http.Response,error) {
    //发起http请求
    //添加一些域到req.Header
    return t.transport().RoundTrip(req)
}
func (t *CustomTransport)Client() *http.Client {
    return &http.Client{Transport: t}//结构体直接传变量进行初始化
}

http.Client被设计成上下两层,一层是业务层:初始化HTTP Method 、目标url、请求参数、请求内容。一层是传输层细节,包括:http底层传输细节、http代理、gzip压缩、连接池管理、SSL认证等。

http服务端

处理http请求

func ListenAndServe(addr string, handler Handler) error

用于在指定的 TCP 网络地址 addr 进行监听,然后调用服务端处理程序来处理传入的连接请求。

第二个参数为服务端处理程序,通常为空。意味着调用http.DefaultServeMux进行处理,而服务端编写的业务逻辑处理http.Handle()或http.HandleFunc()默认注入到http.DefaultServeMux。

func main() {
    http.Handle("/foo",fooHandler)
    http.HandleFunc("/bar",func(w http.ResponseWriter,r *http.Request){
        fmt.Fprintf(w,"Hello,%q",html.EscapeString(r.URL.Path))
    })
    log.Fatal(http.ListenAndServe(":8080",nil))
}

如果想更多地控制服务端的行为,可以自定义 http.Server。

s := &http.Server{
        Addr: ":8080",
        Handler: myHandler,
        ReadTimeout: 10*time.Second,
        WriteTimeout: 10*time.Second,
        MaxHeaderBytes: 1<<20,
    }
    log.Fatal(s.ListenAndServe())

处下https请求

func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) error

certFile对应SSL证书 文件存放路径,keyFile对应证书私钥文件路径。如果证书是由证书颁发机构签署的,certFile 参数指定的路径必须是存放在服务器上的经由CA认证过的SSL证书。

5.4 JSON处理

较xml更轻量级,易于阅读的解析,跨语言,跨平台。go语言使用内置库encoding/json库处理。

type Book struct {
    Title     string
    Authors   []string
    Publisher string
    Price     float32
}

func main() {
    gobook := Book{
        Title: "Go编程", //只用作数据传输的结构体直接写,不用构造函数
        Authors: []string{"A", "B"}, //切片在用大括号初始化
        Publisher: "turning.com",
        Price: 9.9, //结构体构造,最后一个也要逗号
    }
    _,err := json.Marshal(gobook)//编码

    if err != nil {
        fmt.Print("suc")
    }
}

大多数数据类型都可以转化为有效的JSON文本,但channel、complex和函数除外。

如果数据结构中含有指针,将会转化指针所指向的值。如果指针指向0值,则输入null。

go中json中数据映射:

  • 布尔型不变
  • 整数和浮点转为数字串
  • 字串以UTF-8转为Unicode字符集的字符串。
  • 数组和切片转为json里的数组,
  • 结构体转为json对象,仅开头字母大小的

解码json数据

func Unmarshal(data []byte, v interface{}) error

该方法根据如下约定查找字段,如查找Foo

  • 一个包含Foo的字段
  • 一个名为Foo的字段
  • 一个名为Foo或除了首字母其它字母不区分大小写的名为Foo的字段。

如果字段在Go目标类型中不存在则丢弃该字段。同时私有字段不受影响。

解码未知结构的json数据

解码未知结构的数据只需要输入到空接口。解码成map[string]interface{}结构。

    b :=[]byte(`{
    "Title": "Go编程",
    "Price": 9.99
    }`)
    var r interface{}
    _ := json.Unmarshal(b,&r)
    //判断目标结构是否是符合预期
    gobook,ok := r.(map[string]interface{})//类型判断
    if ok{
        for k,v := range gobook{//遍历哈希
            switch v2 := v.(type) {//取类型
            case string:
                fmt.Print("is string")
            case []interface{}:
                fmt.Print("is an array")
            default:
                fmt.Print("is another type not handle yet")
            }
        }
    }

json流式读写

func NewDecoder(r io.Reader) *Decoder 
func NewEncoder(w io.Writer) *Encoder

func main() {
    dec :=json.NewDecoder(os.Stdin)
    enc :=json.NewEncoder(os.Stdout)
    for {
        var v map[string]interface{}
        if err := dec.Decode(&v); err != nil {
            log.Println(err)
            return
        }
        for k := range v {
            if k != "Title" {
                v[k] = nil
            }
        }
        if err := enc.Encode(&v); err != nil {
            log.Println(err)
        }
    }
}

json流式读写的编解码都是包装流,以对象指针为形参。

你可能感兴趣的:(许世伟的Go语言基础 第五章总结)