Socket
大约 6 分钟
Socket
介绍
- 2022/10/25
自定义协议
- 2023/2/16
// 定义一个自定义协议的结构体,包含消息的长度、类型和内容
type MyProtocol struct {
Length int // 消息的长度,用4个字节表示
Type int // 消息的类型,用4个字节表示,比如1表示文本,2表示图片,3表示音频等
Data []byte // 消息的内容,用字节切片表示,长度由Length决定
}
// 定义一个编码函数,将自定义协议的结构体转换为字节切片,用于发送数据
func Encode(mp *MyProtocol) []byte {
// 创建一个缓冲区,用于存储编码后的数据
buf := bytes.NewBuffer([]byte{})
// 使用encoding/binary包中的Write函数,按照大端字节序,将结构体中的字段写入缓冲区
binary.Write(buf, binary.BigEndian, mp.Length)
binary.Write(buf, binary.BigEndian, mp.Type)
binary.Write(buf, binary.BigEndian, mp.Data)
// 返回缓冲区中的字节切片
return buf.Bytes()
}
// 定义一个解码函数,将字节切片转换为自定义协议的结构体,用于接收数据
func Decode(data []byte) *MyProtocol {
// 创建一个缓冲区,用于存储解码后的数据
buf := bytes.NewBuffer(data)
// 创建一个自定义协议的结构体,用于存储解码后的字段
mp := &MyProtocol{}
// 使用encoding/binary包中的Read函数,按照大端字节序,从缓冲区中读取字段到结构体中
binary.Read(buf, binary.BigEndian, &mp.Length)
binary.Read(buf, binary.BigEndian, &mp.Type)
// 根据Length的值,创建一个字节切片,用于存储Data字段
mp.Data = make([]byte, mp.Length)
binary.Read(buf, binary.BigEndian, &mp.Data)
// 返回解码后的结构体
return mp
}
使用示例
/ 服务端的示例代码
package main
import (
"fmt"
"log"
"net"
)
// 定义一个处理连接的函数,接收一个net.TCPConn类型的参数
func handleConn(conn *net.TCPConn) {
// 延迟关闭连接
defer conn.Close()
// 创建一个缓冲区,用于存储接收到的数据
buf := make([]byte, 1024)
// 循环读取数据
for {
// 从连接中读取数据,返回读取的字节数和错误
n, err := conn.Read(buf)
// 如果错误不为空,打印错误并退出循环
if err != nil {
log.Println(err)
break
}
// 如果读取的字节数为0,说明连接已经关闭,退出循环
if n == 0 {
log.Println("connection closed")
break
}
// 打印接收到的数据
fmt.Println("received data:", string(buf[:n]))
// 调用解码函数,将字节切片转换为自定义协议的结构体
mp := Decode(buf[:n])
// 打印解码后的结构体
fmt.Printf("decoded data: %+v\n", mp)
// 根据消息的类型,做不同的处理
switch mp.Type {
case 1: // 文本消息
// 回复一个文本消息,内容为"Hello"
reply := &MyProtocol{
Length: 5,
Type: 1,
Data: []byte("Hello"),
}
// 调用编码函数,将自定义协议的结构体转换为字节切片
data := Encode(reply)
// 将字节切片写入连接,返回写入的字节数和错误
n, err = conn.Write(data)
// 如果错误不为空,打印错误并退出循环
if err != nil {
log.Println(err)
break
}
// 打印写入的字节数
fmt.Println("sent bytes:", n)
case 2: // 图片消息
// 回复一个图片消息,内容为一张图片的字节切片
reply := &MyProtocol{
Length: len(imageBytes),
Type: 2,
Data: imageBytes,
}
// 调用编码函数,将自定义协议的结构体转换为字节切片
data := Encode(reply)
// 将字节切片写入连接,返回写入的字节数和错误
n, err = conn.Write(data)
// 如果错误不为空,打印错误并退出循环
if err != nil {
log.Println(err)
break
}
// 打印写入的字节数
fmt.Println("sent bytes:", n)
case 3: // 音频消息
// 回复一个音频消息,内容为一段音频的字节切片
reply := &MyProtocol{
Length: len(audioBytes),
Type: 3,
Data: audioBytes,
}
// 调用编码函数,将自定义协议的结构体转换为字节切片
data := Encode(reply)
// 将字节切片写入连接,返回写入的字节数和错误
n, err = conn.Write(data)
// 如果错误不为空,打印错误并退出循环
if err != nil {
log.Println(err)
break
}
// 打印写入的字节数
fmt.Println("sent bytes:", n)
default: // 未知消息
// 回复一个文本消息,内容为"Unknown"
reply := &MyProtocol{
Length: 7,
Type: 1
Data: []byte("Unknown"),
}
// 调用编码函数,将自定义协议的结构体转换为字节切片
data := Encode(reply)
// 将字节切片写入连接,返回写入的字节数和错误
n, err = conn.Write(data)
// 如果错误不为空,打印错误并退出循环
if err != nil {
log.Println(err)
break
}
// 打印写入的字节数
fmt.Println("sent bytes:", n)
}
}
}
func main() {
// 定义一个服务端的地址,包含IP和端口
addr := &net.TCPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: 8080,
}
// 使用net包中的ListenTCP函数,监听服务端的地址,返回一个net.TCPListener类型的对象和错误
listener, err := net.ListenTCP("tcp", addr)
// 如果错误不为空,打印错误并退出程序
if err != nil {
log.Fatal(err)
}
// 延迟关闭监听器
defer listener.Close()
// 打印服务端的地址
fmt.Println("server listening on:", addr)
// 循环接收客户端的请求
for {
// 使用net.TCPListener的AcceptTCP方法,接收客户端的连接,返回一个net.TCPConn类型的对象和错误
conn, err := listener.AcceptTCP()
// 如果错误不为空,打印错误并继续循环
if err != nil {
log.Println(err)
continue
}
// 打印客户端的地址
fmt.Println("client connected from:", conn.RemoteAddr())
// 创建一个goroutine,调用处理连接的函数,传入连接对象作为参数
go handleConn(conn)
}
}
简单协议
代码
// MyProtocol 定义一个自定义协议的结构体,包含消息的长度、类型和内容
type MyProtocol struct {
Length int32 // 消息的长度,用4个字节表示
Data []byte // 消息的内容,用字节切片表示,长度由Length决定
}
func (p *MyProtocol) SetData(data []byte) {
p.Length = int32(len(data))
p.Data = data
}
// Encode 定义一个编码函数,将自定义协议的结构体转换为字节切片,用于发送数据
func Encode(mp *MyProtocol) []byte {
// 创建一个缓冲区,用于存储编码后的数据
buf := bytes.NewBuffer([]byte{})
// 使用encoding/binary包中的Write函数,按照大端字节序,将结构体中的字段写入缓冲区
_ = binary.Write(buf, binary.BigEndian, mp.Length)
_ = binary.Write(buf, binary.BigEndian, mp.Data)
// 返回缓冲区中的字节切片
return buf.Bytes()
}
// Decode 定义一个解码函数,将字节切片转换为自定义协议的结构体,用于接收数据
func Decode(data []byte) *MyProtocol {
// 创建一个缓冲区,用于存储解码后的数据
buf := bytes.NewBuffer(data)
// 创建一个自定义协议的结构体,用于存储解码后的字段
mp := &MyProtocol{}
// 使用encoding/binary包中的Read函数,按照大端字节序,从缓冲区中读取字段到结构体中
_ = binary.Read(buf, binary.BigEndian, &mp.Length)
// 根据Length的值,创建一个字节切片,用于存储Data字段
mp.Data = make([]byte, mp.Length)
_ = binary.Read(buf, binary.BigEndian, &mp.Data)
// 返回解码后的结构体
return mp
}
func TestMyProtocol(t *testing.T) {
m := MyProtocol{}
m.SetData([]byte("HelloWorld"))
encode := Encode(&m)
result := Decode(encode)
fmt.Println(string(result.Data)) // HelloWorld
}