【Go语言Decoder报错gob: duplicate type received】

1.场景

要写一个网络通信的,服务器一直监听并解析请求。客户端接收到java端后每次都用一个新的Encoder发送一个数据结构给服务器的Decoder。导致报错:gob: duplicate type received

2. 分析

我的代码报错“gob: duplicate type received”,这个错误通常发生在接收端在接收到已经注册过的类型信息时。在使用gob进行编解码时,编解码器会对发送的数据类型进行注册,确保两端都能识别数据的类型。每次使用新的Encoder发送数据时,如果每次都在发送自定义类型的信息,而接收端的Decoder并没有清除之前接收到的类型信息,那么再次接收到同样的类型时,就会出现“duplicate type received”的错误。

以下是可能的原因和解决建议:

  • 类型信息重复发送:确保每个自定义类型只需在通信开始时发送一次类型信息,后续的数据传输直接使用已经注册过的类型ID。

  • Decoder缓存不清空:如果你在每次接收数据时都使用同一个Decoder实例,并且没有清除之前接收到的类型信息,那么在下次接收时就可能发生冲突。在这种情况下,确保在适当的时候清理或重置Decoder的内部状态。

  • 多线程/并发问题:如果你在并发环境下使用多个Encoder发送数据,且没有做好类型同步,也可能导致类型信息重复发送。这时,你需要确保并发环境下类型注册的一致性。

  • 代码逻辑错误:检查发送端是否无意中多次发送了类型信息,或者接收端在处理数据时的逻辑错误导致类型信息被多次读取和注册。

针对上述情况,请检查代码逻辑,确保类型信息只被发送和注册一次,并在接收端正确处理类型信息,避免重复注册。如果是并发场景,请考虑使用适当的同步机制来管理类型信息的发送和接收。

最后我将客户端的Encoder保存起来,实现了Encoder和Decoder一对一即可。

2.源码解读

