Go基础语法和常用特性解析 | 青训营;

176 阅读8分钟

go基础语法

1.简介

github.com/wangkechun/…

image.png

image.png

image.png

2.配置环境

2.1 安装golang

image.png

2.2 配置编辑环境

image.png

3.基础语法

3.1 helloword

 package main
 ​
 import "fmt"
 ​
 func main() {
    fmt.Println("Hello Golang!")
 }

image.png

3.2 变量

  • 通过var声明(自动推导或者显示指出)
 //var声明
 var a = "initial"
 var b, c int = 1, 2
 var d = true
 var e float64
  • 冒号声明
 f := float32(e)
 g := a + "foo"
 fmt.Println(a, b, c, d, e, f)
 fmt.Println(g)
  • 常量
 const s string = "constant"
 const h = 50000000
 const l = 3e20 / h

image.png

3.3 if-else

 package main
 ​
 import "fmt"
 ​
 func main() {
    if 7%2 == 0 {
       fmt.Println("7 is even")
    } else {
       fmt.Println("7 is odd")
    }
 ​
    if 8%4 == 0 {
       fmt.Println("8 is divisible by 4")
    }
 ​
    if num := 9; num < 0 {
       fmt.Println(num, "is negative")
    } else if num < 10 {
       fmt.Println(num, "has 1 digit")
    } else {
       fmt.Println(num, "has multiple digits")
    }
 }

image.png

3.4 for循环

  • 死循环
 //死循环
 for {
    fmt.Println("loop")
    break
 }
  • 正常循环
 //正常循环
 for j := 7; j < 9; j++ {
    fmt.Println(j)
 }
  • continue关键字
 //continue
 for n := 0; n < 5; n++ {
    if n%2 == 0 {
       continue
    }
    fmt.Println(n)
 }
  • 使用变量控制循环
 i := int32(1)
     //使用i控制
     for i <= 3 {
         fmt.Println(i)
         i += 1
     }
  • 效果

image.png

3.5 switch(可以switch任何变量)(不要要加break)

 a := 2
 switch a {
 case 1:
    fmt.Println("one")
 case 2:
    fmt.Println("two")
 case 3:
    fmt.Println("three")
 case 4, 5:
    fmt.Println("four or five")
 default:
    fmt.Println("other")
 }
 t := time.Now()
 switch {
 case t.Hour() < 12:
    fmt.Println("it's before noon")
 default:
    fmt.Println("it's after noon")
 }

image.png

3.6 数组

 var a [5]int
 a[4] = 100
 fmt.Println(a[4], len(a))
 b := [5]int{1, 2, 3, 4, 5}
 fmt.Println(b)
 var twoD [2][3]int
 for i := 0; i < 2; i++ {
    for j := 0; j < 3; j++ {
       twoD[i][j] = i + j
    }
 }
 fmt.Println("2d:", twoD)

image.png

3.7 切片(可变长度的数组)make创建slice

  • 用make创建长度为3的字符串数组
 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)
  • 创建长度和s一样的切片,将s的内容拷贝给c
 c := make([]string, len(s))
 //将s的内容拷贝给c
 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)
  • 结果

image.png

3.8 map make map[key]value

  • make创建
 m := make(map[string]int)
 m["one"] = 1
 m["two"] = 2
 fmt.Println(m)
 fmt.Println(len(m))
 fmt.Println(m["one"])
 fmt.Println(m["none"]) //不存在的为0
  • 获取键值
 r, ok := m["none"]
 fmt.Println(r, ok)
 //删除map中的数据
 delete(m, "one")
  • 变量创建与初始化
 m2 := map[string]int{"one": 1, "two": 2}
 var m3 = map[string]int{"one": 1, "two": 2}
 fmt.Println(m2, m3)

image.png

3.9 range (遍历加强)for index(key),value(value):=range iterator

  • 遍历数组,;两个参数index与值
 nums := []int{1, 3, 4}
 sum := 0
 //遍历数组,;两个参数index与值
 for i, num := range nums {
    sum += num
    if num == 2 {
       fmt.Println("index:", i, "num:", num)
    }
 }
 fmt.Println(sum)
  • 遍历map,两个参数 key与value
 //遍历map,两个参数 key与value
 m := map[string]string{"a": "A", "b": "B", "c": "C"}
 for k, v := range m {
    fmt.Println(k, v)
 }
  • 遍历map,可以不使用value
 //可以不使用value
 for k := range m {
    fmt.Println("key:", k)
 }

image.png

