- 使用encoding/gob 实现消息的编解码(序列化与反序列化)
- 实现一个简易的服务端,仅接受消息,不处理
- 待学习
- 工厂模式
- TCP协议
type Header struct{
ServiceMethod string // 服务名和方法名,通常与go语言中的结构体和方法相映射
Seq uint64 // 请求的序号,也可以认为是某个请求的ID,用来区分不同的请求
Error string // 错误信息,客户端置为空,服务端如果发送错误,将错误信息置于Error中
}
type Codec interface{
io.Closer
ReadHeader(*Header) error
ReadBody(interface{}) error
Write(*Header, interface{}) error
}
type GobCodec struct{
conn io.ReadWriteCloser // 由构建函数传入,通常是通过TCP或者Unix建立socket时得到的链接实例
buf *bufio.Writer // 防止阻塞而创建的带缓冲的Writer,一般这么做能提升性能
dec *gob.Decoder // Decoder解码
enc *gob.Encoder // Encoder编码
}
var _ Codec = (*GobCodec)(nil) // 确保GobCodec实现了Codec接口
func NewGobCodec(conn io.ReadWriteCloser) Codec{
buf := bufio.NewWriter(conn)
return &GobCodec{
conn: conn,
buf: buf,
dec: gob.NewDecoder(conn),
enc: gob.NewEncoder(buf),
}
}
type Type string
const(
GobType Type = "application/gob"
MagicNumber = 0x3bef5c
)
type Option struct{
MagicNumber int
CodecType codec.GobType
}
var DefaultOption = &Option{
MagicNumber: MagicNumber,
CodecType: codec.GobType,
}
type Server struct{}
func NewServer() *Server{
return &Server
}
var DefaultServer = NewServer() // 默认的Server实例,目的是方便用户使用
func (server *Server) Accept(lis net.Listener){
// 等待socket连接建立
for {
conn, err := lis.Accept()
if err != nil{
log.Println("rpc server: accept error: ", err)
return
}
// 开启子协程处理
go server.ServeConn(conn)
}
}
func (server *Server) ServeConn(conn io.ReadWriteCloser){
defer func(){_ = conn.Close()}()
var opt Option
if err := json.NewDecoder(conn).Decode(&opt); err != nil{
log.Println("rpc server: options error:", err)
return
}
if opt.MagicNumber != MagicNumber{
log.Printf("rpc server: invalid magic number %x", opt.MagicNumber)
return
}
f := codec.NewCodecFuncMap[opt.CodecType]
if f == nil{
log.Printf("rpc server: invalid codec type %s", opt.CodecType)
return
}
server.serveCodec(f(conn))
}
var invalidRequest = struct{}{}
func (server *Server) serveCodec(cc codec.Codec){
sending := new(sync.Mutex)
wg := new(sync.WaitGroup)
for {
// 读取请求
req, err := server.readRequest(cc)
if err != nil{
if req == nil{
break
}
req.h.Error = err.Error()
// 回复请求
server.sendResponse(cc, req.h, invalidRequest, sending)
continue
}
// 处理请求,协程并发执行请求
// 回复请求的报文必须是逐个发送的,容易导致多个回复报文交织在一起,客户端无法解析,所以使用锁(sending)保证
wg.Add(1)
go server.handleRequest(cc, req, sending, wg)
}
}
func Accept(lis net.Listener){DefaultServer.Accept(lis)}
// 使用
lis, _ := net.Listen("tcp", ":9999")
Accept(lis)
func process(conn net.Conn){
defer conn.Close()
reader := bufio.NewReader(conn)
var buf [1024]byte
for {
n, err := reader.Read(buf[:])
if err == io.EOF{
break
}
if err != nil{
fmt.Println("read from client failed, err: ", err)
break
}
recvStr := string(buf[:n])
fmt.Println("收到client发来的数据:", recvStr)
}
}
func main(){
listen, err := net.Listen("tcp", "127.0.0.1:30000")
if err != nil{
fmt.Println("listen failed, err: ", err)
return
}
defer listen.Close()
for{
conn, err := listen.Accept()
if err != nil{
fmt.Println("accept failed, err: ", err)
continue
}
go process(conn)
}
}
func main(){
conn, err := net.Dial("tcp", "127.0.0.1:30000")
if err != nil{
fmt.Println("dial failed, err: ", err)
return
}
defer conn.Close()
for i := 0; i < 20; i++{
msg := `Hello, Hello, how ard you?`
conn.Write([]byte(msg))
}
}
proto.go
// Encode将消息编码
func Encode(message string)([]byte, error){
// 读取消息的长度,转换成int32类型(占4个字节)
var length = int32(len(message))
var pkg = new(bytes.Buffer)
// 写入消息头
err := binary.Write(pkg, binart.LittleEndian, length)
if err != nil{
return nil, err
}
// 写入消息实体
err = binary.Write(pkg, binary.LittleEndian, []byte(message))
if err != nil{
return nil, err
}
return pkg.Bytes(), nil
}
// Decode 解码消息
func Decode(reader *bufio.Reader)(string, error){
// 读取消息的长度
lengthByte, _ := reader.Peek(4) // 读取前4个字节的数据
lengthBuff := bytes.NewBuffer(lengthByte)
var length int32
err := binary.Read(lengthBuff, binary.LittleEndian, &length)
if err != nil{
return "", err
}
// Buffered返回缓冲中现有的可读取的字节数
if int32(reader.Buffered()) < length + 4{
return "", err
}
// 读取真正的消息数据
pack := make([]byte, int(4 + length))
_, err = reader.Read(pack)
if err != nil{
return "", err
}
return string(pack[4:]), nil
}
服务端 server.go
func process(conn net.Conn){
defer conn.Close()
reader := bufio.NewReader(conn)
for {
msg, err := proto.Decode(reader)
if err == io.EOF{
return
}
if err != nil{
fmt.Println("decode msg failed, err: ", err)
return
}
fmt.Println("收到client发来的数据: ", msg)
}
}
func main(){
listen, err := net.Listen("tcp", "127.0.0.1:30000")
if err != nil{
fmt.Println("listen failed, err: ", err)
return
}
defer listen.Close()
for {
conn, err := listen.Accept()
if err != nil{
fmt.Println("accept failed, err: ", err)
continue
}
go process(conn)
}
}
客户端 client.go
func main(){
conn, err := net.Dial("tcp", "127.0.0.1:30000")
if err != nil{
fmt.Println("dial failed, err: ", err)
return
}
defer conn.Close()
for i := 0; i < 20; i++{
msg := `Hello, Hello, How are you?`
data, err := proto.Encode(msg)
if err != nil{
fmt.Println("encode msg failed, err: ", err)
return
}
conn.Write(data)
}
}
协议结构体
type protocol struct{
Length uint32 // 内容长度
Content []byte // 内容
}
装包解包
const HEADER_LEN = 4
func Packet(content string) []byte{
buffer := make([]byte, HEADER_LEN + len(content))
// 将buffer前面四个字节设置为包长度,大端序
binary.BigEndian.PutUint32(buffer[0:4], uint32(len(content)))
copy(buffer[4:], content)
return buffer
}
// 解包,先读取4个字节转换成整型,再读包长度字节
func UnPacket(c net.Conn) (*Protocol, error){
var (
p = &Protocol{}
header = make([]byte, HEADER_LEN)
)
_, err := io.ReadFull(c, header)
if err != nil{
return p, err
}
p.Length = binary.BigEndian.Uint32(header) // 转换成10进制的数字
contentByte := make([]byte, p.Length)
_, e := io.ReadFull(c, contentByte) // 读取内容
if e != nil{
return p, e
}
p.Content = contentByte
return p, nil
}
解析包体内容
func (p *Protocol) parseContent() (map[string]interface{}, error){
var object map[string]interface{}
unmarshal := json.Unmarshal(p.Content, &object)
if unmarshal != nil{
return object, unmarshal
}
return object, nil
}
主函数
func main(){
l, err := net.Listen("tcp", ":9527")
if err != nil{
log.Fatal(err)
}
defer l.Close()
for {
conn, err := l.Accept()
if err != nil{
log.Fatal(err)
}
go func(c net.Conn){
protocol, _ := UnPacket(c)
parseContent, err := protocol.parseContent()
if (err != nil){
}
s := parseContent["serviceId"].(string)
cstr := parseContent["data"].(string)
if s == "Hello World"{
fmt.Printf("serviceId: %s, content: %s", s, cstr)
writeByte := []byte(cstr)
c.Write(writeByte)
}
c.Close
}(conn)
}
}
const HEADER_LEN = 4
type Content struct{
ServiceId string `json:"serviceId"`
Data interface{} `json: "data"`
}
func Packet(serviceId string, content string)[]byte{
bytes, _ := json.Marshal(Content{ServiceId: serviceId, Data: content})
buffer := make([]byte, HEADER_LEN + len(bytes))
// 将buffer前面四个字节设置为包长度,大端序
binary.BigEndian.PutUint32(buffer[0:4], uint32(len(bytes)))
copy(buffer[4:], bytes)
return buffer
}
func main(){
conn, e := net.Dial("tcp", ":9527")
if e != nil{
log.Fatal(e)
}
reader := bufio.NewReader(os.Stdin)
fmt.Print("Text to send:")
text, _ := reader.ReadString('\n')
buffer := Packet("Hello World", text)
conn.Write(buffer)
message, _ := bufio.NewReader(conn).ReadString('\n')
fmt.Print("Message from server:" + message)
defer conn.Close()
}