Go 每日一库之 gotalk

golang每日一库——casbin开源的访问控制框架 部分用于request的定义,它明确了函数中参数的含义。表示经典三元组: 访问实体 (Subject),访问资源 (Object) 和访问方法 (Action)。但是, 你可以自定义你自己的请求表单, 如果不需要指定特定资源,则可以这样定义sub、act,或者如果有两个访问实体, 则为。这些是我们对policy规则的具体描述policy部分的每一行称之为一个策略规则, 每条策略规则通常以形如pp2的开头。如果存在多个policy定义,那么我们会根据前文提到的与具体的某条定义匹配。TIP。 阅读详情

简介

gotalk专注于进程间的通信,致力于简化通信协议和流程。同时它:

  • 提供简洁、清晰的 API;

  • 支持 TCP,WebSocket 等协议;

  • 采用非常简单而又高效的传输协议格式,便于抓包调试;

  • 内置了 JavaScript 文件gotalk.js,方便开发基于 Web 网页的客户端程序;

  • 内含丰富的示例可供学习参考。

那么,让我们来玩一下吧~

快速使用

本文代码使用 Go Modules。

创建目录并初始化:

$ mkdir gotalk && cd gotalk
$ go mod init github.com/darjun/go-daily-lib/gotalk

安装gotalk库:

$ go get -u github.com/rsms/gotalk

接下来让我们来编写一个简单的 echo 程序,服务端直接返回收到的客户端信息,不做任何处理。首先是服务端:

// get-started/server/server.go
package main

import (
  "log"

  "github.com/rsms/gotalk"
)

func main() {
  gotalk.Handle("echo", func(in string) (string, error) {
    return in, nil
  })
  if err := gotalk.Serve("tcp", ":8080", nil); err != nil {
    log.Fatal(err)
  }
}

通过gotalk.Handle()注册消息处理,它接受两个参数。第一个参数为消息名,字符串类型,保证唯一且可辨识即可。第二个参数为处理函数,收到对应名称的消息,调用该函数处理。处理函数接受一个参数,返回两个值。正常处理完成通过第一个返回值传递处理结果,出错时通过第二个返回值表示错误类型。

这里的处理器函数比较简单,接受一个字符串参数,直接原样返回。

然后,调用gotalk.Serve()启动服务器,监听端口。它接受 3 个参数,协议类型、监听地址、处理器对象。此处我们使用 TCP 协议,监听本地8080端口,使用默认处理器对象,传入nil即可。

服务器内部一直循环处理请求。

然后是客户端:

func main() {
  s, err := gotalk.Connect("tcp", ":8080")
  if err != nil {
    log.Fatal(err)
  }

  for i := 0; i < 5; i++ {
    var echo string
    if err := s.Request("echo", "hello", &echo); err != nil {
      log.Fatal(err)
    }

    fmt.Println(echo)
  }

  s.Close()
}

客户端首先调用gotalk.Connect()连接服务器,它接受两个参数:协议和地址(IP + 端口)。我们使用与服务器一致的协议和地址即可。连接成功会返回一个连接对象。调用连接对象的Request()方法,即可向服务器发送消息。Request()方法接受 3 个参数。第一个参数为消息名,这对应于服务器注册的消息名,请求一个不存在的消息名会返回错误。第二个参数是传给服务器的参数,有且只能有一个参数,对应处理器函数的入参。第三个参数为返回值的指针,用于接受服务器返回的结果。

如果请求失败,返回错误err。使用完成之后不要忘记关闭连接对象。

先运行服务器:

$ go run server.go

在开启一个命令行,运行客户端:

$ go run client.go
hello
hello
hello
hello
hello

实际上如果了解标准库net/http,你应该就会发现,使用gotalk的服务端代码与使用net/http编写 Web 服务器非常相似。都非常简单,清晰:

// get-started/http/main.go
package main

import (
  "fmt"
  "log"
  "net/http"
)

