Go 语言集训(六):安全编程

数据加密

  • 单密钥加密算法,对称加密。需要加密的明文、加密算法和密钥。 DES,AES,RC4等
  • 双密钥加密算法,非对称加密。需要加密的明文、加密算法、私钥和公钥。该系统中,私钥和公钥可以用来加密或者解密,但是私钥加密的明文,必须要用对应的公钥解密。用公钥加密的明文,必须用对应的私钥解密。

数字签名

数字签名,是指用于标记数字文件拥有者、创造者、分发者身份的字符串。数字前民更拥有标记文件身份、分发的不可抵赖性等作用。
如:A公司发布了一个可执行文件,成为AProduct.exe, A在Aproduct.exe中加入A公司的数字签名。A公司的数字签名是A公司的私钥加密了AProduct.exe文件的哈希值,我们得到打过数字签名的AProduct.exe后,可以查看数字签名。这个过程就是A公司的公钥解密了文件哈希值。从而验证两个问题:Aproduct.exe是否A公司发布,AProduct.exe是否被篡改。

数字证书

数字证书中包含了银行的公钥,有了公钥后,网银就可以用公钥加密我们提供给银行的信息,这样银行才能用对应的私钥得到我们信息,确保安全。

PKI体系

PKI全称公钥基础设施,使用非对称加密理论,提供数字签名、加密、数字证书等服务体系。一般包括权威认证机构CA,数字证书库,密钥备份及恢复系统、证书作废系统、应用接口API等。

Go语言集训(五):网络编程

网络编程

在Go语言中编写网络程序时,我们看不到传统的那种编码形式。原来使用socket编程是,会按照以下步骤:
1. 建立socket : socket()
2. 绑定socket: bind()
3. 监听: listen() 或者链接:connect()
4. 接受连接: accept()
5. 接收: receive() 或者发送:send
在GO语言中,只需要调动net.Dial()即可。

Dial 函数

func Dial(net, addr string) (Conn, error)
* net 是网络协议的名字
* addr 是IP地址或者域名,端口号以“:”的形式跟随在抵制或者域名的后面,端口号可选。
* 目前支持tcp,tcp4,tcp6,udp,udp4,udp6,ip,ip4,ip6
* 如果ICMP可以使用:ip4:ICMP 或者 ip4:1
成功建立连接后,我们可以进行数据的发送和接收。发送数据时,使用conn的write(),接收是采用Read()

TCP 实例

package main

import (
    "bytes"
    "fmt"
    "io"
    "net"
    "os"
)

func main() {

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

    service := os.Args[1]

    conn, err := net.Dial("tcp", service)
    checkError(err)

    _, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
    checkError(err)

    result, err := readFully(conn)
    checkError(err)

    fmt.Println(string(result))

    os.Exit(0)

}

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

func readFully(conn net.Conn) ([]byte, error) {
    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
}

HTTP客户端

Go内置的net/http提供了最简单的http客户端实现,我们不需要借助第三方网络通信库如libcurl就可以直接使用http中用的最多的GET和POST方式请求数据
1. 基本方法
net /http包的Client类型提供了如下几个方法:

* func(c *Client) Get (url string) (r *Response, err error)
* func(c *Client) Post (url string, bodyType string, body io.Reader) (r *Response, err error)
* func(c *Client) PostForm(url string, data url.Values) (r *Response, err error)
* func(c *Client) Head(url string) (r *Response , err error)
* func(c *Client) Do(req *Request) (resp *Response, err error)
  1. 概要介绍

* http.Get()

resp, err := http.Get("http://www.baidu.com")
if err != nil {
    // handle error
    return
}
defer resp.Body.close()
io.Copy(os.Stdout, resp.Body)
// 上面这段代码请求一个网页首页,并将网页内容打印到标准输出上。
  • http.Post()
resp, err := http.Post("http://www.baidu.com/upload","image/jpeg",&imageDataBuf)
if err != nil {
   //handle error
   return
}
if resp.StatusCode != http.StatusOK {
  //handle error
  return
}
  • http.PostForm()
// 实现了标准编码格式为application/x-www-form-urlencoded的表单提交
resp, err := http.PostForm("http://www.baidu.com/posts", url.Values("title":{"article title"},"content":{"article body"}))
if err != nil {
   //handle error
   return
}
  • http.Head()
//请求目标URL的头部信息
resp, err := http.Head("www.baidu.com")
  • (*http.Client).Do()
//在多数情况下http.Get()和http.PostForm()能够满足需求,但是需要我们发起的HTTP请求需要更多的定制信息。比如传递cookie,设置自定义的“User-Agent”
req, err := http.NewRequest("GET","http://www.baidu.com",nil)
req.Header.Add("User-Agent", "Gobook Custom User-Agent")
client := & http.Client{}
resp, err := client.Do(req)

HTTP服务端

  1. 处理HTTP请求
    func ListenAndServe(add string, handler Handler) error

* addr即坚挺地址
* handle为服务端处理程序。通常为空,意味着调用http.DefaultServeMux处理。服务端编写业务逻辑http.Handle() 或者http.HandleFunc()默认是诸如到http.DefaultServeMux中。

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.ListenAndServer(":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.ListenAndServer())
  1. 处理HTTPS请求
    func ListenAndServeTLS(add string,certFile string, keyFile string, handler Handler) error

* addr即坚挺地址
* certFile对应SSL证书存放的路径
* keyFile对应证书私钥文件路径
* handle为服务端处理程序。通常为空,意味着调用http.DefaultServeMux处理。服务端编写业务逻辑http.Handle() 或者http.HandleFunc()默认是诸如到http.DefaultServeMux中。

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.ListenAndServerTLS(":10443","cert.pem", "key.pem",nil))

//如果更多控制服务端的行为,可以自动以http.Server

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

RPC编程

RPC协议构架于TCP或者UDP,或者HTTP上,允许开发者直接调用另一台计算机上的程序,而无需额外调用编写网络通信相关代码。使得开发包括网络分布式程序在内的应用程序更加容易
net/rpc允许RPC客户端通过网络或者IO连接调用一个远端对象的公开方法(必须是大写字母开头、可外部调用的)。在RPC服务端,可将一个对象注册为可访问的服务,之后该对象的公开方法就能能够以远程的方式提供访问。一个RPC服务端可以注册多个不同类型的对象,但不允许注册同一类型的多个对象。
一个对象需要满足以下条件,才能够提供远程访问:
1. 必须是在对象外部可公开调用的方法
2. 必须有两个参数,且参数的类型都必须是包外部可以访问的类型,或者Go内建支持的类型
3. 第二个参数必须是一个指针
4. 方法必须返回一个error类型的值
func (t *T) MethodName(argType T1, replyType *T2) error

