Go基础 | 青训营笔记

440 阅读6分钟

这是我参与「第五届青训营 」笔记创作活动的第1天

Day1 Go基础及实战

Go基础知识

  1. 什么是Go?

核心特点:高性能、高并发、标准库丰富、学习简单、静态链接、快速编译、跨平台、垃圾回收

什么是静态链接?什么是动态链接?

静态编译,就是编译器在编译可执行文件的时候,将可执行文件需要调用的对应静态库(.a或.lib)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行的时候不依赖于动态链接库。

动态编译的可执行文件需要附带一个的动态链接库。在执行时,需要调用其对应动态链接库中的命令。所以其优点一方面是缩小了执行文件本身的体积,另一方面是加快了编译速度,节省了系统资源。缺点一是哪怕是很简单的程序,只用到了链接库中的一两条命令,也需要附带一个相对庞大的链接库;二是如果其他计算机上没有安装对应的运行库,则用动态编译的可执行文件就不能运行。

  1. 哪些公司正在用Go语言?

  1. 字节跳动为什么全面拥抱Go?

————由于Go的优点

  1. Go的安装与配置

这篇文章是我安装时参考的文章,蛮好用的

cloud.tencent.com/developer/a…

  1. Go是强类型语言

什么是强类型?什么是弱类型?

强类型语言是指声明变量时必须指定变量的类型,而弱类型恰恰相反

这两个术语并没有非常明确的定义,但主要用以描述编程语言对于混入不同数据类型的值进行运算时的处理方式。强类型的语言遇到函数引数类型和实际调用类型不匹配的情况经常会直接出错或者编译失败;而弱类型的语言常常会实行隐式转换,或者产生难以意料的结果。

  1. Go的基础语法

建议结合Go文档和课程内容一起学习

Go实战案例

猜谜游戏

——实现随机生成一个数,并且让用户通过输入来不断猜测,直到猜对就终止程序

核心实现
  1. 随机数生成
rand.Seed(time.Now().UnixNano())//设置随机数种子为我们当前的时间戳,保证我们生成的数字随机。
secretNumber := rand.Intn(maxNum)//Intn(num)生成一个0-num-1的数
  1. 使用bufio包处理用户读入(附Trim函数的用法)
reader := bufio.NewReader(os.Stdin)//创建一个reader
input, err := reader.ReadString('\n')//当读入到\n时停止
if err != nil {
    fmt.Println("An error occured while reading input. Please try again", err)
    return
}
input = strings.Trim(input, "\r\n")//从input字符串的首和尾分别在cutset字符串中是否存在,若不存在则直接停下,若存在则删除
guess, err := strconv.Atoi(input)

在线词典

通过下面这个链接抓包

fanyi.caiyunapp.com/

分析后发现dict是我们的请求,然后copy as curl

通过在线工具转为go代码curlconverter.com/go/

运行后就可以成功翻译

核心实现
  1. 在线工具转化后的代码分析
package main

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

func main() {
    client := &http.Client{}
    var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)//手动构建json数据流
    //创建http请求,并把请求数据包含在内
    req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
    if err != nil {
        log.Fatal(err)
    }
    req.Header.Set("Connection", "keep-alive")
    req.Header.Set("DNT", "1")
    req.Header.Set("os-version", "")
    req.Header.Set("sec-ch-ua-mobile", "?0")
    req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36")
    req.Header.Set("app-name", "xy")
    req.Header.Set("Content-Type", "application/json;charset=UTF-8")
    req.Header.Set("Accept", "application/json, text/plain, */*")
    req.Header.Set("device-id", "")
    req.Header.Set("os-type", "web")
    req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
    req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
    req.Header.Set("Sec-Fetch-Site", "cross-site")
    req.Header.Set("Sec-Fetch-Mode", "cors")
    req.Header.Set("Sec-Fetch-Dest", "empty")
    req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
    req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
    req.Header.Set("Cookie", "_ym_uid=16456948721020430059; _ym_d=1645694872")
    resp, err := client.Do(req)//发送http请求
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()//处理延迟,解决资源释放问题
    bodyText, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s\n", bodyText)//输出response body
}
  1. JSON包的Marshal功能
