Go基础上手|青训营笔记

145 阅读6分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记。

[青训营] day1-Go基础

1.1 Go语言的特性

  1. 高性能、高并发
  2. 语法简单、学习曲线平缓
  3. 丰富的标准库
  4. 完善的工具链
  5. 静态链接
  6. 快速编译
  7. 跨平台
  8. 垃圾回收

1.2 一些语法注意点

Go的语法形式其实和C/C++有很多相似之处。但是也存在着一些不同,接着让我们来看一看吧。

  1. if……else……语句

    • if后面没有括号,但是如果你写了括号的话,在保存的时候编译器会直接帮你去掉。
    • if后面必须加花括号 {} 不能省略
  2. for循环语句

    • Go中没有while、do while等循环,只有唯一的for循环。

    • for后面什么也不写代表一个死循环。

    • 循环中可以用break跳出或continue继续循环

      //三种for循环的表现形式
          for {
              fmt.Print("loop")
              break
          }
      //result:loopfor j := 7; j < 9; j++ {
              fmt.Print(j)
          }
      //result:78for i <= 3 {
              fmt.Print(i)
              i = i + 1
          }
      //result:123
      
  3. switch

    • case后面不需要加()
    • case结尾不用加break
    switch a {
        case 1:
            fmt.Println("one")
        case 2:
            fmt.Println("two")
        default:
            fmt.Println("other")
        }
    
  4. 切片

    • 切片可以任意更改为长度
    • 可以用make来创建一个切片,可以像数组一样去取值,用append来追加元素
    • 初始化时也可以指定长度
    • 可以像Python一样切片操作,但是不支持负数索引
    //切片初始化:make([]string,len)
        s := make([]string, 3)
        s[0] = "a"
        s[1] = "b"
        s[2] = "c"
        fmt.Println("get:", s[2])//
        fmt.Println("len:", len(s))
    ​
        s = append(s, "d")
        s = append(s, "e", "f")
        fmt.Println(s)
    ​
        c := make([]string, len(s))
        copy(c, s)
        fmt.Println(c)
    ​
        fmt.Println(s[2:5])
        fmt.Println(s[:5])
        fmt.Println(s[2:])
    ​
        good := []string{"g", "o", "o", "d"}
        fmt.Println(good)
    
  5. map

    • 可称哈希或者字典
    • 可以用make创建一个空map,一个是key类型,另一个value类型
    • 是完全无序,随机顺序输出
        m := make(map[string]int)
        m["one"] = 1
        m["two"] = 2
        fmt.Println(m)           //map[one:1 two:2]
        fmt.Println(len(m))      //2
        fmt.Println(m["one"])    //1
        fmt.Println(m["unknow"]) //0
    ​
        r, ok := m["unknow"]
        fmt.Println(r, ok) //0 falsedelete(m, "one")
    ​
        m2 := map[string]int{"one": 1, "two": 2}
        var m3 = map[string]int{"one": 1, "two": 2}
        fmt.Println(m2, m3) //map[one:1 two:2] map[one:1 two:2]
    
  6. range

    • 可以用来快速遍历

    • 遍历时对于数组返回有两个值,第一个是索引,第二个是对应位置的值。如果不需要索引的话,可以用下划线来忽略

      func main() {
          nums := []int{2, 3, 4}
          sum := 0
          for i, num := range nums {
              sum += num
              if num == 2 {
                  fmt.Println("index", i, "num:", num) //index 0 num: 2
              }
          }
          fmt.Println(sum) //9
      ​
          m := map[string]string{"a": "A", "b": "B"}
          for k, v := range m {
              fmt.Println(k, v) //a A; b B
          }
          for k := range m {
              fmt.Println("key", k) // key a; key b
          }
      }
      
  7. 函数

    func add(a int, b int) int {
        return a + b
    }
    func add2(a, b int) int {
        return a + b
    }
    func exists(m map[string]string, k string) (v string, ok bool) {
        v, ok = m[k]
        return v, ok
    }
    func main() {
        res := add(1, 2)
        fmt.Println(res) //3
    ​
        v, ok := exists(map[string]string{"a": "A"}, "a")
        fmt.Println(v, ok) //A true
    }
    
  8. 指针

    • 用于传入参数进行修改
    func add3(n int) {
        n += 2
    }
    func add3ptr(n *int) {
        *n += 2
    }
    func main() {
        n := 5
        add3(5)
        fmt.Println(n) //5
        add3ptr(&n)
        fmt.Println(n) //7
    }
    
  9. 结构体

    • 带类型的字段集合
    • 可以用结构体的名称去初始化一个结构体变量,构造时需要传入每个字段的初始值
    • 也可以用键值对的方式去指定初始值,对其中一部分字段进行初始化
    • 结构体支持指针,能够实现对结构体的修改,也可以在某些情况下避免一些大结构体的拷贝开销
    type user struct {
        name     string
        password string
    }
    func main() {
        a := user{name: "Dorri", password: "12345"}
        b := user{"Dorri", "12345"}
        c := user{name: "Dorri"}
        c.password = "12345"
        var d user
        d.name = "Dorri"
        d.password = "12345"
        fmt.Println(a, b, c, d)//{Dorri 12345} {Dorri 12345} {Dorri 12345} {Dorri 12345}
        fmt.Println(checkPassword(a, "haha"))    //false
        fmt.Println(checkPassword2(&a, "12345")) //true
    }
    func checkPassword(u user, password string) bool {
        return u.password == password
    }
    func checkPassword2(u *user, password string) bool {
        return u.password == password
    }
    
  10. 错误处理

    • 在函数里,我们可以在那个函数的返回值类型里面,后面加一个error,就代表这个函数可能会返回错误。
    • 在函数实现的时候,return需要同时return两个值,要么就是如果出现错误的话,那么可以return nil和一个error,那么返回原本的结果和nil。
    type user2 struct {
        name     string
        password string
    }
    func findUser(users []user2, name string) (v *user2, err error) {
        for _, u := range users {
            if u.name == name {
                return &u, nil
            }
        }
        return nil, errors.New("not found")
    }
    func main() {
        u, err := findUser([]user2{{"Dorri", "12345"}}, "Dorri")
        if err != nil {
            fmt.Println(err)
            return
        }
        fmt.Println(u.name) //Dorri
        if u, err := findUser([]user2{{"Dorri", "12345"}}, "li"); err != nil {
            fmt.Println(err) //not found
            return
        } else {
            fmt.Println(u.name)
        }
    }
    
  11. JSON

    • 对于一个已有的结构体,只需要保证每字段的第一个字母是大写,也就是公开字段
    • 用JSON.marshaler去序列化,编程一个JSON的字符串
    • 序列化之后的字符串能够用JSON.unmarshaler去反序列化到一个空的变量里面
    • 大写字母开头

