Go的日常

Posted 右大臣

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go的日常相关的知识,希望对你有一定的参考价值。

书接上回

Go中常用的包:

bufio包原理

bufio 是通过缓冲来提高效率。

io操作本身的效率并不低,低的是频繁的访问本地磁盘的文件。所以bufio就提供了缓冲区(分配一块内存),读和写都先在缓冲区中,最后再读写文件,来降低访问本地磁盘的次数,从而提高效率。

简单的说就是,把文件读取进缓冲(内存)之后再读取的时候就可以避免文件系统的io 从而提高速度。同理,在进行写操作时,先把文件写入缓冲(内存),然后由缓冲写入文件系统。看完以上解释有人可能会表示困惑了,直接把 内容->文件 和 内容->缓冲->文件相比, 缓冲区好像没有起到作用嘛。其实缓冲区的设计是为了存储多次的写入,最后一口气把缓冲区内容写入文件。 bufio 封装了io.Reader或io.Writer接口对象,并创建另一个也实现了该接口的对象。

io.Reader或io.Writer 接口实现read() 和 write() 方法,对于实现这个接口的对象都是可以使用这两个方法的。

Reader对象

bufio.Reader 是bufio中对io.Reader 的封装

后面会用到

实现猜数字游戏:

package main

import (
    "fmt"
    "math/rand"
	"time"
	"os"
	"strconv"
	"strings"
	"bufio"
)

func main() 
	maxnum:=100
    rand.Seed(time.Now().UnixNano())//时间戳
	sect:=rand.Intn(maxnum)//随机数

	
	fmt.Println("please input your guess:")
	reader:=bufio.NewReader(os.Stdin)//向屏幕输入

	for
		input,err:=reader.ReadString('\\n')
		if err!=nil
        fmt.Println("an err please try again",err)
        continue
	
	input=strings.TrimSuffix(input,"\\r\\n")
    //fmt.Println(input)
	guess,err:=strconv.Atoi(input)
	if err!=nil
		fmt.Println("err please try again66666")
        continue
    
	fmt.Println("you guess is",guess)
	if guess>sect
        fmt.Println("you guess is big")
	else if guess<sect
		fmt.Println("you guess is small")
	else 
		fmt.Println("you guess is right")
		break
    



SockS5代理

socket5是什么?

socket5 是一种代理协议,实际上是一个传输层代理协议,比http协议底层。

SOCKS5 是一个代理协议,它在使用TCP/IP协议通讯的前端机器和服务器机器之间扮演一个中介角色,使得内部网中的前端机器变得能够访问Internet网中的服务器,或者使通讯更加安全。SOCKS5 服务器通过将前端发来的请求转发给真正的目标服务器, 模拟了一个前端的行为。在这里,前端和SOCKS5之间也是通过TCP/IP协议进行通讯,前端将原本要发送给真正服务器的请求发送给SOCKS5服务器,然后SOCKS5服务器将请求转发给真正的服务器。

SOCKS5服务器在将通讯请求发送给真正服务器的过程中,对于请求数据包本身不加任何改变。SOCKS5服务器接收到真正服务器的响应后,也原样转发给前端。因此,SOCKS5 协议是一种代理协议,对于各种基于 TCP/IP的应用层协议都能够适应,几乎是万能的。它虽然不能理解自己转发的数据的内部结构,但是它能够忠实地转发通讯包,完成协议本来要完成的功能。

实现一个socks5代理

package main

import (
	"bufio"
	"context"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
)

const socks5Ver = 0x05
const cmdBind = 0x01
const atypIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04

func main() 
	server, err := net.Listen("tcp", "127.0.0.1:1080")
	if err != nil 
		panic(err)
	
	for 
		client, err := server.Accept()
		if err != nil 
			log.Printf("Accept failed %v", err)
			continue
		
		go process(client)
	


func process(conn net.Conn) 
	defer conn.Close()
	reader := bufio.NewReader(conn)
	err := auth(reader, conn)
	if err != nil 
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	
	err = connect(reader, conn)
	if err != nil 
		log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
		return
	


func auth(reader *bufio.Reader, conn net.Conn) (err error) 
	// +----+----------+----------+
	// |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

	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)
	

	// +----+--------+
	// |VER | METHOD |
	// +----+--------+
	// | 1  |   1    |
	// +----+--------+
	_, err = conn.Write([]bytesocks5Ver, 0x00)
	if err != nil 
		return fmt.Errorf("write failed:%w", err)
	
	return nil


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)
	_, 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:%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))
	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([]byte0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0)
	if err != nil 
		return fmt.Errorf("write failed: %w", err)
	
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func() 
		_, _ = io.Copy(dest, reader)
		cancel()
	()
	go func() 
		_, _ = io.Copy(conn, dest)
		cancel()
	()

	<-ctx.Done()
	return nil