func index(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintln(w, "hello world")
}

func main() {
  http.HandleFunc("/", index)

  if err := http.ListenAndServe(":8888", nil); err != nil {
    log.Fatal(err)
  }
}

运行:

$ go run main.go

使用 curl 验证:

$ curl localhost:8888
hello world

WebSocket

除了 TCP,gotalk还支持基于 WebSocket 协议的通信。下面我们使用  WebSocket 重写上面的服务端程序,然后编写一个简单 Web 页面与之通信。

服务端:

func main() {
  gotalk.Handle("echo", func(in string) (string, error) {
    return in, nil
  })

  http.Handle("/gotalk/", gotalk.WebSocketHandler())
  http.Handle("/", http.FileServer(http.Dir(".")))
  if err := http.ListenAndServe(":8080", nil); err != nil {
    log.Fatal(err)
  }
}

gotalk消息处理函数的注册还是与前面的一样。不同的是这里将 HTTP 路径/gotalk/的请求交由gotalk.WebSocketHandler()处理,这个处理器负责 WebSocket 请求。同时,在当前工作目录开启一个文件服务器,挂载到 HTTP 路径/上。文件服务器是为了客户端方便地请求index.html页面。最后调用http.ListenAndServe()开启 Web 服务器,监听端口 8080。

然后是客户端,gotalk为了方便 Web 程序的编写,将 WebSocket 通信细节封装在一个 JavaScript 文件gotalk.js中。可以直接从仓库中的 js 目录下获取使用。接着我们编写页面index.html,引入gotalk.js:

<!DOCTYPE HTML>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <script type="text/javascript" src="gotalk/gotalk.js"></script>
  </head>
  <body>
    <input id="txt">
    <button id="snd">send</button><br>
    <script>
    let c = gotalk.connection()
      .on('open', () => log(`connection opened`))
      .on('close', reason => log(`connection closed (reason: ${reason})`))
    let btn = document.querySelector("#snd")
    let txt = document.querySelector("#txt")
    btn.onclick = async () => {
      let content = txt.value
      if (content.length === 0) {
        alert("no message")
        return
      }
      let res = await c.requestp('echo', content)
      log(`reply: ${JSON.stringify(res, null, 2)}`)
      return false
    }
    function log(message) {
      document.body.appendChild(document.createTextNode(message))
      document.body.appendChild(document.createElement("br"))
    }
    </script>
  </body>
</html>

首先调用gotalk.connection()连接服务端,返回一个连接对象。调用此对象的on()方法,分别注册连接建立和断开的回调。然后给按钮添加回调,每次点击将输入框中的内容发送给服务端。调用连接对象的requestp()方法发送请求,第一个参数为消息名,对应在服务端使用gotalk.Handle()注册的名字。第二个即为处理参数,会一并发送给服务端。这里使用 Promise 处理异步请求和响应,为了编写方便和易于理解使用async-await同步的写法。响应的内容直接显示在页面上:

注意,gotalk.js文件需要放在服务器运行目录的gotalk目录下。

协议格式

gotalk采用基于 ASCII 的协议格式,设计为方便人类阅读且灵活的。每条传输的消息都分为几个部分:类型标识、请求ID、操作、消息内容。

  • 类型标识:只用一个字节,用来表示消息的类型,是请求消息还是响应消息,流式消息还是非流式的,错误、心跳和通知也都有其特定的类型标识。

  • 请求 ID:用 4 个字节表示,方便匹配响应。由于gotalk可以同时发送任意个请求并接收之前请求的响应。所以需要有一个 ID 来标识接收到的响应对应之前发送的哪条请求。

  • 操作:即为我们上面定义的消息名,例如"echo"。

  • 消息内容:使用长度 + 实际内容格式。

看一个官方请求的示例:

+------------------ SingleRequest
|   +---------------- requestID   "0001"
|   |      +--------- operation   "echo" (text3Size 4, text3Value "echo")
|   |      |       +- payloadSize 25
|   |      |       |
r0001004echo00000019{"message":"Hello World"}
  • r:表示这是一个单条请求。

  • 0001:请求 ID 为 1,这里采用十六进制编码。

  • 004echo:这部分表示操作为"echo",在实际字符串内容前需要指定长度,否则接收方不知道内容在哪里结束。004指示"echo"长度为 4,同样采用十六进制编码。

  • 00000019{"message":"Hello World"}:这部分是消息的内容。同样需要指定长度,十六进制00000019表示长度为 25。

详细格式可以查看官方文档。

使用这种可阅读的格式给问题排查带来了极大的便利。但是在实际使用中,可能需要考虑安全和隐私的问题。

聊天室

examples内置一个基于 WebSocket 的聊天室示例程序。特性如下:

  • 可以创建房间,默认创建 3 个房间animals/jokes/golang;

  • 在房间聊天(基本功能);

  • 一个简单的 Web 页面。

运行:

$ go run server.go

打开浏览器,输入"localhost:1235",显示如下:

接下来就可以创建房间,在房间聊天了。

整个实现的有几个要点:

其一,gotalk.WebSocketHandler()创建的 WebSocket 处理器可以设置连接回调:

gh := gotalk.WebSocketHandler()
gh.OnConnect = onConnect

在回调中设置随机用户名,并将当前连接的gotalk.Sock存储下来,方便消息广播:

func onConnect(s *gotalk.WebSocket) {
  socksmu.Lock()
  defer socksmu.Unlock()
  socks[s] = 1

  username := randomName()
  s.UserData = username
}

其二,gotalk设置处理器函数可以有两个参数,第一个表示当前连接,第二个才是实际接收到的消息参数。

其三,enableGracefulShutdown()函数实现了 Web 服务器的优雅关闭,非常值得学习。接收到SIGINT信号,先关闭所有的连接,再退出程序。注意监听信号和运行 HTTP 服务器并不是同一个 goroutine,看它们是如何协作的:

func enableGracefulShutdown(server *http.Server, timeout time.Duration) chan struct{} {
  server.RegisterOnShutdown(func() {
    // close all connected sockets
    fmt.Printf("graceful shutdown: closing sockets\n")
    socksmu.RLock()
    defer socksmu.RUnlock()
    for s := range socks {
      s.CloseHandler = nil // avoid deadlock on socksmu (also not needed)
      s.Close()
    }
  })
  done := make(chan struct{})
  quit := make(chan os.Signal, 1)
  signal.Notify(quit, syscall.SIGINT)
  go func() {
    <-quit // wait for signal

    fmt.Printf("graceful shutdown initiated\n")
    ctx, cancel := context.WithTimeout(context.Background(), timeout)
    defer cancel()

    server.SetKeepAlivesEnabled(false)
    if err := server.Shutdown(ctx); err != nil {
      fmt.Printf("server.Shutdown error: %s\n", err)
    }

    fmt.Printf("graceful shutdown complete\n")
    close(done)
  }()
  return done
}

接收到SIGINT信号后done通道关闭,server.ListenAndServe()返回http.ErrServerClosed错误,退出循环:

done := enableGracefulShutdown(server, 5*time.Second)

// Start server
fmt.Printf("Listening on http://%s/\n", server.Addr)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
  panic(err)
}

<- done

整个聊天室功能比较简单,代码也比较短,建议深入理解。在此基础之上做扩展也比较简单。

总结

gotalk实现了一个简单、易用的通信库。并且提供了 JavaScript 文件gotalk.js,方便 Web 程序的开发。协议格式清晰,易调试。内置丰富的示例。整个库的代码也不长,建议深入了解。

大家如果发现好玩、好用的 Go 语言库,欢迎到 Go 每日一库 GitHub 上提交 issue????