在RPC客户端,GO的net/rpc包提供便利的rpc.Dial()和rpc.DialHTTP()方法来于指定RPC服务端建立连接。建立后,go的net/rpc包允许我们使用同步或者异步的方式接收rpc服务端的结果。调用rpc客户端的call方法进行同步处理,这时候客户端程序按顺序执行,只有接收完RPC服务端的处理结果之后,才可能继续执行后面的程序。当调用RPC客户端的Go方法,进行异步处理,RPC客户端无需等待服务端的结果即可执行后面的程序,而当接收到RPC服务端的处理结果时,再对其进行相应的处理。无论是调用RPC客户端的Call还是Go,都必须制定要调用的服务及其方法名称,以及一个客户端传入参数的引用,还有一个用于接收处理结果参数的指针。
如果没有明确制定RPC传输过程中采用何种编码,默认将使用encoding/gob包进行数据传输。

package server

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

type Args struct {
    A, B int
}

type Quotient struct {
    Quo, Rem int
}

type Arith int

func (t *Arith) Multiply(args *Args, reply *int) error {
    *reply = args.A * args.B
    return nil
}

func (t *Arith) Divide(args *Args, reply *Quotient) error {
    reply.Quo = args.A / args.B
    reply.Rem = args.A % args.B
    return nil
}

// 服务端侦听
func main() {
    arith := new(Arith)
    rpc.Register(arith)
    rpc.HandleHTTP()

    l, e := net.Listen("tcp", ":1234")
    if e != nil {
        log.Fatal("Listen error: ", e)
    }
    go http.Serve(l, nil)
}

//客户端连接调用

func client() {
    client, err := rpc.DialHTTP("tcp", serverAddress+":1234")

    args := &server.Args{7, 8}
    var reply int
    err = client.Call("Arith.Multiply", args, &reply)

    quotient := new(Quotient)
    divCall := client.Go("Arith.Divid", args, "ient, nil)
    replyCall := <-divCall.Done
}

json编解码

空接口是通用类型。如果要解码一段位置结构的JSON,只需要将这段json数据解码输出到一个空接口接口。
* JSON中布尔值会专为Go中bool
* 数值–float64
* 字符串–string
* json数组会转换为[]interface{}
* json对象会转换为map[string]interface{}
* null — nil



func json() { b := []byte(`{ "Title": "Go语言编程", "Authors": ["XuShiwei", "HughLv", "Pandaman", "GuaguaSong", "HanTuo", "BertYuan", "XuDaoli"], "Publisher": "ituring.com.cn", "IsPublished": true, "Price": 9.99, "Sales": 1000000 }`) var r interface{} err := json.Unmarshal(b, &r) // //map[string]interface{}{ // "Title": "Go语言编程", // "Authors": ["XuShiwei", "HughLv", "Pandaman", "GuaguaSong", "HanTuo", "BertYuan", // "XuDaoli"], // "Publisher": "ituring.com.cn", // "IsPublished": true, // "Price": 9.99, // "Sales": 1000000 //} }

简单web界面

目录结构:
uploads:上传文件目的地
views: html文件 包含两个list.html 和 upload.html
photoweb.go

//photoweb.go
package main

import (
    "fmt"
    "html/template"
    "io"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "path"
    "runtime/debug"
    "strings"
)

const (
    ListDir      = 0x0001
    UPLOAD_DIR   = "./uploads"
    TEMPLATE_DIR = "./views"
)

var templates = make(map[string]*template.Template)

func init() {
    fileInfoArr, err := ioutil.ReadDir(TEMPLATE_DIR)
    check(err)
    var templateName, templatePath string
    for _, fileInfo := range fileInfoArr {
        templateName = fileInfo.Name()
        if ext := path.Ext(templateName); ext != ".html" {
            continue
        }
        templatePath = TEMPLATE_DIR + "/" + templateName
        log.Println("Loading template:", templatePath)
        t := template.Must(template.ParseFiles(templatePath))
        if strings.Contains(templateName, "upload") {
            fmt.Println("upload....")
            templates["upload"] = t
        }
        if strings.Contains(templateName, "list") {
            fmt.Println("list....")
            templates["list"] = t
        }
    }
}
func check(err error) {
    if err != nil {
        panic(err)
    }
}
func renderHtml(w http.ResponseWriter, tmpl string, locals map[string]interface{}) {
    err := templates[tmpl].Execute(w, locals)
    check(err)
}
func isExists(path string) bool {
    _, err := os.Stat(path)
    if err == nil {
        return true
    }
    return os.IsExist(err)
}
func uploadHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method == "GET" {
        renderHtml(w, "upload", nil)
    }
    if r.Method == "POST" {
        f, h, err := r.FormFile("image")
        check(err)
        filename := h.Filename
        defer f.Close()
        imagePath := UPLOAD_DIR + "/" + filename
        t, err := os.OpenFile(imagePath, os.O_WRONLY|os.O_CREATE, 0666)
        check(err)
        defer t.Close()
        _, err1 := io.Copy(t, f)
        check(err1)
        http.Redirect(w, r, "/view?id="+filename, http.StatusFound)
    }
}
func viewHandler(w http.ResponseWriter, r *http.Request) {
    imageId := r.FormValue("id")
    imagePath := UPLOAD_DIR + "/" + imageId
    if exists := isExists(imagePath); !exists {
        http.NotFound(w, r)
        return
    }
    w.Header().Set("Content-Type", "image")
    http.ServeFile(w, r, imagePath)
}
func listHandler(w http.ResponseWriter, r *http.Request) {
    fileInfoArr, err := ioutil.ReadDir("./uploads")
    check(err)
    locals := make(map[string]interface{})
    images := []string{}
    for _, fileInfo := range fileInfoArr {
        images = append(images, fileInfo.Name())
        fmt.Println(images)
    }
    locals["images"] = images
    renderHtml(w, "list", locals)
}
func safeHandler(fn http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if e, ok := recover().(error); ok {
                http.Error(w, e.Error(), http.StatusInternalServerError)
                // 或者输出自定义的50x错误页面
                // w.WriteHeader(http.StatusInternalServerError)
                // renderHtml(w, "error", e)
                // logging
                log.Println("WARN: panic in %v. - %v", fn, e)
                log.Println(string(debug.Stack()))
            }
        }()
        fn(w, r)
    }
}
func staticDirHandler(mux *http.ServeMux, prefix string, staticDir string, flags int) {
    mux.HandleFunc(prefix, func(w http.ResponseWriter, r *http.Request) {
        file := staticDir + r.URL.Path[len(prefix)-1:]
        if (flags & ListDir) == 0 {
            if exists := isExists(file); !exists {
                http.NotFound(w, r)
                return
            }
        }
        http.ServeFile(w, r, file)
    })
}
func main() {
    mux := http.NewServeMux()
    staticDirHandler(mux, "/assets/", "./public", 0)
    mux.HandleFunc("/", safeHandler(listHandler))
    mux.HandleFunc("/view", safeHandler(viewHandler))
    mux.HandleFunc("/upload", safeHandler(uploadHandler))
    err := http.ListenAndServe(":8080", mux)
    if err != nil {
        log.Fatal("ListenAndServe: ", err.Error())
    }
}

