从0到1开发go-tcp框架【1-搭建server、封装连接与业务绑定、实现基础Router、抽取全局配置文件】
从0到1开发go-tcp框架【1-搭建server、封装连接与业务绑定、实现基础Router】
本期主要完成对Server的搭建、封装连接与业务绑定、实现基础Router(处理业务的部分)、抽取框架的全局配置文件
- 从配置文件中读取数据(服务器监听端口、监听IP等),通过自定义Router完成具体业务操作
第一版最终项目结构:

1 搭建基础server[V1.0]
1.1 编写server端
- 编写iserver.go,用于定义server的接口
- 编写server.go,定义server结构体,并实现接口
①/zinx/ziface/iserver.go:
package zifacetype IServer interface {Start()Stop()Serve()
}
②/zinx/znet/server.go
package znetimport ("fmt""net"
)type Server struct {Name stringIPVersion stringIP stringPort int
}func NewServer(name string) *Server {s := &Server{Name: name,IPVersion: "tcp4",IP: "0.0.0.0",Port: 8090,}return s
}func (s *Server) Start() {//启动服务监听端口fmt.Printf("[start] Server listener at IP:%s, Port %d is starting\n", s.IP, s.Port)go func() {addr, err := net.ResolveTCPAddr(s.IPVersion, fmt.Sprintf("%s:%d", s.IP, s.Port))if err != nil {fmt.Printf("resolve tcp addr error %v\n", err)return}listener, err := net.ListenTCP(s.IPVersion, addr)if err != nil {fmt.Println("listen ", s.IPVersion, " err ", err)return}fmt.Println("[start] Zinx server success ", s.Name, "Listening...")//阻塞连接,处理业务for {conn, err := listener.AcceptTCP()if err != nil {fmt.Println("Accept err ", err)continue}//处理业务:回显消息go func() {for {buf := make([]byte, 512)cnt, err := conn.Read(buf)if err != nil {fmt.Println("read buf err ", err)continue}fmt.Printf("receive client buf %s, cnt %d \n", buf, cnt)//回显读取到的字节数if _, err := conn.Write(buf[:cnt]); err != nil {fmt.Println("write buf err ", err)continue}}}()}}()
}func (s *Server) Stop() {}func (s *Server) Serve() {s.Start()//阻塞,一直读取客户端所发送过来的消息select {}
}
1.2 测试server端功能
①创建Server.go和Client.go
- 编写myDemo/zinxV1.0/Server.go
package mainimport "myTest/zinx/znet"func main() {s := znet.NewServer("[Zinx v1.0]")s.Serve()
}
- 编写myDemo/zinxV1.0/Client.go
package mainimport ("fmt""net""time"
)/*
模拟客户端
*/
func main() {fmt.Println("client start...")time.Sleep(time.Second * 1)//1 创建服务器连接conn, err := net.Dial("tcp", "127.0.0.1:8090")if err != nil {fmt.Println("client start err ", err)return}for {//2 调用连接向服务器发数据_, err := conn.Write([]byte("Hello Zinx v0.1"))if err != nil {fmt.Println("write conn err ", err)return}// 3 读取服务器返回的数据buf := make([]byte, 512)cnt, err := conn.Read(buf)if err != nil {fmt.Println("client read buf err ", err)return}fmt.Printf("server call back:%s, cnt=%d\n", buf, cnt)//cpu阻塞,让出cpu时间片,避免无限for循环导致其他程序无法获取cpu时间片time.Sleep(time.Second * 1)}
}
②测试结果