参考

  1. gotalk GitHub:https://github.com/rsms/gotalk

  2. Go 每日一库 GitHub:https://github.com/darjun/go-daily-lib

我

我的博客:https://darjun.github.io

欢迎关注我的微信公众号【GoUpUp】,共同学习,一起进步~

Go每日一库之rotatelogs Golang的rotatelogs库是一个用于日志轮转(log rotation)的库。日志轮转是一种常用的日志管理策略,它允许开发者将日志按照一定规则分割成多个文件,以便于管理和分析。通过使用rotatelogs库,开发者可以方便地实现日志文件的自动创建、分割和备份。 阅读详情

相关推荐

[Golang]每日一库-viper

😚一个不甘平凡的普通人,致力于为Golang社区和算法学习做出贡献,期待您的关注和认可,陪您一起学习打卡!!!😘😘😘。

Jing_Hua 632

Golang 每日一库

github 项目地址 https://github.com/darjun/go-daily-lib

An丶的博客 609

go-daily-lib:Go 每日一库

Go 每日一库 每天学习一个 Go 语言库! 相关博客: Go 标准库提供的命令行选项解析库,简单实用。 第三方命令行选项解析库,非常强大。 不使用 cgo 获取用户主目录的第三方库,支持交叉编译。 解析 INI 配置文件的第三方库。 编写命令行程序的库,并提供生成代码的脚手架。 灵活强大的配置管理工具,支持多种 JSON/TOML/YAML 等多种配置格式,支持热更新。 监控文件或目录改动的第三方库。 方便实用的类型转换库。 Go 标准日志库。 使用非常广泛的开源日志库。 读取项目目录中的 .env 文件设置程序的环境变量。支持多种开发模式dev/prod。 功能强大的日期时间库,仿 PHP 的同名库。 发送邮件的第三方库。 Uber 出品的依赖注入库。 Go 操作 JSON 数据的库,支持基本操作和花式操作方法。 message-bus 异步消息通信库。 watermill 功能丰富的

Go每日一库之CSRF库的使用方式和实现原理

CSRF攻击是基于将验证信息存储于cookie中,同时浏览器在发送请求时会自动携带cookie的原理进行的。所以,其预防原理也就是验证请求来源的真实性。csrf包就是利用了token校验的原理,让前后连续的请求签发token、下次请求验证token的方式进行预防的。

yy1715713348的博客 687

搞定安卓11+抓包难题:用Kitsune Mask、magic_overlayfs和LSPosed三件套绕过SSL Pinning(保姆级教程)

本文详细解析了安卓11+系统中抓包面临的核心挑战,包括SSL Pinning和Root检测机制,并提供了使用Kitsune Mask、magic_overlayfs和LSPosed三件套的完整解决方案。通过动态分区技术解析和工具链配置,帮助开发者绕过安全限制,实现高效抓包分析。

weixin_42665255的博客 441

逆向分析美团外卖mtgsig3.0签名算法:从抓包到算法还原实战

在移动应用开发与安全领域,API接口签名是保障数据传输完整性与身份验证的核心机制。其基本原理是通过特定的算法,将请求参数、时间戳等数据与密钥结合,生成唯一的签名串,服务器通过验证签名来识别请求的合法性。这项技术的核心价值在于防止数据篡改、重放攻击和未授权访问,是构建安全客户端-服务器通信的基石。在实际应用中,签名算法广泛应用于电商、社交、金融等各类App的接口保护场景。本文以美团外卖App的mtgsig3.0签名为具体案例,详细拆解了其生成逻辑。通过结合Frida动态调试与Jadx静态分析,文章深入探讨了如

weixin_29325515的博客 1236

Go 每日一库之 resty

