Go语言后端day1笔记 | 青训营

126 阅读6分钟

day1笔记简洁版:)

Day1

Go语言基础

go语言的特点:
  1. 高性能、高并发
  2. 语法简单、学习曲线平滑
  3. 丰富的标准库
  4. 完善的工具链
  5. 静态链接
  6. 快速编译
  7. 跨平台
  8. 垃圾回收
变量:
  • Go中字符串是内置类型(可以通过+拼接)
  • 变量声明:Var  a = "" or  var b int = ""
  • F := value(常量根据上下文自动确定类型)
If else:
  • If后面无括号,且if和大括号同行
  • If后面接分号,可以在if表达式之前加一个执行语句再根据执行之后的值进行判断,如:
if zt:=getStatus();zt!=0{
    fmt.Println(zt)
    return
   }
循环
  • 只有for
  • 没有括号,且三个部分都能省略
分支结构
  • 默认不需要加break;(运行完一个case之后直接不用break直接退出)
  • Switch后不加内容,然后case写条件分支(比多个if-else更清晰)
	t := time.Now()
	switch {
	case t.Hour() < 12:
		fmt.Println("It's before noon")
	default:
		fmt.Println("It's after noon")
	}
数组

拥有序号的固定长度的元素序列

切片
s :=make([]string.3)//使用make创建
s = append(s,"d")//使用append追加元素
copy(c,s)//使用copy进行赋值
fmt.Println(s[2:5])//切片取值,采取左闭右开
,ok语法

go语言函数返回值不一定只有一个,有的第一个返回bool,第二个返回error信息,这时候用ok,_ 有的第一个返回error信息,第二个返回bool,这时候用,ok。

1.在函数返回时检测到出错

value, err := pack1.Func1(param1)
if err != nil {
    fmt.Printf(“Error %s in pack1.Func1 with parameter %v”, err.Error(), param1)
    return err
}
// 函数Func1没有错误:
Process(value)
e.g.: os.Open(file) strconv.Atoi(str)

2.检测映射中是否存在一个键值:key1在映射map1中是否有值

if _, ok := m[another]; ok {
//进入处理
}

3.类型断言:检测一个接口类型变量var是否包含了类型T Go语言里面有一个语法,可以直接判断是否是该类型的变量: value, ok = element.(T),这里value就是变量的值,ok是一个bool类型,element是interface变量,T是断言的类型。

var i []interface{}
	i=append(i,"jiangzhou",666,88.88,[]string{"hello","江洲"},map[string]int{"001":123456,"002":654321});
for _,value:=range i{
		data,ok:=value.(int)
		if ok {
			fmt.Println(data)
		}
	}
Range
  • 遍历map和数组,和for一起使用
函数
  • (数据类型后置)通常返回两个值,一个是返回值,一个是错误信息
指针
  • 只有引用和解引用两种
结构
  • 带类型的字段集合
  • 初始化
	a := user{name: "wang", password: "1024"}
	b := user{"wang", "1024"}
	c := user{name: "wang"}
	c.password = "1024"
  • 普通函数:
func  checkPassword(u user,password string) bool {
	return u.password == password
}
  • 结构体方法:
func (u user) checkPassword(password string) bool {
	return u.password == password
}
错误处理
  • 在函数返回值中加入error
func findUser(users []user, name string) (v *user, err error) {
	for _, u := range users {
		if u.name == name {
			return &u, nil
		}
	}
	return nil, errors.New("not found")
}

出现错误,返回nil,error,否则返回结果和nil。

字符串操作
a := "hello"
Contains(a, "ll")//true                      是否包含
Count(a, "l")//2                             统计数量
HasPrefix(a, "he")//true                     是否以什么开头
HasSuffix(a, "llo")//true                    是否以什么结尾
Index(a, "ll")//2                            返回对应字符串的起始索引
Join([]string{"he", "llo"}, "-")//he-llo     连接多个字符串
Repeat(a, 2)//hellohello                     重复字符串
Replace(a, "e", "E", -1)//hEllo              替换
Split("a-b-c", "-")// [a b c]                切片
ToLower(a)//hello                            变成小写
ToUpper(a)//HELLO                            变成大写
len(a)//5                                    长度
字符串格式化
  • Println(s, n) // hello 123      打印多个变量并换行
  • %v打印任意类型的变量
  • %=v得到详细的结构
  • %#v得到更加详细的结构
JSON处理
  • 将结构体的声明第一个字母大写,如下:
type userInfo struct {
	Name  string
	Age   int `json:"age"`
	Hobby []string
}
  • 即可用json.Marshal进行序列化,变成一个buf数组
  • json.Unmarshal
数字解析
  • 字符串和数字转化:
  • ParseInt(字符串,进制,精度)
  • Atoi快速将字符串转成数组
  • Itoa 将数字转换成字符串
进程信息
  • os.Args获取命令行参数
  • os.Getenv(“Path”)Setenv ,获取或设置环境变量
  • Exec.Command 启动子进程

Go语言实战实例

随机数生成

使用rand.Intn()生成随机数时,需要在之前加上rand.Seed,否则就会生成相同的随机数

读取输入:
  • 输入在Stdin中,直接操作文件不方便,我们将其转换成流: Reader :=bufio.NewReader(os.Stdin)
  • 如何使用go发送请求 打开网址,点开检查,在post的请求中,右键复制成cURL。再利用curlconverter.com/#go 将cURL转换成代码。
  • 发送请求:
server, err := net.Listen("tcp", "127.0.0.1:1080")
  • 接收:
client, err := server.Accept()
reader := bufio.NewReader(conn)

得到json序列,将response的字段解析出来并反序列化(构建一个结构体),将json反序列化到结构体里面。利用oktools.net/json2go 生成结构体。初始化一个结构体,然后将json序列反序列化大结构体中。然后借助.来取其中的结果。同时将要查询的单词改为用户的输入。

TCP echo server

能够返回输入信息的TCPserver image.png

建立连接之后,我们将输出操作改为认证操作

Auth:
  • 浏览器给服务器发送报文
	// +----+----------+----------+
	// |VER | NMETHODS | METHODS  |
	// +----+----------+----------+
	// | 1  |    1     | 1 to 255 |
	// +----+----------+----------+
	// VER: 协议版本,socks5为0x05
	// NMETHODS: 支持认证的方法数量
	// METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
	// X’00’ NO AUTHENTICATION REQUIRED
	// X’02’ USERNAME/PASSWORD

将报文读出(判断版本号是否正确/methodSIze/利用该字段创建method缓冲区)

  • 返回一个包(选择建成方式)
	// +----+--------+
	// |VER | METHOD |
	// +----+--------+
	// | 1  |   1    |
	// +----+--------+
请求阶段Connect
  • 发送报文
	// +----+-----+-------+------+----------+----------+
	// |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
	// +----+-----+-------+------+----------+----------+
	// | 1  |  1  | X'00' |  1   | Variable |    2     |
	// +----+-----+-------+------+----------+----------+
	// VER 版本号,socks5的值为0x05
	// CMD 0x01表示CONNECT请求
	// RSV 保留字段,值为0x00
	// ATYP 目标地址类型,DST.ADDR的数据对应这个字段的类型。
	//   0x01表示IPv4地址,DST.ADDR为4个字节
	//   0x03表示域名,DST.ADDR是一个可变长度的域名
	// DST.ADDR 一个可变长度的值
	// DST.PORT 目标端口,固定2个字节

读取六个字段并进行验证

  • 返回报文
	// +----+-----+-------+------+----------+----------+
	// |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
	// +----+-----+-------+------+----------+----------+
	// | 1  |  1  | X'00' |  1   | Variable |    2     |
	// +----+-----+-------+------+----------+----------+
	// VER socks版本,这里为0x05
	// REP Relay field,内容取值如下 X’00’ succeeded
	// RSV 保留字段
	// ATYPE 地址类型
	// BND.ADDR 服务绑定的地址
	// BND.PORT 服务绑定的端口DST.PORT
Relay

与真正的服务器建立TCP连接

	port := binary.BigEndian.Uint16(buf[:2])

	dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port))
	if err != nil {
		return fmt.Errorf("dial dst failed:%w", err)
	}
	defer dest.Close()
	log.Println("dial", addr, port)

建立浏览器和服务器的双向数据转换。启动两个goroutine:

	go func() {
		_, _ = io.Copy(dest, reader)
		cancel()
	}()//从用户浏览器到底层服务器
	go func() {
		_, _ = io.Copy(conn, dest)
		cancel()
	}()//从底层服务器拷贝到用户浏览器

Context机制(withCancel创建)。 Ctx.Done等待context执行完(即cancel执行时)就是说我们等待两边传出错时才终止传输。

总结

其实day1所讲的内容远不止于此,主要包括go语言的基本原理,三个基础的项目。可能有很多地方过于简略。但本文的目的还是想通过一些碎片的知识点的提点,可以更加方便自己记忆,理解以及回顾。