[日常] Go语言圣经--复数,布尔值,字符串习题

go语言圣经-复数

1.我们把形如a+bi(a,b均为实数)的数称为复数,其中a称为实部,b称为虚部,i称为虚数单位。两种精度的复数类型:complex64和complex128,分别对应float32和float64两种浮点数精度

2.complex函数用于构建复数,real和imag函数分别返回复数的实部和虚部

 

go语言圣经-布尔型

1.布尔值可以和&&(AND)和||(OR)操作符结合,并且有短路行为

2.&&的优先级比||高

 

go语言圣经-字符串

1.一个字符串是一个不可改变的字节序列,文本字符串通常被解释为采用UTF8编码的Unicode码点(rune)序列

2.内置的len函数可以返回一个字符串中的字节数目,不是字符数目,对于非ASCII字符的UTF8编码会要两个或多个字节

3.+操作符将两个字符串链接构造一个新字符串

4.字符串值也可以用字符串面值方式编写,只要将一系列字节序列包含在双引号即可

5.原生的字符串面值形式是`...`,使用反引号代替双引号用于编写正则表达式,HTML模板、JSON面值、命令行提示信息会很方便

6.UTF8是一个将Unicode码点编码为字节序列的变长编码,Go语言的源文件采用UTF8编码,并且Go语言处理UTF8编码的文本也很出色,Go语言的range循环在处理字符串的时候,会自动隐式解码UTF8字符串

7.每个符号都分配一个唯一的Unicode码点,Unicode码点对应Go语言中的rune整数类型(译注:rune是int32等价类型)。小于256码点值可以写在一个十六进制转义字节中,例如\\x41对应字符\'A\',更大的码点则必须使用\\u或\\U转义形式

 

7.utf8.RuneCountInString(s)函数 统计字符个数

8.四个包对字符串处理尤为重要:bytes、strings、strconv和unicode包

9.将一个整数转为字符串,一种方法是用fmt.Sprintf返回一个格式化的字符串;另一个方法是用strconv.Itoa(“整数到ASCII”):

10.字符串转换成整数 strconv.ParseInt  strconv.ParseFloat

 

练习 3.10: 编写一个非递归版本的comma函数,使用bytes.Buffer代替字符串链接操作。

 

练习 3.11: 完善comma函数,以支持浮点数处理和一个可选的正负号的处理。

 

练习 3.12: 编写一个函数,判断两个字符串是否是是相互打乱的,也就是说它们有着相同的字符,但是对应不同的顺序。

package main

import(
        "fmt"
        "strings"
        "bytes"
)

func main(){
        fmt.Println(comma(-5123456.23))
        fmt.Println(compare("abec","ecab"))
}
/*
练习 3.10: 编写一个非递归版本的comma函数,使用bytes.Buffer代替字符串链接操作。

练习 3.11: 完善comma函数,以支持浮点数处理和一个可选的正负号的处理。
*/
func comma(str float64)string{
        //整型转换成字符串
        s := fmt.Sprintf("%.2f",str)
        //取出小数点后面部分
        var end string
        if dot := strings.LastIndex(s, "."); dot >= 0 {
                end = s[dot:]
                s = s[:dot]
        }
        num := len(s)
        var buf bytes.Buffer
        j := 1
        for i:=num-1;i>=0;i--{
                buf.WriteByte(s[i])
                if j%3==0 && i!=0{
                        buf.WriteString(",")
                }
                j++
        }
        res:=buf.String()
        var r bytes.Buffer
        //反转字符串
        for i:=len(res)-1;i>=0;i--{
                r.WriteByte(res[i])
        }
        r.WriteString(end)
        return r.String()
}
//练习 3.12: 编写一个函数,判断两个字符串是否是是相互打乱的,也就是说它们有着相同的字符,但是对应不同的顺序。
func compare(str1 string,str2 string)bool{
        //比较两个字符串的长度,外层循环是较长的
        num1:=strings.Count(str1,"")
        num2:=strings.Count(str2,"")
        if num2 > num1{
                str1,str2=str2,str1
        }
        var res bool
        for _,v :=range str1{
                res = false
                for _,sv :=range str2{
                        if v== sv{
                                res =true
                        }
                }
                if !res{
                        break
                }
        }
        return res
}

  

以上是关于Go的日常的主要内容,如果未能解决你的问题,请参考以下文章

知识分享之Golang——go-i18n国际化组件

Go语言TCP Socket编程

[上海][部门直招] UCloud 招聘 Golang 后端工程师 15薪

Go 后端主要做什么

go(golang)中的类型转换

golang环境变量的设置