func (self *CommandIO) ReadCommand() (cmd *Command, err error) {
	var cmdLen uint16
	err = binary.Read(self.conn, binary.LittleEndian, &cmdLen)
	if err != nil {
		return
	}
	data := make([]byte, int(cmdLen))
	mac, err := self.readThenHmac(data)
	if err != nil {
		return
	}
	err = self.readAndCmpHmac(mac)
	if err != nil {
		return
	}
	cmd, err = self.decodeCommand(data)
	if cmd.Type == CMD_BYE {
		err = io.EOF
		return
	}
	return
}
ReadCommand() is not goroutine-safe.
package proto
import (
	"code.google.com/p/snappy-go/snappy"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/binary"
	"hash"
	"io"
	"sync"
)
type CommandIO struct {
	writeAuth   hash.Hash
	cryptWriter io.Writer
	readAuth    hash.Hash
	cryptReader io.Reader
	conn        io.ReadWriter
	writeLock *sync.Mutex
}
func (self *CommandIO) writeThenHmac(data []byte) (mac []byte, err error) {
	writer := self.cryptWriter
	self.writeAuth.Reset()
	var datalen uint16
	datalen = uint16(len(data))
	err = binary.Write(self.writeAuth, binary.LittleEndian, datalen)
	if err != nil {
		return
	}
	err = writen(writer, data)
	if err != nil {
		return
	}
	mac = self.writeAuth.Sum(nil)
	return
}
func (self *CommandIO) readThenHmac(data []byte) (mac []byte, err error) {
	reader := self.cryptReader
	self.readAuth.Reset()
	var datalen uint16
	datalen = uint16(len(data))
	err = binary.Write(self.readAuth, binary.LittleEndian, datalen)
	if err != nil {
		return
	}
	n, err := io.ReadFull(reader, data)
	if err != nil {
		return
	}
	if n != len(data) {
		err = io.EOF
		return
	}
	mac = self.readAuth.Sum(nil)
	return
}
func (self *CommandIO) writeHmac(mac []byte) error {
	if len(mac) == 0 {
		return nil
	}
	return writen(self.conn, mac)
}
func (self *CommandIO) readAndCmpHmac(mac []byte) error {
	if len(mac) == 0 {
		return nil
	}
	macRecved := make([]byte, self.readAuth.Size())
	n, err := io.ReadFull(self.conn, macRecved)
	if err != nil {
		return err
	}
	if n != len(macRecved) {
		return ErrCorruptedData
	}
	if !xorBytesEq(mac, macRecved) {
		return ErrCorruptedData
	}
	return nil
}
func (self *CommandIO) decodeCommand(data []byte) (cmd *Command, err error) {
	compress := ((data[0] & cmdflag_COMPRESS) != 0)
	var npadding int
	npadding = int(data[0] >> 3)
	data = data[1 : len(data)-npadding]
	decoded := data
	if compress {
		decoded, err = snappy.Decode(nil, data)
		if err != nil {
			return
		}
	}
	cmd, err = UnmarshalCommand(decoded)
	if err != nil {
		return
	}
	return
}
func (self *CommandIO) encodeCommand(cmd *Command, compress bool) (data []byte, err error) {
	bsonEncoded, err := cmd.Marshal()
	if err != nil {
		return
	}
	data = bsonEncoded
	if compress {
		data, err = snappy.Encode(nil, bsonEncoded)
		if err != nil {
			return
		}
	}
	var flag byte
	if compress {
		flag |= cmdflag_COMPRESS
	}
	nrBlk := (len(data) + blkLen) / blkLen
	npadding := (nrBlk * blkLen) - (len(data) + 1)
	flag |= byte((npadding & 0xFF) << 3)
	data = append(data, 0)
	copy(data[1:], data[:len(data)-1])
	data[0] = flag
	data = append(data, make([]byte, npadding)...)
	return
}
func (self *CommandIO) WriteCommand(cmd *Command, compress bool) error {
	data, err := self.encodeCommand(cmd, compress)
	if err != nil {
		return err
	}
	var cmdLen uint16
	cmdLen = uint16(len(data))
	if cmdLen == 0 {
		return nil
	}
	self.writeLock.Lock()
	defer self.writeLock.Unlock()
	err = binary.Write(self.conn, binary.LittleEndian, cmdLen)
	if err != nil {
		return err
	}
	mac, err := self.writeThenHmac(data)
	if err != nil {
		return err
	}
	err = self.writeHmac(mac)
	if err != nil {
		return err
	}
	return nil
}