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的日常的主要内容,如果未能解决你的问题,请参考以下文章