gnet--高性能的网络库

官网地址 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)
}

开启多线程之后

posted @   朝阳1  阅读(257)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示