可以看到每隔1秒服务器就从客户端接受到数据并回显
2 封装连接conn、业务绑定[V2.0]
V0.1版本我们已经实现了了⼀一个基础的Server框架,现在我们需要对客户端链接和不不同的客户端链接所处 理理的不不同业务再做⼀一层接⼝口封装,当然我们先是把架构搭建起来。
现在在 ziface 下创建⼀一个属于链接的接⼝口⽂文件 iconnection.go ,当然他的实现⽂文件我们放在 znet 下的 connection.go 中。
需要的方法:
- 启动连接
- 停止连接
- 得到连接的conn对象
- 得到连接的id
- 得到客户端连接的地址和端口
- 发送数据的方法
- 连接所绑定的处理业务的函数
2.1 封装Conn
- 定义iconnection接口
- 创建connection结构体并实现iconnection
- 创建/zinx/ziface/iconnection.go:
package zifaceimport "net"type IConnection interface {//启动连接Start()//停止连接Stop()//获取当前连接的Conn对象GetTCPConnection() *net.TCPConn//获取当前连接模块的idGetConnectionID() uint32//获取远程客户端的TCP状态 IP:PortRemoteAddr() net.Addr//发送数据Send()
}//定义一个处理连接业务的方法
type HandleFunc func(*net.TCPConn, []byte, int) error
- 创建/zinx/znet/connection.go
package znetimport ("fmt""myTest/zinx/ziface""net"
)type Connection struct {Conn *net.TCPConnConnID uint32isClosed boolhandleAPI ziface.HandleFunc//告知当前的连接已经退出ExitChan chan bool
}func NewConnection(conn *net.TCPConn, connID uint32, callback_api ziface.HandleFunc) *Connection {c := &Connection{Conn: conn,ConnID: connID,handleAPI: callback_api,isClosed: false,ExitChan: make(chan bool, 1),}return c
}func (c *Connection) StartReader() {fmt.Println("reader goroutine is running...")defer fmt.Println("connID=", c.ConnID, "Reader is exit, remote addr is ", c.RemoteAddr().String())defer c.Stop()//读取数据for {buf := make([]byte, 512)cnt, err := c.Conn.Read(buf)if err != nil {fmt.Printf("connID %d receive buf err %s\n", c.ConnID, err)continue}//调用当前所绑定的处理业务的方法HandleAPIif err := c.handleAPI(c.Conn, buf, cnt); err != nil {fmt.Println("ConnID", c.ConnID, " handle is err ", err)break}}
}//启动连接
func (c *Connection) Start() {fmt.Printf("ConnID %d is Start...", c.ConnID)go c.StartReader()
}//停止连接
func (c *Connection) Stop() {fmt.Println("Connection Stop()...ConnectionID = ", c.ConnID)if c.isClosed {return}c.isClosed = truec.Conn.Close()close(c.ExitChan)
}//获取当前连接的Conn对象
func (c *Connection) GetTCPConnection() *net.TCPConn {return c.Conn
}//获取当前连接模块的id
func (c *Connection) GetConnectionID() uint32 {return c.ConnID
}//获取远程客户端的TCP状态 IP:Port
func (c *Connection) RemoteAddr() net.Addr {return c.Conn.RemoteAddr()
}//发送数据
func (c *Connection) Send() {}
2.2 修改server.go(通过封装的conn实现处理业务)
将修改server.go,添加CallBackToClient方法,用于实现具体业务
将ZinxV1.0版本中的server.go的处理业务逻辑部分更换为封装后的Conn来调用