简介resty是 Go 语言的一个 HTTP client 库。resty功能强大,特性丰富。它支持几乎所有的 HTTP 方法(GET/POST/PUT/DELETE/OPTION/HEA...

darjun的博客 4514

在树莓派5(8G版)上运行Ollama + LLM

直接上结论,Ollama在树莓派上能安装,且本身运行起来毫无压力,但具体到大语言模型,Deepseek似乎不总是最佳选择。

ki1381的专栏 1648

Go 每日一库-viper

安装go get github.com/spf13/viper功能viper 是用来管理 go 程序的项目配置,是一个完整的解决方案它支持:设置默认值从 JSON、TOML、YAML、HCL、envfile 和 Java 属性配置文件中读取实时观看和重新阅读配置文件(可选)从环境变量中读取从远程配置系统(etcd 或 Consul)读取,并观察变化从命令行标志读取从缓冲区读取设置显式值可以将 Vi...

Anubis 1042

海思MPP平台千兆以太网驱动集成与测试指南

千兆以太网(GE)是嵌入式视觉系统中音视频流传输与边缘控制的核心物理接口,其底层驱动需兼顾DMA高效调度、中断实时响应与PHY链路稳定性。在Linux嵌入式环境中,GE驱动通常基于net_device框架实现,并深度耦合于SoC专用媒体处理平台(如海思MPP),通过硬件抽象层(HAL)屏蔽寄存器操作,提供零拷贝、RGMII时序适配、描述符环管理等关键能力。技术价值体现在降低CPU负载、保障1080p@30fps实时推流、支持ONVIF/RTSP协议交互及固件远程升级。典型应用场景包括网络摄像机(IPC)、A

weixin_42599908的博客 89

分享一个简单的迭代学习机器人双臂控制(MATLAB)

1 什么是迭代学习控制 迭代学习控制(iterative learning control,简称ILC)由Uchiyama于1978年首先提出,不过因为论文由日文撰写,影响不是很大。1984年,Arimoto等人用英文介绍了该方法。它是指不断重复一个同样轨迹的控制尝试,并以此修正控制律,以得到非常好的控制效果的控制方法。 迭代学习控制的特点是“在重复中学习”,通过反复的迭代修正,达到改善控制效果的目的。迭代学习控制的原理是根据系统前次运行所获得的输出误差信息,在控制过程中不断修正控制输入,随着迭代的进行,逐

weixin_45776027的博客 4483

整理一篇golang常用的三方库

golang的每日一库,加快你的开发速度,玩golang的一定要看看,让你的代码更加简洁,追求1行代码解决问题,joker会持续更新……...

qq_39272466的博客 802

Codex 命令行高效操作指南

本文详细介绍了Codex命令行工具的高效使用方法,包括安装配置、核心参数解析和实战技巧。通过`codex complete`、`codex explain`等指令,开发者可以快速生成代码、调试错误和重构项目,显著提升开发效率。特别推荐使用`--temperature`和`--max-tokens`参数优化生成结果。

weixin_29201313的博客 530

【亲测免费】 探索 Go 语言每日宝藏库:轻松提升开发效率

在 Go 语言的世界里,有一个宝藏般的开源项目,它名为 "Go 每日一库",致力于帮助开发者每天了解一个新的 Go 语言库。这个项目不仅提供了深入的技术分析,还给出了每个库的实际应用场景,对于希望拓宽视野和技术栈的你来说,无疑是一个绝佳的学习资源。 ## 项目介绍 "Go 每日一库" 是一个持续更新的项目,作者每天都会挑选一个有趣的或者实用的 Go 语言库进行详细的讲解。这些库涵盖了命令行解析...

gitblog_00044的博客 834

大模型之Spring AI实战系列(二):Spring Boot + OpenAI 打造聊天应用全攻略

本篇文章是《Spring AI实战》专栏的第一篇开发入门文章,我们将借助一个完整的OpenAI聊天应用示例,详细介绍如何运用Spring Boot和Spring AI快速搭建一个支持OpenAI API的聊天服务,全面涵盖基础对话、流式对话、多提供商配置等核心功能。

寻道AI,探索AI无限可能! 6855

手把手教你使用YOLOv11完成人脸分类识别,YOLOv11人脸识别,解决中文乱码问题,全网最详细教程

手把手教你使用YOLOv11完成人脸分类识别,YOLOv11人脸识别,解决中文乱码问题,全网最详细教程

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 4443

go之每日一库----标准库---flag

golang标准库中的flag库是一个帮助解析命令行选项的工具,可以利用它来开发命令行工具。

呼噜_ 386

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

CNN文本分类词汇表构建与文本向量化过程详解

卷积神经网络文本分类是自然语言处理领域的重要技术,其中**词汇表构建**和**文本向量化**是决定模型性能的关键步骤。本文将深入解析cnn-text-classification-tf项目中这两个核心过程的实现原理。🔍 ## 词汇表构建的核心原理 词汇表构建是文本分类的第一步,它负责将原始文本数据转换为模型可理解的数字表示。在cnn-text-classification-tf项目中,词汇表

gitblog_00310的博客 599

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Go每日一库之8:cast(类型转换)

cast库能在几乎所有常见类型之间转换,使用非常方便。代码量也很小,有时间建议读读源码。完整示例代码在GitHub上。ps: 本以为春节就这么几天,偷个懒,没想到。。。

dz45693的专栏 545

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

每日一库系列01 go的logrus库介绍

有时候需要在输出中添加一些字段,可以通过调用和实现。接受一个类型的参数,其底层实际上为"age": 18,})实际上,WithFields返回一个类型的值,它将和设置的保存下来。调用Entry相关方法输出日志时,保存下来的也会随之输出。Hook: 接口,需实现Levels()和Fire()方法。Levels()方法返回感兴趣的日志级别,输出其他日志时不会触发钩子。Fire是日志输出前调用的钩子方法。LevelHooks: 这是一个类型别名,表示一个映射,键是日志级别,值是一个包含多个钩子的切片。

