day1笔记简洁版:)
Day1
Go语言基础
go语言的特点:
- 高性能、高并发
- 语法简单、学习曲线平滑
- 丰富的标准库
- 完善的工具链
- 静态链接
- 快速编译
- 跨平台
- 垃圾回收
变量:
- 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
建立连接之后,我们将输出操作改为认证操作
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语言的基本原理,三个基础的项目。可能有很多地方过于简略。但本文的目的还是想通过一些碎片的知识点的提点,可以更加方便自己记忆,理解以及回顾。