全部代码:
/zinx/znet/server.go:
package znetimport ("fmt""github.com/kataras/iris/v12/x/errors""net"
)type Server struct {Name stringIPVersion stringIP stringPort int
}func NewServer(name string) *Server {s := &Server{Name: name,IPVersion: "tcp4",IP: "0.0.0.0",Port: 8090,}return s
}//定义当前客户端连接所绑定的handleAPI(暂时写死处理业务逻辑:数据回显)
func CallBackToClient(conn *net.TCPConn, data []byte, cnt int) error {fmt.Println("[Conn handle] CallBackToClient....")if _, err := conn.Write(data[:cnt]); err != nil {fmt.Println("write buf err ", err)return errors.New("CallBackToClient error")}return nil
}func (s *Server) Start() {//启动服务监听端口fmt.Printf("[start] Server listener at IP:%s, Port %d is starting\n", s.IP, s.Port)go func() {addr, err := net.ResolveTCPAddr(s.IPVersion, fmt.Sprintf("%s:%d", s.IP, s.Port))if err != nil {fmt.Printf("resolve tcp addr error %v\n", err)return}listener, err := net.ListenTCP(s.IPVersion, addr)if err != nil {fmt.Println("listen ", s.IPVersion, " err ", err)return}fmt.Println("[start] Zinx server success ", s.Name, "Listening...")//阻塞连接,处理业务for {conn, err := listener.AcceptTCP()if err != nil {fmt.Println("Accept err ", err)continue}var cid uint32 = 0dealConn := NewConnection(conn, cid, CallBackToClient)cid++//开启goroutine处理启动当前conngo dealConn.Start()处理业务:回显消息//go func() {// for {// buf := make([]byte, 512)// cnt, err := conn.Read(buf)// if err != nil {// fmt.Println("read buf err ", err)// continue// }// fmt.Printf("receive client buf %s, cnt %d \n", buf, cnt)// //回显读取到的字节数// if _, err := conn.Write(buf[:cnt]); err != nil {// fmt.Println("write buf err ", err)// continue// }// }////}()}}()
}func (s *Server) Stop() {}func (s *Server) Serve() {s.Start()//阻塞,一直读取客户端所发送过来的消息select {}
}
2.3 测试ZinxV2.0功能
①修改Server.go和Client.go的日志打印
创建/myDemo/ZinxV2.0/Client.go和/myDemo/ZinxV2.0/Server.go,这部分测试代码和V1.0没有区别,将打印日志换成Zinx2.0即可
- Client.go
package mainimport ("fmt""net""time"
)/*
模拟客户端
*/
func main() {fmt.Println("client start...")time.Sleep(time.Second * 1)//1 创建服务器连接conn, err := net.Dial("tcp", "127.0.0.1:8090")if err != nil {fmt.Println("client start err ", err)return}for {//2 调用连接向服务器发数据_, err := conn.Write([]byte("Hello Zinx v0.2"))if err != nil {fmt.Println("write conn err ", err)return}// 3 读取服务器返回的数据buf := make([]byte, 512)cnt, err := conn.Read(buf)if err != nil {fmt.Println("client read buf err ", err)return}fmt.Printf("server call back:%s, cnt=%d\n", buf, cnt)//cpu阻塞,让出cpu时间片,避免无限for循环导致其他程序无法获取cpu时间片time.Sleep(time.Second * 1)}
}
- Server.go
package mainimport "myTest/zinx/znet"func main() {s := znet.NewServer("[Zinx v2.0]")s.Serve()
}
②测试结果