func (dec *Decoder) Decode(e any) error {//首先,函数检查传入的 e 是否为 nil。如果为 nil,则直接丢弃即将读取的值并返回,不会进行实际的解码操作。if e == nil {return dec.DecodeValue(reflect.Value{})}//然后,获取 e 的反射值 value,并进一步判断其是否为指针类型。//根据 gob 的设计,解码的目标必须是一个指向预期类型值的指针。//如果不是指针类型,函数将返回一个错误,提示用户尝试将数据解码到非指针类型。value := reflect.ValueOf(e)if value.Type().Kind() != reflect.Pointer {dec.err = errors.New("gob: attempt to decode into a non-pointer")return dec.err}//如果 value 是指针类型,函数会调用 DecodeValue 方法进行实际的解码工作。//DecodeValue 方法会将从输入流读取的值解码并存放到 value 所指向的具体类型实例中。return dec.DecodeValue(value)
}func (dec *Decoder) DecodeValue(v reflect.Value) error {//首先,检查传入的反射值v是否有效。如果v无效(v.Kind() == reflect.Invalid),那么函数会跳过该值,不进行任何处理。if v.IsValid() {//如果v是一个非nil的指针,函数会尝试通过指针存储解码后的值。如果不是指针或者虽然是指针但为nil,那么v必须是可以设置的(v.CanSet()),否则函数会返回一个错误。if v.Kind() == reflect.Pointer && !v.IsNil() {// 表示正确,什么也不做。接下来会用这个point来存储} else if !v.CanSet() {return errors.New("gob: DecodeValue of unassignable value")}}// Make sure we're single-threaded through here.dec.mutex.Lock()defer dec.mutex.Unlock()dec.buf.Reset() // In case data lingers from previous invocation.// 设置内部错误状态为nil(dec.err = nil)。dec.err = nil// 通过调用decodeTypeSequence(false)从输入流中解析出要解码的类型ID。id := dec.decodeTypeSequence(false)if dec.err == nil {// 如果解析类型ID过程中没有遇到错误,则调用decodeValue(id, v)将解析出的数据解码,并存入反射值vdec.decodeValue(id, v)}return dec.err
}
func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId {firstMessage := true// 循环读取编码数据流,直到遇到错误或达到文件结束(EOF)for dec.err == nil {// 每次循环开始时,首先检查当前缓冲区(dec.buf)是否有剩余数据,如果没有,则尝试接收新的消息(通过 recvMessage 方法)。if dec.buf.Len() == 0 {if !dec.recvMessage() {if !firstMessage && dec.err == io.EOF {dec.err = io.ErrUnexpectedEOF}break}}// 从缓冲区中读取并解析类型 ID (typeId)id := typeId(dec.nextInt())// 如果类型 ID 为正数,表示接下来的数据属于该类型,此时返回该类型 ID,表明已准备好解码对应的值。if id >= 0 {return id}// 如果类型 ID 为负数,表示接下来是类型定义,调用 recvType 方法解析并注册该类型。dec.recvType(-id)if dec.err != nil {break}// 当解码接口类型(isInterface 参数为 true)时,处理完类型定义之后,可能会有额外的 DelimitedValue 数据。这时,跳过其计数值,以确保正确解析下一个值。if dec.buf.Len() > 0 {if !isInterface {dec.err = errors.New("extra data in buffer")break}dec.nextUint()}firstMessage = false}return -1
}
// 接收并加载类型定义。这个函数的主要作用是处理 gob 编码格式中关于自定义类型的定义信息。
func (dec *Decoder) recvType(id typeId) {//检查给定的类型 ID(id)是否已经存在于 dec.wireType 映射表中。// 如果该类型 ID 已经存在或者小于预设的 firstUserId,说明收到了重复的类型定义,这是一种错误情况,函数将返回错误信息 "gob: duplicate type received"。if id < firstUserId || dec.wireType[id] != nil {dec.err = errors.New("gob: duplicate type received")return}// 创建一个新的 wireType 结构体实例 wirewire := new(wireType)// 使用 decodeValue 方法解码类型定义,将接收到的二进制数据转换并填充到 wire 结构体中。这里的 tWireType 是 wireType 结构体类型的反射值。dec.decodeValue(tWireType, reflect.ValueOf(wire))if dec.err != nil {return}// Remember we've seen this type.dec.wireType[id] = wire
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/771659.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

后端常问面经之Spring和Mybatis框架

Spring的IOC介绍一下&#xff1a; 所谓控制就是对象的创建、初始化、销毁。 创建对象&#xff1a;原来是 new 一个&#xff0c;现在是由 Spring 容器创建。 初始化对象&#xff1a;原来是对象自己通过构造器或者 setter 方法给依赖的对象赋值&#xff0c;现在是由 Spring 容器…

AI研报:从Sora看多模态大模型发展

《从Sora看多模态大模型发展》的研报来自浙商证券&#xff0c;写于2024年2月。 这篇报告主要探讨了多模态大模型的发展趋势&#xff0c;特别是OpenAI发布的视频生成模型Sora&#xff0c;以及其对行业发展的影响。以下是报告的核心内容概述&#xff1a; Sora模型的发布&#x…

【学习】python标准库importlib.import_module,用于动态导入模块。

importlib.import_module 是 Python 标准库中的一部分&#xff0c;用于在运行时动态地导入模块。 具体用法示例&#xff1a; # module1.py def say_hello():print("Hello from module 1!")# module2.py def say_hello():print("Hello from module 2!")imp…

面试算法-103-对链表进行插入排序

题目 给定单个链表的头 head &#xff0c;使用 插入排序 对链表进行排序&#xff0c;并返回 排序后链表的头 。 插入排序 算法的步骤: 插入排序是迭代的&#xff0c;每次只移动一个元素&#xff0c;直到所有元素可以形成一个有序的输出列表。 每次迭代中&#xff0c;插入排序…

基于单片机的太阳能充电系统设计

摘要:本文所设计的太阳能充电系统主要由以下几个模块组成:STC89C52 主控模块、TP4056 充电电路、电压AD 采集模块、LCD1602 液晶显示模块和太阳能充电电池等组成。此太阳能充电器制作简单,性价比高,性能稳定。 关键词:LCD1602;太阳能充电系统;ADC0832 太阳能充电系统的充…

MySQL WHERE 条件查询

我们通常要求在执行 SELECT 查询时&#xff0c;都要带上查询条件。那这一节&#xff0c;我们就来学习一些简单的 WHERE 条件查询。 我们仍然以技术派文章表 article 为例&#xff0c;比如说我们要查找标题为“聊聊分库分表”的文章&#xff0c;可以这么写&#xff1a; SELECT *…

echarts做水滴图;解决[echarts] unknown series liquidfill 水球加载问题

一份echarts示例代码&#xff0c;包含水滴图 直接在echarts里使用水滴图liquidfill会报错[echarts] unknown series liquidfill 解决方案&#xff1a;需要下载echarts-liquidfill依赖 echarts-liquidfill2兼容echarts4; echarts-liquidfill3兼容echarts5; 例如&#xff1a;我的…

Error establishing a database connection

WordPress网站打开的时候出现“Error establishing a database connection”是怎么了呢&#xff1f;被黑了吗&#xff1f; 大家在使用WordPress建站时常遇到这样的问题。根据我的经验&#xff0c;出现这个情况一般有下以几种原因&#xff1a; 1、数据库密码填写的有误。需要检…

IDEA使用手册

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

【vue3(七)】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、ref二、TS接口泛型规范1.创建ts文件&#xff0c;定义接口2.引入规范 三、props的使用四、生命周期&#xff08;生命周期函数&#xff0c;生命周期钩子&…

MySQL多表联查函数

1 多表联查 1.1 表之间的关系 表和表的关系有: 一对一 老公 --> 老婆 , 人 ---> 身份证/户口本 一对多 皇帝 --> 妻妾 , 人 ---> 房/车 多对多 订单 --> 商品 1.2 合并结果集 合并结果集,是将多表查询的结果纵向合并 语法: select field1,field2 from t1 un…

rust中字符串String常用方法和注意事项

Rust 中通常说的字符串指的是&#xff1a;String 和 &str(字符串字面值、或者叫字符串切片)这两种类型。str是rust中基础字符串类型&#xff0c;String是标准库里面的类型。Rust 中的字符串本质上是&#xff1a;Byte的集合&#xff08;Vec<u8>&#xff09; 基础类型…

【第三方登录】Google邮箱

登录谷歌邮箱开发者 https://console.developers.google.com/ 先创建项目 我们用的web应用 设置回调 核心主要&#xff1a; 1.创建应用 2.创建客户端ID 3.设置域名和重定向URL 4.对外公开&#xff0c;这样所有的gmail邮箱 都能参与测试PHP代码实现 引入第三方包 h…

Git 的cherry-pick含义

目录 1. cherry-pick的基本概念 2. cherry-pick的使用场景 3. cherry-pick的使用方法 结论 1. cherry-pick的基本概念 git cherry-pick是一个Git命令&#xff0c;它允许你选择一个或多个其他分支上的提交&#xff08;commits&#xff09;&#xff0c;并将它们复制到你当前的…

Spring实例化Bean的三种方式

参考资料&#xff1a; Core Technologies 核心技术 spring实例化bean的三种方式 构造器来实例化bean 静态工厂方法实例化bean 非静态工厂方法实例化bean_spring中有参构造器实例化-CSDN博客 1. 构造函数 1.1. 空参构造函数 下面这样表示调用空参构造函数&#xff0c;使用p…

HTML5入门笔记

我使用中英互译的方法来制作本次笔记&#xff0c;课程来自网上精品资源 VSCode相关快捷键 选择文件夹和拖拽文件夹来打开 使用&#xff01;加enter&#xff08;回车&#xff09;&#xff0c;输入默认模板 <!DOCTYPE html> <html lang"en"> <head&…

go的Job Scheduling

背景 司内线上服务有很多异步脚本,大量冗余代码,管理很不方便 急需一个美丽的框架,让代码变得美好 包 go get github.com/go-co-op/gocron/v2 介绍 gocron is a job scheduling package which lets you run Go functions at pre-determined intervals. 概念 Job Job封…

linux查看usb是3.0还是2.0

1 作为device cat /sys/devices/platform/10320000.usb30drd/10320000.dwc3/udc/10320000.dwc3/current_speed 或 /sys/class/udc/10320000.dwc3/current_speed 如下 high-speed usb2.0 super-speed usb3.0 2 作为host linux下使用以下命令查看 &#xff0c;如果显示 速率为…

python关于字符串基础学习

字符串 python字符串是不可改变的 Python不支持单字符类型&#xff0c;单字符也是作为一个字符串使用的。 字符串编码 python3直接支持Unicode,可以表示世界上任何书面语言的字符 python3的字符默认就是16位Unicode编码&#xff0c;ASCII是Unicode的子集 使用内置函数 ord()…

c++初步

作业&#xff1a; 定义自己的命名空间&#xff0c;其中有string类型的变量&#xff0c;再定义两个函数&#xff0c;一个函数完成字符串的输入&#xff0c;一个函数完成求字符串长度&#xff0c;再定义一个全局函数完成对该字符串的反转 #include <iostream> #include &…