go:封装io.Reader 实现灵活可扩展的io程序 在go中有一个非常重要的接口`io.Reader`。可以在自己的对象中实现这个接口,使得第三方库可以兼容`Read(buf)`操作。 通常,需要更进一步,包装base Reader接口,实现自己的逻辑。**这样可以屏蔽底层的io操作**,最大化的提供兼容性。 这里以实现一个RC4加密器为例,主要展示的是Reader的封装用法,而不是加密。 最终,它**可以传入任意Reader作为数据源用来加密**,同时它自己也是一个Reader,可以应用在任意其它兼容的地方作为一个中间件。(思考一下,如何基于http包和它做一个完全RC4加密通讯的框架) ```go package rc4 import ( "io" ) type RC4Reader struct { baseReader io.Reader // 特别要注意这个baseReader,这里屏蔽了底层的操作,专注于加密这个用户逻辑 password string sbox []byte i, j int } func NewReader(password string, reader io.Reader) *RC4Reader { key := []byte(password) keylength := len(key) r := new(RC4Reader) r.password = password r.baseReader = reader sbox := make([]byte, 256) for i := 0; i <= 255; i++ { sbox[i] = byte(i) } j := 0 for i := 0; i < 256; i++ { j = (j + int(sbox[i]) + int(key[i%keylength])) % 256 sbox[i], sbox[j] = sbox[j], sbox[i] } r.sbox = sbox return r } func (r *RC4Reader) Read(p []byte) (n int, err error) { tp := make([]byte, len(p), len(p)) n, err = r.baseReader.Read(tp) // 从baseReader读取数据,基于对接口的信任,这一定能取到需要加密的数据,而不需要管这个数据是从string还是文件或者网络流中来。这就是接口的妙用。 // 同时,处理baseReader的错误,进行封装即可 if err != nil { return n, err } tp = tp[:n] // 下面是加密编码操作,不需要理解 ret := make([]byte, n, n) for i := 0; i < n; i++ { r.i = (r.i + 1) % 256 //a r.j = (r.j + int(r.sbox[r.i])) % 256 //b r.sbox[r.i], r.sbox[r.j] = r.sbox[r.j], r.sbox[r.i] ret[i] = tp[i] ^ r.sbox[(int(r.sbox[r.i])+int(r.sbox[r.j]))%256] } copy(p, ret) return n, nil } ``` 因为封装了baseReader,这个加密器就能兼容所有的io.Reader的实现。而不需要去管这个reader是来源于何方。 调用: ```go func main() { // 使用strings.Reader作为数据源,直接加密string的数据 r := rc4.NewReader("yzh", strings.NewReader("yzhyzhyzhyzhyzhyzhyzhyzhyzhyzhyzhyzh")) var enc bytes.Buffer for { ret := make([]byte, 4, 4) n, err := r.Read(ret) if err != nil { break } enc.Write(ret[:n]) } fmt.Println("encrypt is", enc.Bytes()) // 使用bytes.Reader作为数据源,解码bytes数据 rdec := rc4.NewReader("yzh", bytes.NewReader(enc.Bytes())) var dec bytes.Buffer for { ret := make([]byte, 4, 4) n, err := rdec.Read(ret) if err != nil { break } dec.Write(ret[:n]) } fmt.Println("decrypt is", dec.Bytes(), dec.String()) } ``` 因为baseReader的存在,加/解密的数据源可以是任何实现了io.Reader的结构。包括strings,bytes甚至网络流,这对用户来说是极其灵活方便的。也统一了go的代码规范。岂不美哉。 来自 大脸猪 写于 2020-01-14 15:01 -- 更新于2021-01-11 13:29 -- 0 条评论