这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天
go的基本语法
基本上和c++语法类似,但是做了很多的简化。其中比较重要的应该就是引入了切片这个数据类型。数组是不可变的, 即长度确定后不可以动态扩容。而切片则可以动态扩容与数组相比切片的长度是不固定的,可以追加元素,在追加时 可能使切片的容量增大。 其他语法基本与c++类似,做了些简化,比较好理解。
go的三个实例
1.猜字游戏
核心内容是:一个是随机种子,确保每次的数字不一样。另一个是go的输入操作,这节课用的bufio这个库来实现输入,也可以使用fmt.Scanf()进行输入,类似于c语言里面的scanf
var guess int
fmt.Scanf("%d", &guess)
随机种子
rand.Seed(time.Now().UnixNano())
2.简单抓包实验
学到了什么是JSON序列化以及反序列化,前者是将go的数据转化为JSON类型,后者则是将JSON类型转化为在go中定义的类型。 以及defer关键字,它的意义就是在函数结束之前执行derfer后面的语句。
3.socks5
这个东西之前没接触过,对我来说还是比较新颖的。通过老师的讲解,在我理解看来,简单来说,socks5服务器类似于一个中介,将client要发送的东西经过socks5服务器转发给server。这里在进行curl时,要到cmd里面进行。
这个图也很好说明socks5运行的大致过程,首先是client与socks5服务器建立链接
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)
}
if ver != socks5Ver {
return fmt.Errorf("not supported ver:%w", err)
}
methodSize, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read methodSize err:%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
}
第二步socks5服务器与server建立tcp链接,这需要解析出来server端的ip以及端口号,根据协议的字段,一段一段的进行解析
buf := make([]byte, 4)
_, err = io.ReadFull(reader, buf)
if err != nil {
return fmt.Errorf("read header failed:%w", err)
}
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", ver)
}
addr := ""
switch atyp {
case atypIPV4:
_, 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])
case atypeHost:
hostsize, err := reader.ReadByte()
if err != nil {
return fmt.Errorf("read hostsize failed")
}
host := make([]byte, hostsize)
_, err = io.ReadFull(reader, host)
if err != nil {
return fmt.Errorf("read host failed:%w", err)
}
addr = string(host)
case atypeIPV6:
return errors.New("IPV6 is not supported yet")
default:
return errors.New("invaild atyp")
}
_, err = io.ReadFull(reader, buf[:2])
if err != nil {
return fmt.Errorf("read port failed:%w", err)
}
port := binary.BigEndian.Uint16(buf[:2])
dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port))
第三步就是client通过socks5服务器向server发送数据
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
_, _ = io.Copy(dest, reader)
cancel()
}()
go func() {
_, _ = io.Copy(conn, dest)
cancel()
}()
<-ctx.Done()
这里使用context.WithCancel(context.Background())来防止还没有发送或接受完数据函数就结束了,从而导致了发送数据的失败。