《Network Programming With Go》不是很深入。

3. Socket level Programming

socket

// resolve IP
package main

import (
    "fmt"
    "net"
    "os"
)

func main() {
    name := "www.baidu.com"
    addr, err := net.ResolveIPAddr("ip", name)
    if err != nil {
        fmt.Println("Resolution error", err.Error())
        os.Exit(1)
    }
    fmt.Println("addr ip is ", addr.String())
}

编写一个时间回显tcp服务器:

// DaytimeServer
// telnet localhost 1200
package main

import (
    "fmt"
    "net"
    "os"
    "time"
)

func main() {
    service := ":1200"
    tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
    checkError(err)

    listener, err := net.ListenTCP("tcp", tcpAddr)
    checkError(err)

    for {
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
        daytime := time.Now().String()
        conn.Write([]byte(daytime))
        conn.Close()
    }
}

func checkErr(err error) {
    if err != nil {
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
        os.Exit(1)
    }
}

编写一个简单的tcp回显服务器:
// SimpleEchoServer
package main

import (
    "fmt"
    "net"
    "os"
)

func main() {
    service := ":1201"
    tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
    checkError(err)

    listener, err := net.ListenTCP("tcp", tcpAddr)
    checkError(err)

    for {
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
        handleClient(conn)
        conn.Close() // close  the client
    }
}
func handleClient(conn net.Conn) {
    var buf [512]byte
    for {
        n, err := conn.Read(buf[0:])
        if err != nil {
            return
        }
        fmt.Println(string(buf[0:]))
        _, err2 := conn.Write(buf[0:n])
        if err2 != nil {
            return
        }
    }
}

func checkError(err error) {
    if err != nil {
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
        os.Exit(1)
    }
}

可以很容易修改成并发的,使用 goroutine

// SimpleEchoServer
package main

import (
    "fmt"
    "net"
    "os"
)

func main() {
    service := ":1201"
    tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
    checkError(err)

    listener, err := net.ListenTCP("tcp", tcpAddr)
    checkError(err)

    for {
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
        go handleClient(conn) // NOTE: use goroutine
    }
}
func handleClient(conn net.Conn) {
    defer conn.Close() // NOTE: close connection on exit
    var buf [512]byte
    for {
        n, err := conn.Read(buf[0:])
        if err != nil {
            return
        }
        fmt.Println(string(buf[0:]))
        _, err2 := conn.Write(buf[0:n])
        if err2 != nil {
            return
        }
    }
}

func checkError(err error) {
    if err != nil {
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
        os.Exit(1)
    }
}

Controlling TCP connections

// Timeout
func (c *TCPConn) SetTimeout(nsec int64) os.Error

// Staying alive, a client wish to stay connected to a server even if it has nothing to send
func (c *TCPConn) SetKeepAlive(keepalive bool) os.Error

UDP client server demo:

// UDPDaytimeClient
package main

import (
    "fmt"
    "net"
    "os"
)

func main() {
    service := "localhost:1200"
    udpAddr, err := net.ResolveUDPAddr("udp4", service)

    conn, err := net.DialUDP("udp", nil, udpAddr)
    checkError(err)

    _, err = conn.Write([]byte("anything"))
    checkError(err)
    var buf [512]byte
    n, err := conn.Read(buf[0:])
    checkError(err)

    fmt.Println(string(buf[0:n]))
}
func checkError(err error) {
    if err != nil {
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
        os.Exit(1)
    }
}

// udpserver.go
package main

import (
    "fmt"
    "net"
    "os"
    "time"
)

func main() {
    service := ":1200"
    updAddr, err := net.ResolveUDPAddr("udp4", service)
    checkError(err)
    conn, err := net.ListenUDP("udp", updAddr)
    checkError(err)
    for {
        handleClient(conn)
    }
}

func handleClient(conn *net.UDPConn) {
    var buf [512]byte
    _, addr, err := conn.ReadFromUDP(buf[0:])
    if err != nil {
        return
    }
    daytime := time.Now().String()
    conn.WriteToUDP([]byte(daytime), addr)
}

func checkError(err error) {
    if err != nil {
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
        os.Exit(1)
    }
}

Raw sockets and the type IPConn

// Ping
package main

import (
    "fmt"
    "net"
    "os"
)