1.3实战

2.在线字典

遇到的问题

1652166381332.png

控制台出现如下的问题。问题在于这个项目我们需要在,终端中运行。

*解决如下*

1652166547755.png

  1. SOCKS5 代理介绍

    • socks5协议是代理协议,协议是明文传输

    • 作用:某些企业为了保证安全性,有很严格的防火墙策略,副作用是访问某些资源会很麻烦,socks5相当于在防火墙开了个扣子,让授权的用户可以通过单个端口去访问内部所有资源。

    • socks5代理-原理

1652169942526.png - 正常浏览器访问一个网站,如果不经过代理服务器的话,就是先和对方的网扎建立起TCP连接,然后三次握手,握手完之后发起一个HTTP请求,然后服务返回HTTP响应。

    -   正常浏览器访问一个网站,设置代理服务器的话,首先是浏览器和socks5代理建立TCP连接,代理再和真正的服务器建立TCP连接。这里我们可以分成四个阶段,握手阶段,认证阶段,请求阶段,relay阶段。

        -   第一阶段:握手阶段,浏览器会向socks5代理发送请求,包的内容包括一个协议的版本号,还有支持的认证的种类,socks5服务器会选中一个认证方式,返回给浏览器如果返回的是00的话就代表不需要认证,返回其他类型的话会开始认证流程,这里我们就不对认证流程进行概述。
        -   第三阶段:请求阶段,认证通过之后浏览器会socks5服务器发起请求欧。主要信息包括版本号请求的类型,一班主要是connection请求,就代表代理服务器要和某个域名或者某个IP地址某个端口建立TCP连接。代理服务器收到响应之后,会真正好人后端服务器建立连接,然后返回一个响应。
        -   第四阶段:rely阶段。此时浏览器会发送正常发送请求,然后代理服务器接受到请求之后,会直接把请求转换成真正的服务器上。然后如果真正的服务器以后返回响应的话,那么也会把请求转发到浏览器