3 实现基础Router[V3.0]
3.1 Request请求封装
将连接和数据绑定在一起
zinx/ziface/irequest.go:
package zifaceimport "net"type IRequest interface {GetConnection() *net.TCPConnGetData() []byte
}
zinx/znet/request.go:
package znetimport "net"type Request struct {conn *net.TCPConndata []byte
}func (r *Request) GetConnection() *net.TCPConn {return r.conn
}func (r *Request) GetData() []byte {return r.data
}
3.2 Router模块
zinx/ziface/irouter.go
package zifacetype IRouter interface {//处理请求之前的方法PreHandle(request IRequest)Handler(request IRequest)//处理请求之后的方法PostHandler(request IRequest)
}
zinx/znet/router.go
package znetimport "myTest/zinx/ziface"type BaseRouter struct {
}//这里做了空实现,直接让后续Router继承BaseRouter,然后根据需要重写对应方法即可
func (br *BaseRouter) PreHandle(request ziface.IRequest) {}
func (br *BaseRouter) Handler(request ziface.IRequest) {}func (br *BaseRouter) PostHandler(request ziface.IRequest) {}
3.3 框架集成router模块
- 取消znet/server.go中的HandlerFunc模块,改为Router。server.go中添加Router属性
- 将znet/connection.go中的callback_api ziface.HandleFunc参数改为Router
zinx/znet/connection.go
package znetimport ("fmt""myTest/zinx/ziface""net"
)type Connection struct {Conn *net.TCPConnConnID uint32isClosed bool//告知当前的连接已经退出ExitChan chan boolRouter ziface.IRouter
}func NewConnection(conn *net.TCPConn, connID uint32, router ziface.IRouter) *Connection {c := &Connection{Conn: conn,ConnID: connID,Router: router,isClosed: false,ExitChan: make(chan bool, 1),}return c
}func (c *Connection) StartReader() {fmt.Println("reader goroutine is running...")defer fmt.Println("connID=", c.ConnID, "Reader is exit, remote addr is ", c.RemoteAddr().String())defer c.Stop()//读取数据for {buf := make([]byte, 512)_, err := c.Conn.Read(buf)if err != nil {fmt.Printf("connID %d receive buf err %s\n", c.ConnID, err)continue}//封装请求,改为router处理r := Request{conn: c.Conn,data: buf,}go func(request ziface.IRequest) {c.Router.PreHandle(request)c.Router.Handler(request)c.Router.PostHandler(request)}(&r)}
}//启动连接
func (c *Connection) Start() {fmt.Printf("ConnID %d is Start...", c.ConnID)go c.StartReader()
}//停止连接
func (c *Connection) Stop() {fmt.Println("Connection Stop()...ConnectionID = ", c.ConnID)if c.isClosed {return}c.isClosed = truec.Conn.Close()close(c.ExitChan)
}//获取当前连接的Conn对象
func (c *Connection) GetTCPConnection() *net.TCPConn {return c.Conn
}//获取当前连接模块的id
func (c *Connection) GetConnectionID() uint32 {return c.ConnID
}//获取远程客户端的TCP状态 IP:Port
func (c *Connection) RemoteAddr() net.Addr {return c.Conn.RemoteAddr()
}//发送数据
func (c *Connection) Send() {}
zinx/znet/server.go
package znetimport ("fmt""myTest/zinx/ziface""net"
)type Server struct {Name stringIPVersion stringIP stringPort intRouter ziface.IRouter
}func NewServer(name string) *Server {s := &Server{Name: name,IPVersion: "tcp4",IP: "0.0.0.0",Port: 8090,Router: nil,}return s
}func (s *Server) Start() {//启动服务监听端口fmt.Printf("[start] Server listener at IP:%s, Port %d is starting\n", s.IP, s.Port)go func() {addr, err := net.ResolveTCPAddr(s.IPVersion, fmt.Sprintf("%s:%d", s.IP, s.Port))if err != nil {fmt.Printf("resolve tcp addr error %v\n", err)return}listener, err := net.ListenTCP(s.IPVersion, addr)if err != nil {fmt.Println("listen ", s.IPVersion, " err ", err)return}fmt.Println("[start] Zinx server success ", s.Name, "Listening...")//阻塞连接,处理业务for {conn, err := listener.AcceptTCP()if err != nil {fmt.Println("Accept err ", err)continue}var cid uint32 = 0dealConn := NewConnection(conn, cid, s.Router)cid++//开启goroutine处理启动当前conngo dealConn.Start()}}()
}func (s *Server) Stop() {}func (s *Server) Serve() {s.Start()//阻塞,一直读取客户端所发送过来的消息select {}
}func (s *Server) AddRouter(router ziface.IRouter) {s.Router = router
}
测试框架集成router效果
myDemo/ZinxV3.0/client.go
package mainimport ("fmt""net""time"
)/*
模拟客户端
*/
func main() {fmt.Println("client start...")time.Sleep(time.Second * 1)//1 创建服务器连接conn, err := net.Dial("tcp", "127.0.0.1:8090")if err != nil {fmt.Println("client start err ", err)return}for {//2 调用连接向服务器发数据_, err := conn.Write([]byte("Hello Zinx v0.3"))if err != nil {fmt.Println("write conn err ", err)return}// 3 读取服务器返回的数据buf := make([]byte, 512)cnt, err := conn.Read(buf)if err != nil {fmt.Println("client read buf err ", err)return}fmt.Printf("server call back:%s, cnt=%d\n", buf, cnt)//cpu阻塞,让出cpu时间片,避免无限for循环导致其他程序无法获取cpu时间片time.Sleep(time.Second * 1)}
}
myDemo/ZinxV3.0/server.go
package mainimport ("fmt""myTest/zinx/ziface""myTest/zinx/znet"
)//自定义一个Router,测试路由功能
type PingRouter struct {znet.BaseRouter
}func (pr *PingRouter) PreHandle(request ziface.IRequest) {_, err := request.GetConnection().Write([]byte("pre handle success..."))if err != nil {fmt.Println("server call pre handle err ", err)return}fmt.Println("server call pre handle...")
}func (pr *PingRouter) Handler(request ziface.IRequest) {_, err := request.GetConnection().Write([]byte("handle success..."))if err != nil {fmt.Println("server call handle err ", err)return}fmt.Println("server call handler....")
}func (pr *PingRouter) PostHandler(request ziface.IRequest) {_, err := request.GetConnection().Write([]byte("post handle success..."))if err != nil {fmt.Println("server call post handle err ", err)return}fmt.Println("server call post handler...")
}func main() {s := znet.NewServer("[Zinx v3.0]")//添加自定义路由router := &PingRouter{}s.AddRouter(router)s.Serve()
}
最终效果:

按照模板方法设计模式,完成了调用
4 抽取全局配置文件[V4.0]
4.1 编写/zinx/util/globalobj.go
主要用于读取zinx配置文件的信息
package utilimport ("encoding/json""io/ioutil""myTest/zinx/ziface"
)type GlobalObj struct {TCPServer ziface.IServer //当前全局Zinx的server对象Host string //当前服务器主机监听的ipTcpPort int //当前服务器主机监听的端口号Name string //当前服务器的名称Version string //当前Zinx的版本号MaxConn int //当前服务器所允许的最大连接数MaxPackageSize uint32 //当前Zinx框架数据包的最大值
}var GlobalObject *GlobalObj//从配置文件中重新加载GlobalObject的信息
func (g *GlobalObj) Reload() {data, err := ioutil.ReadFile("conf/zinx.json")if err != nil {panic(err)}//将json文件数据解析到struct中err = json.Unmarshal(data, &GlobalObject)if err != nil {panic(err)}
}//在其他文件导入该util包的时候会加载init
func init() {GlobalObject = &GlobalObj{Name: "ZinxServerApp",Version: "V0.4",TcpPort: 8090,Host: "0.0.0.0",MaxConn: 120,MaxPackageSize: 4096,}//尝试从conf/zinx.json中去加载用户自定义的参数GlobalObject.Reload()
}
4.2 替换之前server.go中的硬编码
包括/zinx/znet/server.go和/zinx/znet/connection.go部分
- server:
- connection:
4.3 测试
编写myDemo/ZinxV4.0
- 并且编写对应的.json配置文件(Client.go与Server.go都与V3.0一样)

zinx.json
{"Name": "Zinx Server Application","Version": "V0.4","Host": "0.0.0.0","TcpPort": 8091,"MaxConn": 30,"MaxPackageSize": 1024
}
最后效果:

参考:https://www.yuque.com/aceld/npyr8s/bgftov
相关文章:
从0到1开发go-tcp框架【1-搭建server、封装连接与业务绑定、实现基础Router、抽取全局配置文件】
从0到1开发go-tcp框架【1-搭建server、封装连接与业务绑定、实现基础Router】 本期主要完成对Server的搭建、封装连接与业务绑定、实现基础Router(处理业务的部分)、抽取框架的全局配置文件 从配置文件中读取数据(服务器监听端口、监听IP等&a…...
建设银行秋招指南,备考技巧和考试内容详解
建设银行秋招简介 银行作为非常吃香的岗位,每年都有不少同学通过投递简历,进入笔试,再到面试成功,成功到银行就职,也有相当一部分同学因为信息差,符合条件却没有报名。无法进入银行工作。 建设银行的秋招…...
Cilium 系列-7-Cilium 的 NodePort 实现从 SNAT 改为 DSR
系列文章 Cilium 系列文章 前言 将 Kubernetes 的 CNI 从其他组件切换为 Cilium, 已经可以有效地提升网络的性能。但是通过对 Cilium 不同模式的切换/功能的启用,可以进一步提升 Cilium 的网络性能。具体调优项包括不限于: 启用本地路由 (Native Rou…...
React的hooks---useReducer
useReducer 作为 useState 的代替方案,在某些场景下使用更加适合,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。 使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为父组件可以向自…...
自然语言处理从入门到应用——LangChain:模型(Models)-[文本嵌入模型Ⅱ]
分类目录:《自然语言处理从入门到应用》总目录 本文将介绍如何在LangChain中使用Embedding类。Embedding类是一种与嵌入交互的类。有很多嵌入提供商,如:OpenAI、Cohere、Hugging Face等,这个类旨在为所有这些提供一个标准接口。 …...
Olap BI工具对比
背景 目前公司主要使用数据存储有MySQL、ES、Hive、HBase、TiDB等 MySQL用于存储应用的基本支撑数据,数据量少;ES和Hbase用于存储和查询调用记录,数据量多;Hive和TiDB用于DC上使用,数据量多。主要使用的数据分析平台…...
【iOS】Cocoapods的安装以及使用
文章目录 前言一、Cocoapods的作用二、安装Cocoapods三、使用Cocoapods总结 前言 最近笔者在仿写天气预报App时用到了api调用数据,一般的基本数据类型我们用Xcode中自带的框架就可以转换得到。但是在和风天气api中的图标的格式为svg格式。 似乎iOS13之后Xcode中可…...
OpenCvSharp (C# OpenCV) 二维码畸变矫正--基于透视变换(附源码)
导读 本文主要介绍如何使用OpenCvSharp中的透视变换来实现二维码的畸变矫正。 由于CSDN文章中贴二维码会导致显示失败,大家可以直接点下面链接查看图片: C# OpenCV实现二维码畸变矫正--基于透视变换 (详细步骤 + 代码) 实现步骤 讲解实现步骤之前先看下效果(左边是原图,右边…...
下级平台级联视频汇聚融合平台EasyCVR,层级显示不正确的原因排查
视频汇聚平台安防监控EasyCVR可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等,以及厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等,能对外分发RTSP、RTMP、FLV、HLS、WebRTC等…...
Android程序CPU使用大的异常分析
程序出现CPU使用过高的问题,如果能够重现,就比较好办了,可以top命令查看各线程的cpu使用,定位到线程。 以下是问国内某AI的答案 在Android应用中,如果某个应用消耗了大量的CPU资源,可以采取以下方法进行分…...
[数学建模] 0、关于数学建模的一点看法付费专栏食用说明
文章目录 1、前言2、数学建模学习索引2.1、建模知识点 3、实战建模论文索引3.1、国赛真题索引3.1.1、[数学建模] [2001年国赛模拟] 1. 血管的三维重建3.1.2、[数学建模] [2011年B国赛模拟] 2. 交巡警服务平台的设置与调度3.1.3、[数学建模][2012年A国赛模拟] 3. 葡萄酒的评价 3…...
2.oracle数据库自增主键
不同于mysql,oracle主键自增不能在建表时直接设置,其实也很简单 1.建表 CREATE TABLE test(id NUMBER NOT NULL,key1 VARCHAR2(40) NULL,key2 VARCHAR2(40) NULL);2.设置主键 alter table test add constraint test_pk primary key (id);3.新建序列tes…...
算法通关村第二关——链表加法的问题解析
题目类型 链表反转、栈 题目描述 * 题目: * 给你两个非空链表来表示两个非负整数,数字最高位位于链表的开始位置。 * 它们的每个节点都只存储一个数字。将这两个数相加会返回一个新的链表。 * 你可以假设除了数字0外,这两个数字都不会以0开头…...
mapboxGL中楼层与室内地图的结合展示
概述 质量不够,数量来凑,没错,本文就是来凑数的。前面的几篇文章实现了楼栋与楼层单体化的展示、室内地图的展示,本文结合前面的几篇文章,做一个综合的展示效果。 实现效果 实现 1. 数据处理 要实现上图所示的效果…...
使用Anaconda3创建pytorch虚拟环境
一、Conda配置Pytorch环境 1.conda安装Pytorch环境 打开Anaconda Prompt,输入命令行: conda create -n pytorch python3.6 输入y,再回车。 稍等,便完成了Pytorch的环境安装。我们可以利用以下命令激活pytorch环境。 conda…...
QT 常用数据结构整理
目录 QString篇 QString篇 //初始化bool bOk false;QString str "sd";QString strTemp(str);str QString("%1,%2").arg("11").arg("-gg");qDebug()<<str;str.sprintf("%s %d","ni",1);qDebug()<<…...
Fiddler使用教程|渗透测试工具使用方法Fiddler
提示:如有问题可联系我,24小时在线 文章目录 前言一、Fiddler界面介绍二、菜单栏1.菜单Fiddler工具栏介绍Fiddler命令行工具详解 前言 网络渗透测试工具: Fiddler是目前最常用的http抓包工具之一。 Fiddler是功能非常强大,是web…...
网站密码忘记了怎么办?chrome浏览器,谷歌浏览器。
有时候忘记了网站的密码,又不想“忘记密码”去一番折腾。如果你正好用的是 chrome 浏览器。 那么根本就没必要折腾,直接就能看到网站密码。 操作如下 1.在浏览器右上角点击三个小点: 2.点这三个点: 3.选择“显示密码”&#x…...
23款奔驰GLS450加装原厂香氛负离子系统,清香宜人,久闻不腻
奔驰原厂香氛合理性可通过车内空气调节组件营造芳香四溢的怡人氛围。通过更换手套箱内香氛喷雾发生器所用的香水瓶,可轻松选择其他香氛。香氛的浓度和持续时间可调。淡雅的香氛缓缓喷出,并且在关闭后能够立刻散去。车内气味不会永久改变,香氛…...
流数据湖平台Apache Paimon(一)概述
文章目录 第1章 概述1.1 简介1.2 核心特性1.3 基本概念1.3.1 Snapshot1.3.2 Partition1.3.3 Bucket1.3.4 Consistency Guarantees一致性保证 1.4 文件布局1.4.1 Snapshot Files1.4.2 Manifest Files1.4.3 Data Files1.4.4 LSM Trees 第1章 概述 1.1 简介 Flink 社区希望能够将…...
intv_ai_mk11效果惊艳案例:为初创公司1小时生成完整BP商业计划书框架
intv_ai_mk11效果惊艳案例:为初创公司1小时生成完整BP商业计划书框架 1. 商业计划书生成效果展示 1.1 从零到完整的商业计划书 intv_ai_mk11在商业计划书生成方面展现出惊人的效率和质量。我们实测了一个真实案例:一家智能硬件初创公司需要准备融资用…...
微信小程序物流信息对接实战:发货接口的完整实现指南
1. 微信小程序物流对接的核心价值 对于电商类小程序来说,物流信息同步是用户体验的关键环节。当用户下单后,最关心的就是"我的包裹到哪了"。传统做法需要用户手动复制单号到第三方平台查询,而通过微信官方物流接口,可以…...
Arduino_ConnectionHandler库:嵌入式网络连接状态管理与自适应重连
1. Arduino_ConnectionHandler 库深度解析:嵌入式网络连接管理的工程实践指南1.1 库定位与核心价值Arduino_ConnectionHandler是 Arduino 官方生态中面向物联网终端设备的网络连接抽象管理层,其设计目标并非替代底层通信协议栈(如 WiFiClient…...
实战指南:在快马平台用trae构建电商购物车状态管理系统
今天想和大家分享一个实战项目:用trae在电商场景下构建购物车状态管理系统。这个方案特别适合需要清晰数据流的中小型项目,比如电商平台、管理后台等。下面我会详细拆解整个实现过程,希望能给有类似需求的同学一些参考。 项目结构设计 首先…...
Scarab:重构空洞骑士模组管理体验的技术实践
Scarab:重构空洞骑士模组管理体验的技术实践 【免费下载链接】Scarab An installer for Hollow Knight mods written in Avalonia. 项目地址: https://gitcode.com/gh_mirrors/sc/Scarab 问题溯源:模组管理的隐性成本与技术瓶颈 量化手动管理的效…...
Graphormer图神经网络效果展示:含手性中心/立体异构体分子的预测能力验证
Graphormer图神经网络效果展示:含手性中心/立体异构体分子的预测能力验证 1. 模型概述 Graphormer是一种基于纯Transformer架构的图神经网络,专门为分子图(原子-键结构)的全局结构建模与属性预测而设计。该模型在OGB(…...
如何为华硕笔记本安装轻量级性能控制工具:G-Helper完整指南
如何为华硕笔记本安装轻量级性能控制工具:G-Helper完整指南 【免费下载链接】g-helper Lightweight, open-source control tool for ASUS laptops and ROG Ally. Manage performance modes, fans, GPU, battery, and RGB lighting across Zephyrus, Flow, TUF, Stri…...
Qwen3.5-2B效果展示:对含中英混排、公式符号的PDF截图进行精准语义还原
Qwen3.5-2B效果展示:对含中英混排、公式符号的PDF截图进行精准语义还原 1. 模型概览 Qwen3.5-2B是通义千问团队推出的轻量化多模态基础模型,属于Qwen3.5系列的小参数版本(20亿参数)。这个模型主打低功耗、低门槛部署特性&#x…...
忍者像素绘卷镜像免配置:一键切换‘天界画坊’/‘木叶村’双主题UI
忍者像素绘卷镜像免配置:一键切换天界画坊/木叶村双主题UI 1. 产品概述 忍者像素绘卷是一款专为像素艺术创作者设计的图像生成工作站,基于Z-Image-Turbo深度优化引擎开发。这款工具将传统忍者文化与现代AI技术完美结合,创造出独特的16-Bit复…...
cv_resnet101_face-detection_cvpr22papermogface 模型部署的网络安全考量:防范403 Forbidden等常见攻击
cv_resnet101_face-detection_cvpr22papermogface 模型部署的网络安全考量:防范403 Forbidden等常见攻击 把一个人脸检测模型,比如 cv_resnet101_face-detection_cvpr22papermogface,部署成一个Web API,这事儿听起来挺酷的。想象…...






