这是我参与「第三届青训营-后端场」笔记创作活动的第一篇笔记。
源码
短链接:hi-hi.cn/go
Go语言特点
- 高性能、高并发
- 语法简单,学习曲线平缓
- 丰富的标准库
- 完善的工具链
- 静态链接
- 快速编译
- 跨平台
- 垃圾回收
开发环境
- 集成开发环境
(略)
- 基于云的开发环境
短链接:hi-hi.cn/gitpod
基础语法
Hello World
package main
import(
"fmt"
)
func main(){
fmt.PrintIn("Hello World")
}
变量
// 声明变量
var a = "initial"
var b, c int = 1, 2
f := float32(e)
常量
// 声明常量
const s string = "constant"
常量可根据上下文自动确定类型
循环
条件无括号
for j := =7; j < 9; j++{ // 三个部分均可省略,若均为空则为死循环
fmt.PrintIn(j)
}
switch-case分支
变量名无括号
无break也不需跑完所有case分支
可使用任意变量类型
switch后不加变量,在case后加条件可取代if-else
数组
具有编号且长度固定
// 声明数组
var a [5]int
切片
可变长度
s:= make([]string, 3)
s = append(s, "d") // 需重新赋值回原数组
c := make([]strinh,len(s)) // 创建时即可指定长度
copy(c, s) // 在两个数组间复制
s[2:5] // 不支持负数索引
map (哈希/字典)
m := make(map[string]int)
m["one"] = 1
r, ok := m["unknow"] // 确认key是否存在于map中
delete(m, "one") // 从mao中删除
// map完全无序
range
for i, num := range nums{ // 返回索引和数值,不需要返回索引可用下划线free
... // 遍历map时返回key和value
}
函数
// 示例:两数相加
func add(a int, 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
}
指针
主要用于对常用参数进行修改
// 不起作用,因为其中的n为拷贝
func add2(n int){
n += 2
}
// 要使用指针,在参数前加*,使用时需在参数前加&
func add2ptr(n *int){
*n += 2
}
...
add2ptr(&n)
结构体
type user struct{
name string
password string
}
a:= user{name:"wang", password:"1024"}
// 读取写入
d.name = "wang"
// 结构体可作为参数,使用指针可对结构体进行修改,也可以防止对大型结构体拷贝的开销
func checkPassword(u user, password string)bool{
return u.password == password
}
结构体方法
func(u user)checkPassword(password string)bool{
return u.password == password
}
// 结构变为结构体方法后可用a来调用该函数
a := user(name: "wang", password: "1024")
a.checkPassword("2048")
错误处理
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")
}
func main(){
u, err := findUser([]user{{"wang", "1024"}}, "wang")
if err != nil{
fmt.PrintIn(err)
return
}
fmt.PrintIn(u.name)// wang
if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil{
fmt.PringIn(err)//not found
return
}else{
fmt.PrintIn(u.name)
}
}
字符串操作
a := "hello"
// 以下省略fmt.PrintIn(string. )
Contains(a, "ll") //true
Count(a,"l") //2
HasPrefix(a, "he") //true
HasSuffux(a, "llo") //true
Index(a, "ll") //2 查找某个字符串的位置
Join([]string{"he", "llo"}, "-") //he-llo
Repeat(a, 2) //hellohello
Replace(a, "e", "E", -1) //hEllo
Spit("a-b-c", "-") //[a b c]
ToLower(a) //hello
ToUpper(a) //HELLO
len(a) //5
b := "你好"
len(b) //6
字符串格式化
s := "hello"
n := 123
p := point{1, 2}
fmt.PrintIn(s, n) //hello 123
fmt.PrintIn(p) //{1 2}
//Go中用%v即可输出全类型数据
fmt.Printf("s=%v\n", s) //s=hello
fmt.Printf("n=%v\n", n) //n=123
fmt.Printf("p=%v\n", p) //p={1 2}
fmt.Printf("p=%+v\n", p) //p={x:1 y:2}
fmt.Printf("p=%#v\n", p) //p=main.point{x:1 y:2}
f := 3.141592653
fmt.PrintIn(f) //3.141592653
fmt.Printf("%.2f\n", f) //3.14
JSON处理
// 字符段名称需首字母大写
package main
import{
"encoding/json"
"fmt"
}
type userInfo struct{
Name string
Age int 'json:"age"' //在输出时将显示age
Hobby []string
}
func main(){
a := userInfo(Name: "wang", Age: 18, Hobby:[]string{"Golang", "TypeScript"}
buf, err := json.Marshal(a)
if err != nil{
panic(err)
}
fmt.PrintIn((buf)) //{123 34 78 97...}
fmt.PrintIn(string(buf)) //{Name:"wang",age:18,Hobby:{"Golang","TybeScript"}
var b userInfo
err = json.Unmarshal(buf, &b)
if err != nil{
panic(err)
}
fmt.Printf("%#n\n", b) //main.userInfo{Name:...}
时间处理
now := time.Now()
fmt.PrintIn(now)
t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
fmt.PrintIn(t)) //2022-03-27 01:25:36 +0000 UTC
fmt.OrintIn(t.Year(),t.Month(), t.Day(), t.Hour(), t.Minute())//2022 March 21 1 25
fmt.PrintIn(t.Format("2006-01-02 15:04:05") //202-03-27 01:25:36
diff := t2.Sub(t)
fmt.PrintIn(diff) //1h5m0s
fmt.PrintIn(diff.Minutes(), diff.Seconds())//65 3900
t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
if err != nil{
panic(err)
}
fmt.PrintIn(t3 == t) //true
fmt.PrintIn(now, Unix()) //1648738080 //时间戳
数字解析
import {
"fmt"
"strconv"
}
func main(){
f, _ := strconv.ParseFloat("1.234", 64)
fmt.PrintIn(f) //1.234
n, _ := strconv.ParseInt("111", 10, 64)
fmt.PrintIn(n) //111
n, _ := strconv.ParseInt("0x1000", 0, 64)
fmt.PrintIn(n) //4096
n2, _ := strconv.Atoi("123") //Atoi:十进制字符串转为数字 itoA:数字转为字符串
fmt.PrintIn(n2) //123
n2, err := strconv.Atoi("AAA")
fmt.PrintIn(n2, err) //0 strconv.Atoi: parsing"AAA" invalid syntax
进程信息
import{
"fmt"
"os
os/exec"
}
func main(){
//go run example/20-env/main.go a b c d
fmt.PrintIn(os.Args) //[/var/folders/8p/n34xxfnx38dg8bv_x8162t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]
fmt.PrintIn(os.Getenv("PATH")) // /user/local/go/bin...
fmt.PrintIn(os.Setenv("AA", "BB"))
buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
if err != nil{
panic(err)
}
fmt.PrintIn(string(buf)) // 127.0.0.1 localhost
}
实战
猜谜游戏
- 生成随机数
package main
import(
"fmt"
"math/rand"
)
func main(){
maxNum := 100
secretNumber := rand.Intn(maxNumber)
fmt.PrintIn("The secret number is ", secretNumber)
问题:多次输出结果为相同数字
// 未添加随机数种子
解决:头文件加“time”,用时间戳初始化随机数种子
rand.Seed(time.Now().UnixNano())
- 读取用户输入
fmt.PrintIn("Please input your guess")
readr := bufo.NewReader(os.Stdin)
input, err := reader.ReadString('\n') //读取一行输入
if err != nil{
fmt.PrintIn("An error occired while reading input.Please try again", err)
return
}
input = strings.TrimSuffix(input, "\n")//去掉换行符,win系统为\r\n
guess, err := strconv.Atoi(input)//将字符串转化为数字
if err != nil{
fmt.PrintIn("Invalid input.Please enter an integer value")
return
}
fmt.PrintIn("Your guess is", guess)
- 实现判断逻辑
- 实现游戏循环
加入for循环,将return改为continue,只有胜利时为break
在线词典
- 抓包
以彩云小译为例
Network-dict-POST
- 代码生成
- 生成request body
- 解析response body
- 打印结果
SOCKS5代理
- 原理
- 握手阶段 浏览器向socks5发送请求
- 认证阶段 socks返回认证方式,00表示不需要认证
- 请求阶段 认证通过后浏览器向socks5发起请求,一般为connection请求,代理服务器收到响应后会和后端服务器建立连接,然后返回一个响应
- relay阶段 浏览器发送正常请求,代理服务器收到后将请求转到真正的服务器上,真正的服务器若返回响应,也会把响应转发到浏览器
- TCP echo server
- auth
- 请求阶段
- relay阶段
//代理部分看懂课程源码了,但还不太能用文字清晰地表达出来,之后有时间可以单独整理