3.10 函数

  • 返回单值
 func add(a int, b int) int {
    return a + b
 }
 ​
 func add2(a, b int) int {
    return a + b
 }
  • 返回多值
 // 第一个值返回value,第二个返回是否存在
 func exists(m map[string]string, k string) (v string, ok bool) {
    v, ok = m[k]
    return v, ok
 }
  • 调用
 res := add2(1, 2)
 fmt.Println(res)
 ​
 v, ok := exists(map[string]string{"a": "A"}, "a")
 fmt.Println(v, ok)
  • 结果

image.png

3.11 指针__(有限指针,修改传入参数)__

  • 两个函数
 func add2(n int) {
    n += 2
 }
 ​
 func add2ptr(n *int) {
    *n += 2
 }
  • 函数2可以改变实参的值
 n := 5
 add2(n)
 fmt.Println(n)
 add2ptr(&n)
 fmt.Println(n)

image.png

3.12 结构体 type name struct{}

  • 语法
 type user struct {
    name     string
    password string
 }
  • 初始化结构体变量
 a := user{name: "wang", password: "1024"}
 b := user{"wang", "1024"}
 c := user{name: "wang"}
 c.password = "1024"
 var d user
 d.name = "wang"
 d.password = "1024"
  • 结构体做函数参数
 func checkPassword(u user, password string) bool {
    return u.password == password
 }
  • 结构体指针做函数参数
 func checkPassword2(u *user, password string) bool {
    return u.password == password
 }
  • 调用
 fmt.Println(a, b, c, d)
 fmt.Println(checkPassword(a, "haha"))
 fmt.Println(checkPassword2(&a, "haha"))

image.png

3.13 为结构体创建方法

  • 语法

func (variable 结构体类型或结构体指针类型) resetPassword(password string) 返回值类型

 type user struct {
    name     string
    password string
 }
 ​
 func (u user) checkPassword(password string) bool {
    return u.password == password
 }
 ​
 func (u *user) resetPassword(password string) {
    u.password = password
 }
  • 调用
 func main() {
    a := user{name: "wang", password: "1024"}
    a.resetPassword("2048")
    fmt.Println(a.checkPassword("2048"))
 }
  • 效果

image.png

3.14 错误处理

 func findUser(users []user, name string) (v *user, err error) {
    //不需要index的话,可以用占位符替代
    for _, u := range users {
       if u.username == name {
          return &u, nil
       }
    }
    return nil, errors.New("not found!")
 }
 u, err := findUser([]user{{"wang", "1024"}}, "wang")
 if err != nil {//有错误
    fmt.Println(err)
    return
 }
 fmt.Println(u.username)//无错误,读取姓名
 if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
    fmt.Println(err)//有错误
    return
 } else {
    fmt.Println(u.username)
 }

image.png

3.15 字符串处理

 fmt.Println(strings.Contains(a, "li"))
 fmt.Println(strings.Count(a, "l"))
 fmt.Println(strings.HasPrefix(a, "he"))
 fmt.Println(strings.HasSuffix(a, "llo"))
 fmt.Println(strings.Index(a, "li"))
 fmt.Println(strings.Join([]string{"he", "llo"}, "-"))
 fmt.Println(strings.Repeat(a, 2))
 fmt.Println(strings.Replace(a, "e", "E", -1))
 fmt.Println(strings.Split("a-b-c","-"))
    fmt.Println(strings.ToLower(a))
 fmt.Println(strings.ToUpper(a))
 fmt.Println(len(a))
 b:="你好"
 fmt.Println(len(b))

image.png

  • 字符串输出格式化

fmt.printf("%v",variable) //输出任意的变量

 fmt.Printf("s=%v\n", s)
 fmt.Printf("n=%v\n", n)
 fmt.Printf("p=%v\n", p)

fmt.printf("%+v",variable)//详细输出变量信息

 fmt.Printf("p=%+v\n", p)

fmt.printf("%#v",variable)//更详细输出变量信息

 fmt.Printf("p=%#v\n", p)

输出小数

 f := 3.14159
 fmt.Println(f)
 fmt.Printf("%.2f", f)
  • 效果

image.png

3.16 json处理

1.要想转为json,字段首个字母必须大写

 // 要想转为json,字段首个字母必须大写
 type userInfo struct {
    Name  string
    Age   int `json:"age"` //这样设置转成的json为小写
    Hobby []string
 }