m0_73358879的博客 849

Go每日一题

一 、空 struct{} 占多少空间?

weixin_43746366的博客 362

Go 每日一库之 ants

简介处理大量并发是 Go 语言的一大优势。语言内置了方便的并发语法,可以非常方便的创建很多个轻量级的 goroutine 并发处理任务。相比于创建多个线程,goroutine 更轻量、资源...

darjun的博客 3021

Go每日一库之Flag

上面我们介绍了使用定义选项,这种方式需要我们先定义变量,然后变量的地址。还有一种方式,调用flag.Type(其中Type可以为等)会自动为我们分配变量,返回该变量的地址。除了使用时需要解引用,其它与前一种方式基本相同。flag库并没有显示支持短选项,但是可以通过给某个相同的变量设置不同的选项来实现。即两个选项共享同一个变量。由于初始化顺序不确定,必须保证它们拥有相同的默认值。否则不传该选项时,行为是不确定的。除了使用flag库提供的选项类型,我们还可以自定义选项类型。= nil {

二进制杯莫停的博客 832

Golang一日一库之 日志库 zap

简介 在开发过程中 会使用到日志库去记录错误的日志,尤其是golang中 有无穷无尽的error 如果不记录,当你的代码出错,就无从排错了。 zap 是开源的 Go 高性能日志库 主要有以下特点: 支持不同的日志级别 能够打印基本信息等但不支持日志的分割 但是可以使用 lumberjack 也是 zap 官方推荐用于日志分割 官网:https://github.com/uber-go/zap...

有什么问题回复不及时,可以私聊我。也可以加我的星球:知识爬行者 1283

每日一库:Go 中的 bcrypt 加密库

bcrypt简单安全,是 Go 中密码加密的优选。

2401_89221867的博客 341
上一篇: 你不知道的 Go 之 string
下一篇: Go 每日一库之 fasttemplate
darjun
博客等级 码龄10年 109粉丝 82原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值