func checkError(err error) {
    if err != nil {
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
        os.Exit(1)
    }
}
func main() {
    if len(os.Args) != 2 {
        fmt.Println("Usage: ", os.Args[0], "host")
        os.Exit(1)
    }
    addr, err := net.ResolveIPAddr("ip", os.Args[1])
    if err != nil {
        fmt.Println("Resolution error", err.Error())
        os.Exit(1)
    }

    fmt.Println(addr)
    conn, err := net.DialIP("ip4:icmp", addr, addr)
    checkError(err)

    var msg [512]byte
    msg[0] = 8  //echo
    msg[1] = 0  //code 0
    msg[2] = 0  //checksum, fix later
    msg[3] = 0  //checksum, fix later
    msg[4] = 0  //identifier[0]
    msg[5] = 13 //identifier[1]
    msg[6] = 0  // sequence[0]
    msg[7] = 37 //sequence[1]
    len := 8

    check := checkSum(msg[0:len])
    msg[2] = byte(check >> 8)
    msg[3] = byte(check & 255)

    _, err = conn.Write(msg[0:len])
    checkError(err)

    _, err = conn.Read(msg[0:])
    checkError(err)

    fmt.Println("Got response")
    if msg[5] == 13 {
        fmt.Println("identifier matches")
    }
    if msg[7] == 37 {
        fmt.Println("Sequence matches")
    }
}

func checkSum(msg []byte) uint16 {
    sum := 0
    for n := 1; n < len(msg)-1; n += 2 {
        sum += int(msg[n])*256 + int(msg[n+1])
    }
    sum = (sum >> 16) + (sum & 0xffff)
    sum += (sum >> 16)
    answer := uint16(^sum)
    return answer
}

4. Data serialisation

  • ASN.1
  • JSON
  • gob
  • base64

5. Application Level Protocols

  • version control
  • message format
  • data format: byte encoded or character encoded
  • state

6. Managing character sets and encodings

Go use utf8 encoded characters in its strings. Each character is of type rune(alias for int32) as a Unicode character can be 1,2 or 4 bytes in UTF8 encoding. A string is an array of rune.

7. Security

Data integrity (数据完整性)

// MD5 Has
package main

import (
    "crypto/md5"
    "fmt"
)

func main() {
    hash := md5.New()
    bytes := []byte("hello\n")
    hash.Write(bytes)
    hashValue := hash.Sum(nil)
    hashSize := hash.Size()
    // print out in ASCII from as four hexadcimal numbers
    for n := 0; n < hashSize; n += 4 {
        var val uint32
        val = uint32(hashValue[n])<<24 +
            uint32(hashValue[n+1])<<16 +
            uint32(hashValue[n+2])<<8 +
            uint32(hashValue[n+3])
        fmt.Printf("%x ", val)
    }
}

Symmetric key encryption

  • Blowfish
  • DES

Public key encryption

  • crypto/rsa

X.509 certificates

A public key infrastructure(PKI) is a framework for a collections of public keys, along with additional information such as owner name and location.

TLS

  • crypto/tls

8. HTTP

HTTP client

package main

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

func main() {
    url := "http://127.0.0.1:8000"
    response, err := http.Get(url)
    if err != nil {
        fmt.Println(err.Error())
        os.Exit(2)
    }
    if response.Status != "200 OK" {
        fmt.Println(response.Status)
        os.Exit(2)
    }
    // b, _ := httputil.DumpResponse(response, false)
    // fmt.Print(string(b))

    var buf [512]byte
    reader := response.Body
    for {
        n, err := reader.Read(buf[0:])
        if err != nil {
            fmt.Println(err)
            os.Exit(0)
        }
        fmt.Print(string(buf[0:n]))
    }

}

Proxy handling:

proxyURL, err := url.Parse(proxyString) // http://proxy-host:port
transport := &http.Transport{Proxy: http.ProxyURL(proxyURL)}
client := &http.Client{Transport: transport}
// func ProxyFromEnvironment(req *Request) (*url.URL, error)

Servers

  • File server
package main

import "net/http"

func main() {
    fileServer := http.FileServer(http.Dir("/home/httpd/html"))
    err := http.ListenAndServe(":8000", fileServer)
    checkError(err)
}
  • Handler function
func Handle(pattern string, handler Handler)
func HandleFunc(pattern string, handler func(*Conn, *Request))

9. Templates

  • html/template, text/template
  • pipelines: {{. | html}}
  • template.FuncMap{"emailExpand": EmailExpand}
  • variables in templates are prefixed by '$'
  • conditional