list.html

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>List</title>
</head>
<body>
<ol>
{{range $.images}}
<li><a href="/view?id={{.|urlquery}}">{{.|html}}</a></li>
{{end}}
</ol>
</body>
</html>

upload.html

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Upload</title>
</head>
<body>
<form method="POST" action="/upload" enctype="multipart/form-data">
Choose an image to upload: <input name="image" type="file" />
<input type="submit" value="Upload" />
</form>
</body>
</html>

Go 语言集训(四):并发

并发编程一般采用共享内存系统和消息传递系统。Go语言采用消息传递系统。即“不要通过共享内存来通信,而应该通过通信来共享内存

Channel

Channel 是Go语言提供的goroutine间的通信方式。可以使用channel在两个或者多个goroutine之间传递消息。channel是进程内的通信方式,因此通过channel传递对象的过程和调用函数时的参数传递行为比较一致,比如也可以传递指针。如果需要跨进程通信,我们建议用分布式系统的方法来解决,比如使用Socket或者HTTP等通信协议。

Channel是类型相关的。也就是说,一个channel智能传递一种类型的值,这个烈性需要在声明channel时制定。可以将其认为是一种类型安全的管道。

select

通过调用select()函数来监控一系列的文件句柄,一旦其中一个文件句柄发生了IO动作,该select()调用就会被返回。GO语言在语言级别支持select关键字,用于处理异步IO问题。
select与switch非常类似,由select开始一个新的选择快,每个选择条件由case语句来描述。与switch语句可以选择任何可使用相等比较的条件相比,select有比较多的限制,其中最大的一条限制是每个case语句里必须是一个IO操作。

select {
    case <- chan1:
    //如果chanl成功读到数据,则进行该case语句
    case chan2 <- 1:
    //如果成功向chan2写入数据,则进行case处理
    default:
    //如果上面都没有成功,则进入default处理。
}

缓冲机制

之前都是传递单个数据场景,如果持续传输大量数据的场景,就有些问题。需要给channel带上缓冲,从而达到消息队列的效果。
创建一个带缓冲的channel:
c := make(chan int, 1024)
调用make()时,缓冲区大小作为第二个参数传入即可。即使没有读取方,写入方也可以一直往channel里写入,在缓冲区被填写完之前,都不会被阻塞。

超时机制

如果发现channle已满,或者从channel读取数据时发现channel为空。如果不正确处理这些情况,很容易导致goroutine锁死。 Go语言没有提供直接的超时处理机制,但我们可以利用select机制。虽然select不是专为超时而设计的,却能很方便解决超时问题。因为select特点是只要其中一个case已经完成,程序会继续往下执行,而不会考虑其他case情况。

timeout := make(chan bool, 1)
go func() {
    time.Sleep(1e9)
    timeout <- true
}()
select {
    case <- ch:
    //从ch中读取到了数据
    case <- timeout:
    //一直没有从ch读取到数据,但从timeout读取到了数据
}

channel的传递

管道是一种非常广泛的一种设计模式,比如在处理数据时,我们采用管道设计,这样可以比较容易以插件的方式增加数据的处理流。

type PipeData struct {
    value int
    handler func(int) int
    next chan int
}

func handle(queue chan *PipeData) {
    for data := range queue {
        data.next <- data.handler(data.value)
    }
}

单向channel

单向channel 智能用于发送或者接受数据。channel本身必然是同时支持读写的,否则根本无法使用。加入一个channle真的只能读,那么只能为空,所以这是没有意义的。单向channle,其实只是对channel的一种使用限制。

我们在讲一个chanel变量传递到一个函数时,可以通过其指定为单向channel变量,从而限制该函数对此channel的操作。比如只能往这个channle写,或者读。

单向channel声明很简单。

var ch1 chan int //ch1是一个正常的chan,不是单向的
var ch2 chan<- float64 //ch2是一个单向channle,只能用于写float64数据
var ch3 <- chan int //ch3是一个只能读的channle,只能读int数据。

//初始化
ch4 := make(chan int)
ch5 := <-chan int(ch4) //ch5是一个单向读取的
ch6 := cha <- int(ch4) //ch6是一个单向写的

close(ch) //关闭channel

多核并行化

在执行一些昂贵的计算任务时,我们希望能够尽量利用现代服务器普遍具备的多核特性来尽量将任务并行化,从而降低总计算时间的目的。此时我们需要了解CPU核心的数量,并针对性地分解计算任务到多个goroutine中去并行运行。