json.marshal(var)进行序列化得到buf数组,然后用string包裹可得到json对象

 a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
 buf, err := json.Marshal(a) //转为json
 if err != nil {
    panic(err)
 }
 fmt.Println(buf)         //buf数组
 fmt.Println(string(buf)) //转为json字符串

对生成的json数据进行缩进设置

 buf, err = json.MarshalIndent(a, "", "\t")
 if err != nil {
    panic(err)
 }
 fmt.Println(string(buf))

利用buf数组和变量指针实现反序列化

 var b userInfo
 //反序列化
 err = json.Unmarshal(buf, &b)
 if err != nil {
    panic(err)
 }
 fmt.Printf("%#v\n", b)
  • 效果

image.png

3.17 时间处理

  • 获得当前时间
 //获取当前时间
 now := time.Now()
 fmt.Println(now)
  • 使用time.Date创建时间(参数是年月日时分秒时区)
 t := time.Date(2023, 2, 23, 1, 25, 26, 0, time.UTC)
 t2 := time.Date(2024, 3, 27, 2, 30, 36, 0, time.UTC)
 fmt.Println(t)
 fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute())
  • 按照示例对日期格式化
 //按照示例进行格式化
 fmt.Println(t.Format("2006-01-02 15:04:05"))
  • 日期相减获得分秒之差
 diff := t2.Sub(t)
 fmt.Println(diff)
 fmt.Println(diff.Minutes(), diff.Seconds())
  • 利用time.Parse(示例,值)生成时间
 //示例 + 值生成新的时间
 t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
 if err != nil {
    panic(err)
 }
 fmt.Println(t3 == t) //成成时间戳
 fmt.Println(now.Unix())
  • 效果

image.png

3.18 解析字符串为数字

 f, _ := strconv.ParseFloat("1,234", 64)
 fmt.Println(f)
  • 转为10进制64为整形
 n, _ := strconv.ParseInt("111", 10, 64)
 fmt.Println(n)
  • 第二个参数为0,让自动推断进行转换
 n, _ = strconv.ParseInt("0x1000", 0, 64)
 fmt.Println(n)
  • strconv.Atoi直接转为整形
 n2, _ := strconv.Atoi("123")
 fmt.Println(n2)
  • 转换异常的处理
 //转换异常
 n2, err := strconv.Atoi("AAA")
 fmt.Println(n2, err)
  • 效果

image.png

3.19 获取进程相关信息

  • 获取命令行参数
 fmt.Println(os.Args)
  • 获取或写入环境变量
     fmt.Println(os.Getenv("PATH"))
     fmt.Println(os.Setenv("AA", "BB"))
  • 快速启动子进程命令,并获取输入输出
 buf, err := exec.Command("group", "127.0.0.1", "/etc/hosts").CombinedOutput()
 if err != nil {
    panic(err)
 }
 fmt.Println(string(buf))
  • 效果

image.png

4.猜数字游戏

4.1 生成随机数(rand)

注意:需要设置随机数种子,否则每次生成相同的随机数

 maxNum := 100
 //初始化随机数种子
 rand.Seed(time.Now().UnixNano())
 //生成不大于100的随机数
 secretNumber := rand.Intn(maxNum)
 fmt.Printf("The secret number is", secretNumber)

image.png

4.2 实现用户输入输出

  • 使用os标准输入流获得buf io的输入
 reader := bufio.NewReader(os.Stdin)
  • 读入一行数据(可能出错),遇到换行结束
 input, err := reader.ReadString('\n')
     if err != nil {
         fmt.Println("An err occured while reading input,please again", err)
         return
     }
  • 去掉输入的换行
 input = strings.TrimSuffix(input, "\n")
 input = strings.TrimSuffix(input, "\r")
  • 字符串转为数字
 guess, err := strconv.Atoi(input)
 if err != nil {
    fmt.Println("invalid input.please enter an integer value", err)
    return
 }
 fmt.Println("Your guess is:", guess)
  • 效果

image.png

4.3 数字判断

  • 逻辑
 //判断逻辑
 if guess > secretNumber {
    fmt.Println("bigger,please again")
 } else if guess < secretNumber {
    fmt.Println("small,please again")
 } else {
    fmt.Println("niu bi")
 }
  • 效果

image.png

