这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天
由于作者原来有一些go语言基础,故跳过第一节基础语法讲解,直接进行第二节实战案例的演示
go猜谜游戏
这是作者在课前阅读题目后写出的go代码
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
guessNumber:=rand.Intn(100)
var guess int
for{
fmt.Scan(&guess)
if guess==guessNumber{
fmt.Println("you are right")
break
}else if guess>guessNumber{
fmt.Println("to big")
continue
}else if guess<guessNumber{
fmt.Println("to small")
continue
}
}
return
}
但是代码随机生成的随机数每一次都是81,这完全违背了整个猜谜游戏的初衷。我们可以通过添加一个随机数种子来解决这个问题
rand.Seed(time.Now().UnixNano())
现在看一下运行效果
35
to big
33
to small
34
you are right
可以看到运行结果,完全符合我们的预期。
同时通过和课程代码相比
reader := bufio. NewReader(os.Stdin)
input, err := reader .ReadString("\n')
input = strings. TrimSuffix(input, "In")
课程中使用了一个bufio的包来接收用户输入,是由于后面的课会使用bufio这个包,所以没有使用scanf来进行接收用户输入。
在线词典
这个项目通过使用http包来对彩云小译的词典接口进行post获取response,通过使用json包对req的序列化以及对resp的反序列化获取正常的json数据通过中断显示出来
这个项目使用了两个根据curd数据自动生成代码以及根据json自动生成struct的网站
通过json包对数据进行序列化后发送
request:=DictRequest{TransType: "en2zh", Source: word}
buf,err:=json.Marshal(request)
通过json包对数据进行反序列化后输出
var dictResponse DictResponse
err=json.Unmarshal(bodyText, &dictResponse)
运行示例
liulongxin@liulongxindeAir go_guess_number % go run main.go good
good UK: [gud] US: [gʊd]
a.好的;善良的;快乐的;真正的;宽大的;有益的;老练的;幸福的;忠实的;优秀的;完整的;彻底的;丰富的n.利益;好处;善良;好人ad.=well%
SOCKS5
代理auth
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:%v", ver)
}
methodSize, err := reader. ReadByte()
if err != nil {
return fmt. Errorf("read methodSize failed:›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
}
通过reader.ReadByte()这个函数把传递进来的*bufio.Reader接收后将ver,methodSize和method解析出来
请求阶段
func connect(reader *bufio.Reader,conn net.Conn) (err error) {
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 supperted ver:%v",ver)
}
if cmd!=cmdBuild{
return fmt.Errorf("not supperted cmd:%v",ver)
}
addr:=""
switch atyp {
case atypeIPV4:
_,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 atype")
}
_,err=io.ReadFull(reader,buf[:2])
if err != nil {
return fmt.Errorf("read port failed:%w",err)
}
port:=binary.LittleEndian.Uint16(buf[:2])
log.Println("dial",addr,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)
}
return nil
}
relay阶段
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()
ctx,cancel:=context.WithCancel(context.Background())
defer cancel()
go func() {
_,_=io.Copy(dest,reader)
cancel()
}()
go func() {
_,_=io.Copy(conn,dest)
cancel()
}()
<-ctx.Done()
relay阶段使用了一个context包来进行goroutine与主线程的同步,在不使用context的情况下函数执行不会等待goroutine执行结束就会直接返回nil,但是当引入context包后ctx,cancel:=context.WithCancel(context.Background())及<-ctx.Done()机制可以让主线程等待goroutine执行结束后<-ctx.Done()停止阻塞,函数继续执行。