type DictRequest struct {//后面跟的绿色部分是说当我们用json解析时,对key做调整,即绿色的成为新key
    TransType string `json:"trans_type"`
    Source    string `json:"source"`
    UserID    string `json:"user_id"`
}
func main() {
    client := &http.Client{}
    request := DictRequest{TransType: "en2zh", Source: "good"}
    buf, err := json.Marshal(request)//Marshal函数把结构体转化为bytes流json
    if err != nil {
        log.Fatal(err)
    }
    var data = bytes.NewReader(buf)//要注意建立Reader的时候,从原来的strings变为bytes
    //创建http请求,并把刚刚的bytes流包含在内
    req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
    ...
    ...
  1. 解析response body时利用在线工具

oktools.net/json2go

可以把我们刚刚浏览器中的body粘贴进去,方便地得到一个结构体数组

然后调用Unmarshal函数,再%v输出即可

  1. 提取关键数据输出

找到音标对应的key和翻译对应的key,然后安排输出即可

SOCKS5代理

代理的含义

公司、学校都会有内网,当我们从内网访问外网的服务器的时,往往是不能直接实现的。

通常我们需要有一台代理服务器负责转发我们的请求,建立连接的方式有多种,SOCKS5协议就是其中的一种。

SOCKS5代理原理

主要是以下几个流程:协商阶段——认证阶段(本次项目实现无密码认证,不过这里是可以有多种方式的,比起SOCKS4有了极大的改进)——请求阶段——响应阶段

核心实现
  1. 创建一个简单的echo server
package main

import (
    "bufio"
    "log"
    "net"
)

func main() {
    server, err := net.Listen("tcp", "127.0.0.1:1080")//创建server,监听本地1080端口
    if err != nil {
        panic(err)
    }
    for {//死循环判断是否有请求,并与它建立连接
        client, err := server.Accept()
        if err != nil {
            log.Printf("Accept failed %v", err)
            continue
        }
        go process(client)//创建go routine处理(类比子线程)
    }
}

func process(conn net.Conn) {
    defer conn.Close()//延迟关闭资源
    reader := bufio.NewReader(conn)//用请求创建Reader
    for {
        b, err := reader.ReadByte()//从Reader逐字节读入
        if err != nil {
            break
        }
        _, err = conn.Write([]byte{b})//把从Reader逐字节读入的数据返回
        if err != nil {
            break
        }
    }
}
  1. 实现鉴权auth

解析三个参数,返回两个参数,表明后续建立链接的协议与鉴权方式(0x00是不需要密码)

  1. 请求阶段request

解析六个参数,判断是否合法,然后返回六个参数

  1. 响应阶段relay

利用Copy函数,把代理服务器访问时得到的数据全都返回给Client机器

课后作业

1.fmt.scanf的使用

其实十分类似与scanf的使用,而且很方便地能够处理非法输入,完成的核心代码如下

var x int
if _, err := fmt.Scanf("%d", &guess); err != nil {
    fmt.Println("An error occured while reading input. Please try again", err)
    continue
}

2.再实现一个翻译引擎

使用的是火山翻译,本来是想用百度翻译的,但是它有反爬,所以放弃了

3.并行观察运行速度

计算程序运行时间

    start := time.Now()
    ...
    ...
    finish := time.Now()
    fmt.Println("run time:", finish.Sub(start))

多线程——sync.WaitGroup的使用

func func1 (..., wg *sync.WaitGroup){
    ...
    defer wg.Done()
    ...
}

func main(){
    ...
    var wg sync.WaitGroup
    go func1(..., &wg)
    go func1(..., &wg)
    ...
    wg.Wait()
    ...
}