4.4 出错无限游戏

 for {
    input, err := reader.ReadString('\n')
    if err != nil {
       fmt.Println("An err occured while reading input,please again", err)
       continue
    }
    //去掉输入的换行
    input = strings.TrimSuffix(input, "\n")
    input = strings.TrimSuffix(input, "\r")
    guess, err := strconv.Atoi(input)
    if err != nil {
       fmt.Println("invalid input.please enter an integer value", err)
       continue
    }
    fmt.Println("Your guess is:", guess)
 ​
    //判断逻辑
    if guess > secretNumber {
       fmt.Println("bigger,please again")
    } else if guess < secretNumber {
       fmt.Println("small,please again")
    } else {
       fmt.Println("niu bi")
       break //游戏胜利,退出
    }
 }
  • 效果

image.png

5.网络词典

5.1 获取翻译服务

彩云小译 - 在线翻译 (caiyunapp.com)

image.png

image.png

image.png

5.2 生成模拟请求并复制到ide

Convert curl to Go (curlconverter.com)

 package main
 ​
 import (
    "fmt"
    "io"
    "log"
    "net/http"
    "strings"
 )
 ​
 func main() {
    client := &http.Client{}
    var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
    req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
    if err != nil {
       log.Fatal(err)
    }
    req.Header.Set("authority", "api.interpreter.caiyunai.com")
    req.Header.Set("accept", "application/json, text/plain, */*")
    req.Header.Set("accept-language", "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,en-GB;q=0.6")
    req.Header.Set("app-name", "xy")
    req.Header.Set("content-type", "application/json;charset=UTF-8")
    req.Header.Set("device-id", "f8fed39c44af2bc4c015cc7e2584fde0")
    req.Header.Set("origin", "https://fanyi.caiyunapp.com")
    req.Header.Set("os-type", "web")
    req.Header.Set("os-version", "")
    req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
    req.Header.Set("sec-ch-ua", `"Chromium";v="112", "Microsoft Edge";v="112", "Not:A-Brand";v="99"`)
    req.Header.Set("sec-ch-ua-mobile", "?0")
    req.Header.Set("sec-ch-ua-platform", `"Windows"`)
    req.Header.Set("sec-fetch-dest", "empty")
    req.Header.Set("sec-fetch-mode", "cors")
    req.Header.Set("sec-fetch-site", "cross-site")
    req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.58")
    req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
    resp, err := client.Do(req) //发起请求
    if err != nil {
       log.Fatal(err)
    }
    defer resp.Body.Close()
    bodyText, err := io.ReadAll(resp.Body)
    if err != nil {
       log.Fatal(err)
    }
    fmt.Printf("%s\n", bodyText)
 }
  • 效果

image.png

5.3构造请求数据的结构体

 type DictRequest struct {
    TransType string `json:"trans_type"`
    Source    string `json:"source"`
    UserID    string `json:"user_id"`
 }

5.4 利用自己的结构体模拟请求的json

 request := DictRequest{TransType: "en2zh", Source: "good"}
 buf, err := json.Marshal(request)
 if err != nil {
    log.Fatal(err)
 }
 var data = bytes.NewReader(buf)
 req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
 if err != nil {
    log.Fatal(err)
 }
  • 效果不变

image.png

  • 反序列化解析http的response

JSON转Golang Struct - 在线工具 - OKTools

复制返回的json值

image.png

image.png

  • 结构体复制到项目并反序列化输出
     //fmt.Printf("%s\n", bodyText)
     //反序列化到响应对象
     var dictResponse DictResponse
     err = json.Unmarshal(bodyText, &dictResponse)
     if err != nil {
         log.Fatal(err)
     }
     fmt.Printf("%#v\n", dictResponse)
  • 效果

image.png

