杂项|青训营笔记

79 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 13 天

指针

空指针是nil

Go中的指针作用极其有限,限于修改传入参数(也是值传递)

func fun(a *int) {

*a += 1

}

fun(&num)

结构体

type user struct {

    name     string

    password string

}

a := user{name: "wang", password: "1024"}

b := user{"wang", "1024"}

默认初始化为0或空

也通过.访问成员

 

成员函数

func (u user) check (password string) bool {

    return u.password == password

}

func (u *user) check (password string) bool {///

}

错误处理

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 connect(reader *bufio.Reader, conn net.Conn) (err error) {

    // +----+-----+-------+------+----------+----------+

    // |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个字节

 

    buf := make([]byte, 4) //前面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 {  //判断目标类型是ipv4还是ipv6还是域名

    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:%w", err)

       }

       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: no supported yet")

    default:

       return errors.New("invalid 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))    //和目标服务器建立tcp

    if err != nil {

       return fmt.Errorf("dial dst failed:%w", err)

    }

    defer dest.Close()

    log.Println("dial", addr, port)

    // +----+-----+-------+------+----------+----------+

    // |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

    _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})

    if err != nil {

       return fmt.Errorf("write failed: %w", err)

    }

 

    //代理服务器双向转发目标服务器和客户端之间数据

    //带cancel等待,cancel表完成。context是帮助同步的结构体(编程模式)

    ctx, cancel := context.WithCancel(context.Background())

    //cancel可以多次调用

    defer cancel()

 

    go func() { //这里是参数列表

       _, _ = io.Copy(dest, reader)

       cancel()

    }()    //这里是go的调用参数列表,类似c++的lambda

    go func() {

       _, _ = io.Copy(conn, dest)

       cancel()

    }()

    //当ctx完成时才返回

    <-ctx.Done()

    return nil

}