go之路(第一天)| 青训营笔记

69 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 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里面进行。

image.png 这个图也很好说明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())来防止还没有发送或接受完数据函数就结束了,从而导致了发送数据的失败。