5.5 输出自己需要的字段

 //输出需要的信息
 fmt.Println("good", "UK", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
 for _, value := range dictResponse.Dictionary.Explanations {
    fmt.Println(value)
 }
  • 效果

image.png

5.6 将查询封装为函数实现多次输入查询

  • 查询封装
 func query(word string) {
    client := &http.Client{}
    request := DictRequest{TransType: "en2zh", Source: word}
    buf, err := json.Marshal(request)
    if err != nil {
       log.Fatal(err)
    }
    var data = bytes.NewReader(buf)
    req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
    if err != nil {
       log.Fatal(err)
    }
    req.Header.Set("authority", "api.interpreter.caiyunai.com")
    req.Header.Set("accept", "application/json, text/plain, */*")
    req.Header.Set("accept-language", "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,en-GB;q=0.6")
    req.Header.Set("app-name", "xy")
    req.Header.Set("content-type", "application/json;charset=UTF-8")
    req.Header.Set("device-id", "f8fed39c44af2bc4c015cc7e2584fde0")
    req.Header.Set("origin", "https://fanyi.caiyunapp.com")
    req.Header.Set("os-type", "web")
    req.Header.Set("os-version", "")
    req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
    req.Header.Set("sec-ch-ua", `"Chromium";v="112", "Microsoft Edge";v="112", "Not:A-Brand";v="99"`)
    req.Header.Set("sec-ch-ua-mobile", "?0")
    req.Header.Set("sec-ch-ua-platform", `"Windows"`)
    req.Header.Set("sec-fetch-dest", "empty")
    req.Header.Set("sec-fetch-mode", "cors")
    req.Header.Set("sec-fetch-site", "cross-site")
    req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.58")
    req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
    resp, err := client.Do(req) //发起请求
    if err != nil {
       log.Fatal(err)
    }
    defer resp.Body.Close()
    bodyText, err := io.ReadAll(resp.Body)
    if err != nil {
       log.Fatal(err)
    }
    //请求错误
    if resp.StatusCode != 200 {
       log.Fatal("bad StatusCode", resp.StatusCode, "body", string(bodyText))
    }
    //fmt.Printf("%s\n", bodyText)
    //反序列化到响应对象
    var dictResponse DictResponse
    err = json.Unmarshal(bodyText, &dictResponse)
    if err != nil {
       log.Fatal(err)
    }
    //fmt.Printf("%#v\n", dictResponse)
    //输出需要的信息
    fmt.Println("good", "UK", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
    for _, value := range dictResponse.Dictionary.Explanations {
       fmt.Println(value)
    }
 }
  • 主函数实现多次调用
 func main() {
    reader := bufio.NewReader(os.Stdin)
    for {
       fmt.Println("please input your word(input 0 exit):")
       word, _ := reader.ReadString('\n')
       word = strings.TrimSuffix(word, "\n")
       word = strings.TrimSuffix(word, "\r")
       if word == "0" { //输入0退出
          return
       }
       query(word)
       fmt.Println("------------------------------------------")
    }
 }
  • 效果

image.png

6.SOCKS5代理服务器

6.1 socks5代理服务器原理

image.png

6.2 编写侦听端口(简单回复)

  • 配置打印请求信息的函数
 func process(conn net.Conn) {
    //断开连接
    defer conn.Close()
    //读取数据
    reader := bufio.NewReader(conn)
    for {
       //每次读一个字节
       b, err := reader.ReadByte()
       if err != nil {
          break
       }
       _, err = conn.Write([]byte{b})
       if err != nil {
          break
       }
    }
 }
  • 监听本机服务的1080端口
 server, err := net.Listen("tcp", "127.0.0.1:1080")
 if err != nil {
    panic(err)
 }
  • 开启服务与连接进程
 for {
    client, err := server.Accept()
    if err != nil {
       log.Printf("Accept failed %v", err)
       continue
    }
    //启动子线程,轻松处理上万并发
    go process(client)
 }
  • 效果

image.png

6.3 编写请求认证

  • 常量
 const socks5Ver = 0x05
 const cmdBind = 0x01
 const atypeIPV4 = 0x01
 const atypeHOST = 0x03
 const atypeIPV6 = 0x04
  • 修改process进行认证
 err := auth(reader, conn)
 if err != nil {
    log.Printf("client %v auth failed %v", conn.RemoteAddr(), err)
    return
 }
 log.Printf("auth success")
  • 认证方法
 func auth(reader *bufio.Reader, conn net.Conn) (err error) {
    //先读取一个版本
    ver, err := reader.ReadByte()
    if err != nil {
       return fmt.Errorf("read ver failed:%w", err)
    }
    //不是代理5的版本
    if ver != socks5Ver {
       return fmt.Errorf("not supported ver:%v", ver)
    }
    //读取方法类型
    methodSize, err := reader.ReadByte()
    if err != nil {
       return fmt.Errorf("read methodSize failed:%w", err)
    }
    method := make([]byte, methodSize)
    _, err = io.ReadFull(reader, method)
    if err != nil {
       return fmt.Errorf("read method failed:%w", err)
    }
    log.Println("ver", ver, "method", method)
    _, err = conn.Write([]byte{socks5Ver, 0x00})
    if err != nil {
       return fmt.Errorf("write failed:%w", err)
    }
    return nil
 }
  • 效果

image.png

6.4 截取浏览器报文(编写connection函数)

  • 修改process函数,加入connect函数
 //连接
 err = connect(reader, conn)
 if err != nil {
    log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
    return
 }
  • 创建长度为4的缓冲区
 //创建长度为4的缓冲区
 buf := make([]byte, 4)
 _, err = io.ReadFull(reader, buf)
 if err != nil {
    return fmt.Errorf("read header failed:%w", err)
 }
  • 读取到报文的前4个字段
 ver, cmd, atyp := buf[0], buf[1], buf[3]
  • 验证字段的合法性
 if ver != socks5Ver {
    return fmt.Errorf("not supported ver:%v", ver)
 }
 if cmd != cmdBind {
    return fmt.Errorf("not supported cmd:%v", cmd)
 }
 addr := ""
  • 判断报文的类型
 switch atyp {
 //ipv4
 case atypeIPV4:
    _, err = io.ReadFull(reader, buf)
    if err != nil {
       return fmt.Errorf("read atyp failed:%w", err)
    }
    addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])
    //host类型
 case atypeHOST:
    hostSize, err := reader.ReadByte()
    if err != nil {
       return fmt.Errorf("read hostSize failed:%w", err)
    }
    host := make([]byte, hostSize)
    _, err = io.ReadFull(reader, host)
    if err != nil {
       return fmt.Errorf("read host failed:%w", err)
    }
    addr = string(host)
    //ipv6
 case atypeIPV6:
    return errors.New("IPv6: no supported yet")
 default:
    return errors.New("invalid atyp")
 }
  • 复用之前的切片,只用两个空间
 _, err = io.ReadFull(reader, buf[:2])
 if err != nil {
    return fmt.Errorf("read port failed:%w", err)
 }
  • 解析出端口号
 port := binary.BigEndian.Uint16(buf[:2])
 ​
 log.Println("dial", addr, port)
  • 返回报文信息
 _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
 if err != nil {
    return fmt.Errorf("write failed: %w", err)
 }
  • 效果