type Vector []float64 func (v Vector) DoSome(i, n int, u Vector, c chan int) { for ; i < n; i++ { v[i] += u.Op(v[i]) } c <- 1 //任务执行完毕 } const NCPU = 16 func (v Vector) DoAll(u Vector) { c := make(chan int, NCPU) //用于接收每个CPU的任务完成信号 for i := 0; i < NCPU; i++ { go v.DoSome(i*len(v)/NCPU, (i+1)*len(v)/NCPU, u, c) } for i := 0; i < NCPU; i++ { <-c //获取每一个数据,表示一个CPU计算完成 } } //并没有发现有明显的加快,可以采用设置GOMAXPROCS的参数,设置并发。

同步

GO语言中的sync采用两种类型的锁。sync.Mutex 和 sync.RWMutex. Mutex是最简单的的锁,goroutine获得后,其他只能等待。RWMutex相对友好,是经典的单写多读模型。从实现上来看,RWMutex类型组合了Mutex。

type RWMutex struct {
    w Mutex
    writeSem unit32
    readSem unit32
    readCount int32
    readerWait int32
}

全局唯一性操作

对于从全局角度值需要运行一次代码,比如全局初始化操作,go语言提供了一个Once类型保证全局的唯一性操作。具体代码如下

var a string
var once sync.Once

func setup() {
    a = "hello world"
}

func doprint() {
    once.Do(setup)
    print(a)
}
func twopoint() {
    go doprint()
    go doprint()
}
// 没有引入once,则setup将会被每个goroutine先调用一次,至少对于这个例子是多余的。 once的Do方法可以保证全局范围内之只调用制定的函数次,而且所有其他goroutine在调用此举是,将会被阻塞。直到全剧唯一的once.Do()调用结束后才继续

案例

src
-cg:center.go centerclient.go player.go
-ipc:client.go server.og ipc_test.og
-cgss.go

cgss.go

package main

import (
    "bufio"
    "cg"
    "fmt"
    "ipc"
    "os"
    "strconv"
    "strings"
)

var centerClient *cg.CenterClient

func startCenterServer() error {
    server := ipc.NewIpcServer(&cg.CenterServer{})
    client := ipc.NewIpcClient(server)
    centerClient = &cg.CenterClient{client}
    return nil
}

func Help(args []string) int {
    fmt.Println(`
    Command:
    login <user><leve><exp>
    logout <user>
    send<mess>
    listplayer
    quit(q)
    help(h)
    `)
    return 0
}

func Quit(args []string) int {
    return 1
}

func Logout(args []string) int {
    if len(args) != 2 {
        fmt.Println("Usage : logout <username>")
        return 0
    }
    centerClient.RemovePlayer(args[1])
    return 0
}

func Login(args []string) int {
    if len(args) != 4 {
        fmt.Println("Usage:login...")
        return 0
    }
    level, err := strconv.Atoi(args[2])

    if err != nil {
        return 0
    }
    exp, err := strconv.Atoi(args[3])
    if err != nil {
        return 0
    }
    player := cg.NewPlayer()
    player.Name = args[1]
    player.Level = level
    player.Exp = exp
    err = centerClient.AddPlayer(player)
    return 0
}

func ListPlayer(args []string) int {
    ps, err := centerClient.ListPlayer("")
    if err != nil {
        fmt.Println("Failed", err)
    } else {
        for i, v := range ps {
            fmt.Println(i+1, ":", v)
        }
    }
    return 0
}

func Send(args []string) int {
    message := strings.Join(args[1:], " ")
    err := centerClient.Broadcast(message)
    if err != nil {
        fmt.Println("Failed", err)
    }
    return 0
}

func GetCommandHandlers() map[string]func(args []string) int {
    return map[string]func([]string) int{
        "help":       Help,
        "h":          Help,
        "login":      Login,
        "logout":     Logout,
        "listplayer": ListPlayer,
        "send":       Send,
        "quit":       Quit,
    }
}

func main() {
    fmt.Println("Game server soltuion")
    startCenterServer()
    Help(nil)
    r := bufio.NewReader(os.Stdin)

    handlers := GetCommandHandlers()

    for {

        fmt.Println("Command >")
        b, _, _ := r.ReadLine()
        line := string(b)
        tokens := strings.Split(line, " ")

        if handlers, ok := handlers[tokens[0]]; ok {
            ret := handlers(tokens)
            if ret != 0 {
                break
            }
        } else {
            fmt.Println("Unknow command", tokens[0])
        }
    }
}

center.go

package cg

import (
    "encoding/json"
    "errors"
    "fmt"
    "ipc"
    "sync"
)

var _ ipc.Server = &CenterServer{}

type Message struct {
    From    string "from"
    To      string "to"
    Content string "content"
}

type CenterServer struct {
    servers map[string]ipc.Server
    players []*Player
    //  rooms   []*Room
    mutex sync.RWMutex
}

func NewCenterServer() *CenterServer {
    servers := make(map[string]ipc.Server)
    players := make([]*Player, 0)
    return &CenterServer{servers: servers, players: players}
}

func (server *CenterServer) addPlayer(params string) error {
    player := NewPlayer()

    err := json.Unmarshal([]byte(params), &player)
    if err != nil {
        return err
    }
    server.mutex.Lock()
    defer server.mutex.Unlock()

    server.players = append(server.players, player)
    return nil
}

func (server *CenterServer) removePlayer(params string) error {
    server.mutex.Lock()
    defer server.mutex.Unlock()

    for i, v := range server.players {
        if v.Name == params {
            fmt.Println("dddd", len(server.players))
            if len(server.players) == 1 {
                server.players = make([]*Player, 0)
                fmt.Println("1111")
            } else if i == len(server.players)-1 {
                server.players = server.players[:i-1]
            } else if i == 0 {
                server.players = server.players[1:]
            } else {
                server.players = append(server.players[:i-1], server.players[:i+1]...)
            }
            return nil
        }
    }
    return errors.New("Plyaer not found")
}

func (server *CenterServer) listPlayer(params string) (players string, err error) {
    server.mutex.Lock()
    defer server.mutex.Unlock()

    if len(server.players) > 0 {
        b, _ := json.Marshal(server.players)
        players = string(b)
    } else {
        err = errors.New("no player online")
    }
    return
}

func (server *CenterServer) broadcast(prams string) error {
    var message Message
    err := json.Unmarshal([]byte(prams), &message)
    if err != nil {
        return err
    }

    server.mutex.Lock()
    defer server.mutex.Unlock()

    if len(server.players) > 0 {
        for _, player := range server.players {
            player.mq <- &message
        }
    } else {
        err = errors.New("No player online")
    }
    return err
}

func (server *CenterServer) Handle(method, params string) *ipc.Response {
    switch method {
    case "addplayer":
        err := server.addPlayer(params)
        if err != nil {
            return &ipc.Response{Code: err.Error()}
        }
    case "removeplayer":
        err := server.removePlayer(params)
        if err != nil {
            return &ipc.Response{Code: err.Error()}
        }
    case "listplayer":
        players, err := server.listPlayer(params)
        if err != nil {
            return &ipc.Response{Code: err.Error()}
        }
        return &ipc.Response{"200", players}
    case "broadcast":
        err := server.broadcast(params)
        if err != nil {
            return &ipc.Response{Code: err.Error()}
        }
        return &ipc.Response{Code: "200"}
    default:
        return &ipc.Response{Code: "404", Body: method + ":" + params}
    }
    return &ipc.Response{Code: "200"}
}

// Name ..
func (server *CenterServer) Name() string {
    return "CenterServer"
}

player.go

package cg

import "fmt"

type Player struct {
    Name  string "name"
    Level int    "level"
    Exp   int    "exp"
    //  Room  int    "rom"
    mq chan *Message
}

func NewPlayer() *Player {
    m := make(chan *Message, 1024)
    player := &Player{"", 0, 0, m}
    go func(p *Player) {
        for {
            msg := <-p.mq
            fmt.Println(p.Name, "received message:", msg.Content)
        }
    }(player)
    return player
}

centerclient.go

package cg

import (
    "encoding/json"
    "errors"
    "ipc"
)

type CenterClient struct {
    *ipc.IpcClient
}

func (client *CenterClient) AddPlayer(player *Player) error {
    b, err := json.Marshal(*player)
    if err != nil {
        return err
    }

    resp, err := client.Call("addplayer", string(b))
    if err == nil && resp.Code == "200" {
        return nil
    }
    return err
}

func (client *CenterClient) RemovePlayer(name string) error {
    ret, _ := client.Call("removeplayer", name)
    if ret.Code == "200" {
        return nil
    }
    return errors.New(ret.Code)
}

func (client *CenterClient) ListPlayer(params string) (ps []*Player, err error) {
    resp, _ := client.Call("listplayer", params)
    if resp.Code != "200" {
        err = errors.New(resp.Code)
        return
    }
    err = json.Unmarshal([]byte(resp.Body), &ps)
    return
}

func (client *CenterClient) Broadcast(message string) error {
    m := &Message{Content: message} //构造message结构体
    b, err := json.Marshal(m)
    if err != nil {
        return nil
    }

    resp, _ := client.Call("broadcast", string(b))
    if resp.Code == "200" {
        return nil
    }
    return errors.New(resp.Code)
}

ipc:client.go

package ipc

import "encoding/json"

type IpcClient struct {
    conn chan string
}

func NewIpcClient(server *IpcServer) *IpcClient {
    c := server.Connect()
    return &IpcClient{c}
}

func (client *IpcClient) Call(method, params string) (resp *Response, err error) {
    req := &Request{method, params}

    var b []byte
    b, err = json.Marshal(req)
    if err != nil {
        return
    }

    client.conn <- string(b)
    str := <-client.conn

    var resp1 Response
    err = json.Unmarshal([]byte(str), &resp1)
    resp = &resp1

    return
}

func (client *IpcClient) Close() {
    client.conn <- "CLOSE"
}

server.og

package ipc

import (
    "encoding/json"
    "fmt"
)

type Request struct {
    Method string "method"
    Params string "params"
}

type Response struct {
    Code string "code"
    Body string "body"
}

type Server interface {
    Name() string
    Handle(method, params string) *Response
}

type IpcServer struct {
    Server
}

func NewIpcServer(server Server) *IpcServer {
    return &IpcServer{server}
}

func (server *IpcServer) Connect() chan string {
    session := make(chan string, 0)
    go func(c chan string) {
        for {
            request := <-c
            if request == "CLOSE" {
                break
            }
            var req Request
            err := json.Unmarshal([]byte(request), &req)
            if err != nil {
                fmt.Println("Invalid request format:", request)
            }

            resp := server.Handle(req.Method, req.Params)
            b, err := json.Marshal(resp)
            c <- string(b)
        }
        fmt.Println("Session closed")
    }(session)
    fmt.Println("A new session has been created successfully")
    return session
}


Go 语言集训(三):面向对象

在Go语言中,面向对象和面向过程是两种不同的方式。

func (a Integer) Less (b Integer) bool {  //面向对象
    return a < b
}

func Integer_Less(a Integer, b Integer) bool {//面向过程
    return a < b
}

GO语言中有4个类型比较特别,看起来像引用类型。

  1. 数组切片:指向数组的一个去见
  2. map:及其常见的数据结构,提供键值查询能力。Map本质是一个字典指针
  3. channel:执行体(goroutine)间的通信设施
  4. 接口(interface):对一组满足某个契约的类型的抽象
    Channel和Map类似,本质上是一个指针。将他们设计为引用类型而不是统一的值类型的原因是,完整复制一个channel或者map并不是常规需求。

匿名组合

type Job struct {
    Command String 
    *log.Logger
}

func(job *Job) Start() {
    job.Log("Starting now...")
    job.Log("started")
}

在合适赋值后,我们在JOB类型的所有成员方法中可以很舒适地借用所有log.Logger提供的方法。对于Job实现者来讲,他甚至根本不用意识到log.Logger类型的存在,这就是匿名组合的美丽。
需要注意的是,不管是非匿名的类型组合还是匿名组合,被组合的类型所包含的方法虽然都升级成外部这个组合类型的方法,但是其实他们被组合方法调用时接收者并没有改变。比如Job例子,即使这个组合后调用的方式变成了job.Log(…),但Log函数的接收者任然是log.Logger指针,因此在Log中不可能访问到Job的其他成员方法和变量。

type X struct {
    Name string
}

type Y struct {
    X
    Name string
}

接口组合中名字冲突时,所有Y类型的Name成员都只会访问到最外层的那个Name变量。X.Name相当于隐藏了起来。

type Logger struct{
    Level int
}
type Y struct{
    *Logger
    Name string
    *log.Logger
}//这里会有问题,可能收到编译错误,但如果定义后没有再用过,也不会产生错误。因为Y类型存在两个名为Logger的成员。虽然类型不同。

可见性

要使某个符号对其他包可见,需要将该符号定义为以答谢字母开头。

type Rect struct {
    X,Y float64
    Width,Height float64
}//这样Rect类型的成员全部被导出了

func (r *Rect) area() float64 {//包内可见
    return r.Width * r.Height 
}

Go语言中符号的可访问新是包一级的而不是类型一级的。在上面例子中,area是Rect内部方法,但同一个包中的其他类型也可以访问到它。这样做很粗犷,但很实用。如果GO语言的可访问性是类型一级的,少不了加上friend这样的关键字,标识两个类是朋友关系,可以访问彼此的私有成员。

接口赋值

  1. 将对象实例赋值给接口
  2. 将一个接口赋值给另外一个接口
    A(小) = B(大)

接口查询

var file1 Wirter = ..
if file5,ok := file.(two.IStream); ok {
    ...
}

if file6,ok := file.(*File); ok {
    ...
}

完整案例

Go 语言集训(二)

闭包

Go的匿名函数是一个闭包
* 基本概念
闭包是可以包含自由(为绑定到特定对象)变量的代码块,这些变量不在这个代码块内或者任何全局上下文中定义,而是在定义代码块的环境中定义。要执行的代码块(由于自由变量包含在代码块中,所以这些自由变量以及他们引用的对象没有被释放)为自由变量提供绑定的计算环境(作用域)
* 闭包的价值
闭包的机制在于可以作为函数对象或者匿名函数,对于类型系统而言,这意味着不仅要标识数据还要表示代码。支持闭包的多数语言都将函数作为第一级对象,这就是说这些函数可以存储到变量中作为参数传递给其他函数,最重要的是能够被函数动态创建和返回
* Go语言中的闭包
GO语言中的闭包同样也会引用到函数外的变量。闭包的实现确保只要闭包还被使用,那么被闭包引用的变量会一直存在。

package main

import "fmt"

func main() {
    var j int = 5
    a := func() func() {
        var i int = 10
        return func() {
            fmt.Printf("i,j: %d,%d\n", i, j)
        }
    }()
    a()
    j *= 2
    a()
    fmt.Println("闭包另外尝试")
    i := incr()
    fmt.Println(i())      //1
    fmt.Println(i())      //2
    fmt.Println(i())      //3
    fmt.Println(incr()()) //1 incr()返回一个func() int ;所以还需要incr()()l对 func()int进行执行
    fmt.Println(incr()()) //1

}

func incr() func() int { // func() int 作为 func的返回值
    var x int
    return func() int {  //这是返回的指针函数
        x++
        return x
    }
}

对于Map的 函数的使用

package main

import "fmt"

func f(p string) {
    fmt.Println("function f parameter:", p)
}

func g(p string, q int) {
    fmt.Println("function g parameters:", p, q)
}

func main() {
    m := map[string]interface{}{//使用interface更加安全,可以匹配不同的接口。
        "f": f,
        "g": g,
    }
    for k, v := range m {
        switch k {
        case "f":
            v.(func(string))("astring")
        case "g":
            v.(func(string, int))("astring", 42)
        }
    }
}

排序的案例

.
├── algorithms
│   ├── bubblesort
│   │   ├── bubblesort.go
│   │   └── buddlesort_test.go
│   └── qsort
│   ├── qsort.go
│   └── qsort_test.go
├── input.data
├── output.data
└── sorter.go

sourter.go

// sorter.go
package main

import "flag"
import "fmt"
import "io"
import "bufio"
import "os"
import "strconv"
import "time"

import "algorithms/bubblesort"
import "algorithms/qsort"

var infile *string = flag.String("i", "infile", "File contains values for sorting")
var outfile *string = flag.String("o", "outfile", "File to receive sorted values")
var algorithm *string = flag.String("a", "qsort", "Sort algorithm")

func main() {
    flag.Parse()

    if infile != nil {
        fmt.Println("inflie =", *infile, "outfile =", *outfile, "algortihm =", *algorithm)
    }
    values, err := readValues(*infile)
    if err == nil {
        t1 := time.Now()
        switch *algorithm {
        case "qsort":
            qsort.QuickSort(values)
        case "bubblesort":
            bubblesort.BubbleSort(values)
        default:
            fmt.Print("sorting algortim", *algorithm, "is either unkonw or unsported")
        }
        t2 := time.Now()
        fmt.Println("The sorting process costs", t2.Sub(t1), "to complete")
        writeValues(values, *outfile)
    } else {
        fmt.Println(err)
    }
}

func writeValues(value []int, outfile string) error {
    file, err := os.Create(outfile)
    if err != nil {
        fmt.Println("Failed to create the output file ", outfile)
        return err
    }

    defer file.Close()
    for _, value := range value {
        str := strconv.Itoa(value)
        file.WriteString(str + "\n")
    }
    return nil
}

func readValues(infile string) (values []int, err error) {
    file, err := os.Open(infile)
    if err != nil {
        fmt.Println("failed to open the input file ", infile)
        return
    }
    defer file.Close()

    br := bufio.NewReader(file)
    values = make([]int, 0)

    for {
        line, isPrefix, err1 := br.ReadLine()

        if err1 != nil {
            if err1 != io.EOF {
                err = err1
            }
            break
        }
        if isPrefix {
            fmt.Println("A too long line, seems unexpted")
            return
        }

        str := string(line) //转换字符数组为字符串

        value, err1 := strconv.Atoi(str)
        fmt.Println(err1)
        if err1 != nil {
            err = err1
            return
        }
        values = append(values, value)
    }
    return
}

sqort.go

package qsort

func quicSort(values []int, left, right int) {
    temp := values[left]
    p := left
    i, j := left, right
    for i <= j {
        for j >= p && values[j] >= temp {
            j--
        }
        if j >= p {
            values[p] = values[j]
            p = j
        }
        if values[i] <= temp && i <= p {
            i++
        }
        if i <= p {
            values[p] = values[i]
            p = i
        }
    }
    values[p] = temp
    if p-left > 1 {
        quicSort(values, left, p-1)
    }
    if right-p > 1 {
        quicSort(values, p+1, right)
    }
}

func QuickSort(values []int) {
    quicSort(values, 0, len(values)-1)
}

bubblesort.go

package bubblesort

func BubbleSort(values []int) {
    flag := true
    for i := 0; i < len(values)-1; i++ {
        flag = true
        for j := 0; j < len(values)-i-1; j++ { //存在交换就把flag变为false,没有的话,则说明排序完成
            if values[j] > values[j+1] {
                values[j], values[j+1] = values[j+1], values[j]
                flag = false
            }
        }
        if flag == true {
            break
        }
    }
}

bubblesort_test.go

package bubblesort

import "testing"

func TestBuddleSort1(t *testing.T) {
    values := []int{5, 4, 3, 2, 1}
    BubbleSort(values)
    if values[0] != 1 || values[2] != 3 || values[3] != 4 || values[4] != 5 {
        t.Error("BubbleSort() failed. Got", values, "exptected 1 2 3 4 5")
    }
}

func TestBuddleSort2(t *testing.T) {
    values := []int{5, 5, 3, 2, 1}
    BubbleSort(values)
    if values[0] != 1 || values[2] != 3 || values[3] != 5 || values[4] != 5 {
        t.Error("BubbleSort() failed. Got", values, "exptected 1 2 3 5 5")
    }
}

func TestBuddleSort3(t *testing.T) {
    values := []int{5}
    BubbleSort(values)
    if values[0] != 5 {
        t.Error("BubbleSort() failed. Got", values, "exptected 5")
    }
}

qsort_testing.go

package qsort

import "testing"

func TestQsort1(t *testing.T) {
    values := []int{5, 4, 3, 2, 1}
    QuickSort(values)
    if values[0] != 1 || values[2] != 3 || values[3] != 4 || values[4] != 5 {
        t.Error("QuickSort() failed. Got", values, "exptected 1 2 3 4 5")
    }
}

父母草原行

父母来京,小侄女全程护送。之前小侄女想到上海来看我,但在上海出差任务安排紧张,没有自己的时间。此行也是为小侄女考上一中的奖励。由于父母年高体弱,没有办法爬山等,草原之行是不错的选择。

行程计划

  • 8月2日: 北京–张家口(宿)
  • 8月3日:张家口–坝上(宿)
  • 8月4日:坝上-承德(宿)
  • 8月5日:承德-北京(宿)

备忘录

  1. 查看8月2-6号的天气
  2. 7月29日租车(迈锐宝)
  3. 购买返程火车
  4. 草原上的住宿提前预定

租车时间

  • 8月2日 13:00 取车
  • 8月6日 13:00 还车

活动安排

  1. 小妮子骑马
  2. 烟花
  3. 承德八大庙or避暑山庄
  4. 草原溜达

物资储备

由于老人和小孩都在,不能像往常一样,开车就走。需要储备一些物资,以防路上有什么情况发生。

  1. 保温箱:已下单
  2. 暖宝宝
  3. 保温杯
  4. 气罐和炉子:茶叶
  5. 防蚊虫
  6. 防晒霜
  7. 卫生纸
  8. 熟食
  9. 零食

小侄女的知识储备

  1. 坝上草原
  2. 木兰围场
  3. 承德
  4. 承德八大庙
  5. 冬奥会
  6. 张家口
  7. 丰宁

阿里行程计划(待更新)

行程目的

不到阿里,不算到西藏。阿里想了5年,等了5年。

  • 转山!神圣的冈仁波齐,世界宗教的中心。宗教之美
  • 古格王朝。历史之美。
  • 羌塘-棕熊、野牦牛、狐狸、狼等等。野性之美。
  • 玛旁雍错、当惹雍错、色林错、纳木错、拉䀚措 。 自然之美
    能否走到文布南村,看具体时间和路线。

总体时间规划

总计16天。

9月27日 Z19 20:44-08:31 北京–西安
9月28日 21:40-0:55 西安–拉萨
9月29日 休整,租车
9月30日 拉萨-卡嘎/萨嘎
10月1日 阿里
10月2日 阿里
10月3日 阿里
10月4日 阿里
10月5日 阿里
10月6日 阿里
10月7日 阿里
10月8日 阿里
10月9日 阿里
10月10日 阿里
10月11日 阿里
10月12日 启程回拉萨
10月13日 16:10-22:15 拉萨-北京

大致活动

  1. 冈仁波齐转山:2天,53KM
  2. 玛旁雍错
  3. 古格王朝

Go 语言集训(一)

编程基本概念

  1. 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
  2. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
  3. 协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
  4. 线程进程都是同步机制,而协程则是异步

编程基础

package Traininig


func varFunc() {
    var v1 int
    var v2 string
    var v3 [10]int //数组
    var v4 []int   // 数组切片
    var v5 struct {
        f int
    }
    var v6 *int           //指针
    var v7 map[string]int //map,key为string,value是int类型
    var v8 func(a int) int
    var (
        v1 int
        v2 string
    )

    var v1 int = 10 //正确方式1
    var v2 = 10     //正确方式2,编译器自动推导出v2的类型
    v3 := 10        //正确方式3,编译器自动推导出v3的类型

    var i int
    i := 2 //出错!

    var v10 int
    v10 = 100

    i, j = j, i

    _, _, nikcname := GetName()

    // -12
    // 3.1415926 浮点类的常量
    // 3.2+12i 复数类型的常量
    // true 布尔类型的常量
    // "foo" 字符串常量
    // -12l -12的long类型
    const Pi float64 = 3.141592658
    const zero = 0.0 //无类型浮点常量
    const (
        size int64 = 1024
        eof        = -1 //无类型整形常量
    )
    const u, v float32 = 0, 3   //u=0.0. v=3.0 常量的多赋值
    const a, b, c = 3, 4, "foo" //无类型整型和字符串常量
    const mask = 1 << 3
    const Home = os.GetEnv("HOME")

    const ( //iota被强制重置为0
        c0 = iota // c0 = 0
        c1 = iota // c1 = 1
        c2 = iota // c2 = 2
    )

    const (
        a = 1 << iota //iota被重置为0,这时候a = 1 << 0 = 1
        b = 1 << iota //b = 1 << 1 = 2
        c = 1 << iota // c = 1 << 3 = 4
    )

    const (
        u         = iota * 24 // u = 0
        v float64 = iota * 42 // v = 42.0
        w         = iota * 42 // w = 84
    )

    const x = itoa // iota 被强制重置为0
    const y = itoa // iota 被强制重置为0

    const (
        c0 = itoa // c0 = 0
        c1        // c1 = 1
        c2        // c2 = 2
    )

    const (
        a = 1 << iota // a=1
        b             // b=2
        c             // c=4
    )

    const (
        Sunday = iota
        Monday
        Tuesday
        Wendesday
        Thursday
        Friday
        Saturday
        numberofDays //这个是私有变量,包外面看不到,因为是小写的
    )

    var v1 bool
    v1 = true
    v2 := false
    v3 := (1 == 2) //v2也会被推导为bool类型

    var b bool
    b = 1       //编译错误
    b = bool(1) //编译错误 bool不支持自动或者强制的类型转换

    var value2 int32
    value1 := 32           //value1被默认推动到int类型
    value2 = value1        //编译错误,因为int和int32 在go中属于不同的类型
    value2 = int32(value1) //编译通过,强制类型转化

    mod := (5 % 3) // mod = 2

    i, j = 1, 2
    if i == j {
        fmt.Println("i and j are equal")
    }

    var i int32
    var j int64
    i, j = 1, 2
    if i == j { //编译出错,两个不同类型的整型数不能直接比较
        fmt.Println("i and j are equal")
    }
    if i == 1 || j == 2 { //编译通过,但能与字面变量(literal)进行比较
        fmt.Println("i and j are equal")
    }

    var fvalue1 float32
    fvalue1 = 12
    fvalue2 := 12.0 //必须添加.0 不然会被推导为整形,并且默认为float64,C语言中的double
    fvalue2 = float64(fvalue1)

    var value1 complex64 //由2个float32构成的复数
    value1 = 3.2 + 12i
    value2 := 3.2 + 12i
    value3 := complex(3.2, 12)

    var str string
    str = "hello world"
    ch := str[0]
    fmt.Printf("The length of \"%s\" is %d", str, len(str))
    fmt.Printf("The fist character of \"%s\" is %c.\n", str, ch)
    str[0] = "H" //编译错误

    str := "Hello,世界"
    n := len(str)
    for i := 0; i < n; i++ {
        ch := str[i]      //以字符数组的方式遍历
        fmt.Printf(i, ch) //以字节的形式输出,一共13位。UTF-8占据3个字节。byte.使用较多
    }

    for i, ch := range str { //以Unicode方式遍历
        fmt.Printf(i, ch)//以字节的形式输出,一共11位,因为每个字符类型是rune
    }

    [32]byte //define 32 byte array
    [2*N]struct {x,y int32}// 复杂类型数组
    [1000]*float64 //指针数组
    [3][5]int //二维数组
    [2][2][2]float64//等同于[2]([2]([2]float64))

    //Go语言中,数组长度在定义后就不可更改,在声明长度可以为一个常量后者一个常量表达式。常量表达式是指编译期即可计算结果的表达式
    var arr [10]int;
    arrLength := len(arr)
    }

//因为浮点数是不精确的表达,所以无法直接用==来判断两个浮点数是否相等
//采用如下方案
//p 为用户自定义的比较精度,比如0.000001
func IsEqual(f1, f2, p float64) bool {
    b = math.a
    return math.Fdim(f1, f2) < p
}

func GetName() (firstname, lastname, nickname string) {
    return "May", "Chan", "Chibi"
}

数组和数组切片

package main

import "fmt"

func modify(array [5]int) {
    array[0] = 10 //试图修改第一个元素
    fmt.Println("In modify(), array values:", array)
}
func arr() {
    array := [5]int{1, 2, 3, 4, 5}
    modify(array)
    fmt.Println("In main(),array values:", array)

    var myArray [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    var mySlice []int = myArray[5:]
    fmt.Println("Elements of Myarray:")
    for _, v := range myArray {
        fmt.Print(v, " ")
    }
    fmt.Println("\n Elements of mySlice:")
    for _, v := range mySlice {
        fmt.Print(v, " ")
    }
    fmt.Println()

    // 可动态增减元素是数组切片比数组更为强大的功能。与数组相比,数组切片多了一个存储能力。即元素个数和分配的空间可以是两个不同的值
    // 合理设置存储能力值,可以大幅度降低数组切片内部重新分配内存和搬送内存块的频率,从而大大提高程序性能

    mySlice1 := make([]int, 5, 6)
    fmt.Println("len(mySlice1)", len(mySlice1))
    fmt.Println("cap(mySlice1)", cap(mySlice1))
    mySlice1 = append(mySlice1, 1, 2)
    mySlice2 := []int{8, 9, 10, 11, 12, 14}
    mySlice2 = append(mySlice1, mySlice2[2:]...) //这里需要添加三个...,映日mySlice1都是int,而mySlice2是[]int类型的,所以需要将元素打散
    fmt.Println(mySlice2)                        //上面会自动扩充mySlice1的大小

    //基于数组切片创建数组切片
    oldSlice := []int{1, 2, 3, 4, 5}
    newSlice := oldSlice[:3] //基于oldslice的前三个元素
    fmt.Println(newSlice)

    slicea := []int{1, 2, 3, 4, 5}
    sliceb := []int{5, 4, 3}
    copy(sliceb, slicea) //智慧复制slicea的前三个元素到sliceb中
    copy(slicea, sliceb) //智慧复制sliceb的3个元素到slicea中
    fmt.Println(slicea)
}

map使用

package main

import "fmt"

type PersonInfo struct {
    ID      string
    Name    string
    Address string
}

func mapf() {
    var personDB map[string]PersonInfo     //变量申明
    personDB = make(map[string]PersonInfo) //变量创建 也可以指定大小 make(map[string] PersonInfo, 100)
    myMap := map[string]PersonInfo{
        "1234": PersonInfo{"1234", "xuan", "Room100"},
    }
    personDB["12345"] = PersonInfo{"12345", "Tome", "Room 123"}
    personDB["1"] = PersonInfo{"1", "Jack", "Room101"}
    //从map中查找键为1234的信息
    person, ok := personDB["1"]
    if ok {
        fmt.Println("Found person", person.Name, "with ID", person.ID)
    } else {
        fmt.Println("Did not find person with ID 1234.")
    }
    delete(myMap, "1234")
}

判断和控制

package main

import "fmt"

func example(x int) int {
    if x == 0 {
        fmt.Println("x==0")
        return x
    } else {
        return 1
    }
}
func main() {
    a := 1
    if a < 5 {
        fmt.Println("a<5")
    } else {
        fmt.Println("a>5")
    }
    b := example(2)
    fmt.Println(b)
    i := 2
    switch i {
    case 0:
        fmt.Printf("0")
    case 1:
        fmt.Printf("1")
    case 2:
        fallthrough //会继续执行下一个case
    case 3, 4, 5, 6:
        fmt.Printf("3,4,5,6")
    default:
        fmt.Printf("Default")
    }
    fmt.Println("\r\nno parameter in switch")
    i = 0
    switch {
    case 0 < i:
        fmt.Println(i)
    case 0 > i:
        fmt.Println("0 > i")
    }

    fmt.Println("for loop")
    sum := 0
    for {
        sum++
        if sum > 100 {
            break
        }
    }
    fmt.Println(sum)

    z := []int{1, 2, 3, 4, 5, 6, 7}
    for i, j := 0, len(z)-1; i < j; i, j = i+1, j-1 {
        z[i], z[j] = z[j], z[i]
    }
    fmt.Println(z)
    for j := 0; j < 5; j++ {
        for i := 0; i < 10; i++ {
            if i > 5 {
                break
            }
            fmt.Println(j, i)
        }
    }
}

云计算基础网络简要

ToR 交换机(Top of Rack)

每个服务器都有一个到ToR交换机的专有连接,而且ToR可以将数据转发到机架中的其他服务器,或者通过高带宽的上行端口发送到机架外部。一个典型的机架可以承载的服务器数量可达40以上,因此许多ToR交换机都有48个10Gb端口和4个连接到汇聚交换机的40Gb上行链路端口。虽然服务器带宽(480Gbit/s)和上行带宽(160Gbit/s)出现了3:1的失配,但是数据中心通信在本质上是极具突发性的,所有服务器同事完全使用10Gibts带宽是非常罕见的。
对于10Gb的端口来说,服务器和ToR交换机之间的短距离通信,可采用低成本的直连铜缆。ToR上行采用光模块,因为需要驱动更长的距离和更高的带宽才能达到其他链接多个服务器机架的交换机。
上行链路可以连接到汇聚交换机,而汇聚交换机可以将来自多个ToR交换机的通信汇聚到核心交换机。
每个ToR交换机都有一个控制平面处理器,用来配置交换机的转发表,监视交换机的健康状况等等。
ToR还可以充当服务器和网络其余部分之间的网关,提供诸如隧道、过滤、监视和负载功能。启用这些功能,需要ToR检查数据包的报头并且使用ACL规则来匹配各种报头字段。

EoR交换机(End of Row)

EoR交换机设计的目标是通过大量的交换机组件之间共享电源、冷却设备和管理基础设备来降低成本。可以认为相当于将多个ToR交换机设计为能够插入单个模块化机箱中的交换机卡。

通过取消每个ToR交换机和汇聚交换机的独立电源、冷却封山和CPU子系统可以消减成本。中央管理处理器不仅降低成本,还提供单点管理模式,不需要对多个ToR交换机单独进行管理。
配置中,每个服务器都必须通过长电缆连接到EoR交换机。缺点就是电缆长度高。

https://www.cnblogs.com/Anker/p/8998904.html

PCIe接口

PCIe接口标准多年来一直用于将外围设备连接到PC的CPU芯片组。该接口标准也用于连接CPU芯片组合服务器内的网络接口卡。存在SR-IOV(single-root IO virtualizaiton单根IO虚拟化)和MR-IOV(Multi-Root IO Virtualization,多根IO虚拟化)标准。

SR-IOV

传统PCI网卡只有一个物理功能,而SR-IOV网卡除了一个物理功能还有多个虚拟功能。每个虚拟功能都具有网卡的全部特征和功能,而且还具有自己的PCI配置空间。使得每个使用SR-IOV兼容网卡驱动程序的虚拟机在功能上像是连接了一个专用网卡。SR-IOV物理功能负责为虚拟功能连接的虚拟端口分配物理端口带宽。要实现这一点,管理程序也必须支持SR-IOV。
SR-IOV看做一种针对虚拟交换机网络连接的硬件卸载,在网卡中实现虚拟交换机网络连接是为了降低CPU利用率并改善性能
虚拟交换机可以被视作共享内存交换机,只需要在共享同一交换机的虚拟机之间传递内存指针。使用SR-IOV是,同一个服务器内两个虚拟机之间的通信从网卡返回之前必须经过网卡PCIe接口。对于当前连接到10Gb网卡的服务器而言,PCIe带宽限制在14Gbit/s左右,而虚拟机交换机可以维持30Gbit/s的传输速率。尽管比起SR-IOV,虚拟交换机需要使用更多CPU资源,但是高性能多核交换机很容易处理这种负担。