官网地址 https://github.com/panjf2000/gnet
这里要吐槽一下,官网没有任何使用文档,也没有example,源码test都么有。。。。
客户端
package main
import (
"encoding/binary"
"io"
"log"
"net"
)
// 封包函数
func packageData(data []byte) ([]byte, error) {
length := len(data)
lengthBytes := make([]byte, 4)
binary.BigEndian.PutUint32(lengthBytes, uint32(length))
return append(lengthBytes, data...), nil
}
// 解包函数
func unpackageData(reader io.Reader) ([]byte, error) {
// 读取长度前缀
lengthBytes := make([]byte, 4)
_, err := io.ReadFull(reader, lengthBytes)
if err != nil {
return nil, err
}
// 将长度前缀转换为整数
length := binary.BigEndian.Uint32(lengthBytes)
// 读取数据
data := make([]byte, length)
_, err = io.ReadFull(reader, data)
if err != nil {
return nil, err
}
return data, nil
}
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal("连接失败:", err)
}
defer conn.Close()
// 要发送的数据
data := []byte("Hello, World!")
// 封包
packet, err := packageData(data)
if err != nil {
log.Fatal("封包失败:", err)
}
// 发送封包后的数据
_, err = conn.Write(packet)
if err != nil {
log.Fatal("发送失败:", err)
}
// 接收响应
response, err := unpackageData(conn)
if err != nil {
log.Fatal("解包失败:", err)
}
// 打印服务器响应
log.Println("服务器响应:", string(response))
}
服务端
package main
import (
"encoding/binary"
"fmt"
"github.com/panjf2000/gnet/v2"
"io"
"log"
"net"
"time"
)
func init() {
fmt.Println("测试加载多少次")
}
var maps = make(map[net.Addr]struct{})
// TODO 假设我们的前缀都是4个字节
// 解包函数,从字节流中提取数据
func unpackageData(reader io.Reader) ([]byte, error) {
// 读取长度前缀
lengthBytes := make([]byte, 4)
_, err := io.ReadFull(reader, lengthBytes)
if err != nil {
return nil, err
}
// 将长度前缀转换为整数
length := binary.BigEndian.Uint32(lengthBytes)
// 读取数据
data := make([]byte, length)
_, err = io.ReadFull(reader, data)
if err != nil {
return nil, err
}
return data, nil
}
// 封包函数,将数据和其长度序列化成字节流
func packageData(data []byte) ([]byte, error) {
// 计算数据长度
length := len(data)
// 将长度转换为字节
lengthBytes := make([]byte, 4)
binary.BigEndian.PutUint32(lengthBytes, uint32(length))
// 拼接长度前缀和数据
packet := append(lengthBytes, data...)
return packet, nil
}
type myhandler struct{}
// OnBoot在引擎准备好接受连接时触发。 参数引擎包含信息和各种实用程序
func (c *myhandler) OnBoot(eng gnet.Engine) (action gnet.Action) {
log.Println("程序启动")
return
}
// 打开新连接时触发OnOpen。
func (c *myhandler) OnOpen(conn gnet.Conn) (out []byte, action gnet.Action) {
log.Println("新连接触发,在线人数", len(maps))
maps[conn.RemoteAddr()] = struct{}{}
return
}
// 关闭时触发
func (c *myhandler) OnClose(conn gnet.Conn, err error) (action gnet.Action) {
log.Println("连接关闭")
return
}
// OnTraffic在套接字接收到远程数据时触发。
func (c *myhandler) OnTraffic(conn gnet.Conn) (action gnet.Action) {
log.Println("接收到消息了")
//解包
data, err := unpackageData(conn)
if err != nil {
if err == io.EOF {
log.Println("连接关闭")
return gnet.Close
}
log.Println("解包错误")
return
}
fmt.Println(string(data))
//回复消息,封包
data, err = packageData([]byte("hello"))
if err != nil {
log.Println("封包错误")
return
}
//异步发消息,可以加回调
conn.AsyncWrite(data, nil)
//conn.AsyncWrite(data, func(conn gnet.Conn, err error) error {
// if err != nil {
// log.Println("发送消息错误")
// return err
// }
// return nil
//})
return
}
// OnTick定时器
func (c *myhandler) OnTick() (delay time.Duration, action gnet.Action) {
log.Println("定时器")
delay = 5 * time.Second // 每5秒触发一次
return
}
// OnShutdown在引擎关闭时触发,它在关闭后被调用
func (c *myhandler) OnShutdown(eng gnet.Engine) {
log.Println("关机了")
}
//启动时可选参数
/*
// Multicore表示引擎是否会被有效地创建为多核,如果是,
//那么你必须注意在所有事件回调之间同步内存,否则
//它将以单线程运行引擎。引擎中的线程数将自动计算
//指定当前进程可用的逻辑cpu的值。
Multicore bool
//设置NumEventLoop以启动给定数量的事件循环程序。
//注意:设置NumEventLoop将覆盖多核。
NumEventLoop int
// LB表示分配新连接时使用的负载均衡算法。
LB loadbalance
// ReuseAddr是否设置SO_REUSEADDR套接字选项。
ReuseAddr bool
// ReusePort是否设置SO_REUSEPORT套接字选项
ReusePort bool
// MulticastInterfaceIndex是组播UDP地址绑定的接口名称索引。
MulticastInterfaceIndex int
// ReadBufferCap是可读事件发生时可以从远程读取的最大字节数。
//默认值是64KB,它可以减少以避免饿死后续连接或增加
//从套接字读取更多数据。
//
//注意ReadBufferCap将始终被转换为大于的两个整数值的最小次幂
//或等于其实际金额。
ReadBufferCap int
// WriteBufferCap是静态出站缓冲区可以容纳的最大字节数,
//如果数据超过这个值,溢出的数据将被存储在弹性链表缓冲区中。
//默认为64KB。
//
//注意,WriteBufferCap总是会被转换为两个大于的整数的最小次幂
//或等于其实际金额。
WriteBufferCap int
// LockOSThread用于确定每个I/O事件循环是否与一个操作系统线程相关联,它在您
//需要某种机制,如线程本地存储,或调用某些C库(如图形库:GLib)
//需要通过go进行线程级操作,或者希望所有I/O事件循环实际上并行运行
//潜在的更高性能。
LockOSThread bool
// Ticker指示Ticker是否已设置。
股票行情自动收录器bool
// TCPKeepAlive设置(SO_KEEPALIVE)套接字选项的持续时间
TCPKeepAlive时间。持续时间
// TCPNoDelay控制操作系统是否应该延迟
//数据包传输希望发送更少的数据包(Nagle的算法)。
//
//默认为true(无延迟),表示发送数据
//写入操作完成后尽快返回。
TCPNoDelay TCPSocketOpt
// SocketRecvBuffer以字节为单位设置最大socket接收缓冲区。
SocketRecvBuffer int
// SocketSendBuffer以字节为单位设置最大socket发送缓冲区。
SocketSendBuffer int
// LogPath将写入日志的本地路径,这是设置日志记录的最简单方法;
// gnet使用这个给定的日志路径实例化一个默认的uber-go/zap日志记录器,你也可以使用
//通过实现以下日志,在生命周期内拥有日志记录器。日志界面。
//
//注意,这个选项可以被选项Logger覆盖。
LogPath字符串
// LogLevel表示日志级别,它应该与LogPath一起使用。
LogLevel日志记录。水平
// Logger是用于记录信息的自定义记录器,如果没有设置,
//那么gnet将使用由go.uber.org/zap提供支持的默认记录器。
日志记录。日志记录器
// EdgeTriggeredIO为底层epoll/kqueue事件循环启用边缘触发I/O。
//除非你100%确定你在做什么,否则不要启用它。
//注意,这个选项只对面向流的协议有效。
EdgeTriggeredIO bool
*/
func main() {
my := new(myhandler)
//WithNumEventLoop 可以用cpu核数
err := gnet.Run(my, "tcp://0.0.0.0:8080",
gnet.WithTicker(false),
gnet.WithMulticore(false),
gnet.WithNumEventLoop(8),
gnet.WithReusePort(true),
gnet.WithReuseAddr(true))
fmt.Println(err)
}