image.png

6.4 双向copy

  • 建立拨号连接
 dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port))
 if err != nil {
    return fmt.Errorf("dial dist failed:%w", err)
 }
 defer dest.Close()
  • context对象双向拷贝
 ctx, cancel := context.WithCancel(context.Background())
 defer cancel()
 ​
 //从当地拷贝到服务器
 go func() {
    _, _ = io.Copy(dest, reader)
    cancel()
 }()
 //从服务器拷贝到当地
 go func() {
    _, _ = io.Copy(conn, dest)
    cancel()
 }()
 ​
 //等到context执行完,cancel执行完
 <-ctx.Done()

7.总结

Go语言基础语法学习总结

Go语言是一门简洁、高效、可靠的编程语言,广泛应用于云计算、分布式系统、网络编程等领域。在学习Go语言的过程中,我们首先需要掌握其基础语法,下面我将对Go语言基础语法进行总结。

  1. 变量与类型
    Go语言是静态类型语言,变量需要显式声明其类型。可以使用关键字var来声明变量,例如:
    var num int
    var str string
  2. 常量
    使用关键字const来定义常量,常量在程序运行期间不可修改,例如:
    const Pi = 3.14159
  3. 数据类型
    Go语言提供了多种基本数据类型,包括int、float、string、bool等。还有复合类型如数组、切片、字典、结构体等。
  4. 控制流程
    Go语言支持条件语句和循环语句来改变程序的控制流程。常见的条件语句有if、switch,循环语句有for和range。
  5. 函数
    函数是Go语言中的重要组成部分,使用关键字func来定义函数。函数可以有参数和返回值,例如:
    func add(a, b int) int {
    return a + b
    }
  6. 包和导入
    Go语言使用包来组织代码。一个Go程序可以由多个包组成,其中只有一个包为main包,该包包含程序的入口函数main。使用关键字import来导入其他包,例如:
    import “fmt”
  7. 结构体
    结构体是一种自定义的数据类型,可以包含多个字段来描述一个对象的属性。通过定义结构体可以创建具有自定义数据结构的变量。
  8. 方法
    Go语言中的方法是一种特殊类型的函数,与结构体关联在一起。通过在函数名前加上接收者类型,可以定义方法。
  9. 接口
    接口是一种抽象类型,用于定义对象的行为。接口定义了一组方法的签名,任何实现了这些方法的类型都隐式地实现了该接口。
  10. 错误处理
    Go语言中通过返回值来进行错误处理。在函数调用过程中,如果发生错误,可以通过返回一个错误类型的值来指示错误的发生。

以上是我对Go语言基础语法的一个总结。掌握了这些基础知识,我们就可以编写简单的Go程序了。当然,Go语言还有很多更高级的特性和库,希望你能继续深入学习和探索。