news 2026/4/16 12:48:43

【Java转Go】即时通信系统代码分析(三)用户消息广播

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Java转Go】即时通信系统代码分析(三)用户消息广播

接上文,改动不大

本期课件

视频: 39-用户消息广播
代码:server.go的Handler方法增加业务

func(this*Server)Handler(conn net.Conn){//用户上线,加入到online map中user:=NewUser(conn)this.mapLock.Lock()this.OnlineMap[user.Name]=user this.mapLock.Unlock()//广播当前用户上线消息this.BroadCast(user,"已上线")//接收客户端发送的消息gofunc(){buf:=make([]byte,4096)for{n,err:=conn.Read(buf)ifn==0{this.BroadCast(user,"下线")return}iferr!=nil&&err!=io.EOF{fmt.Println("Conn Read err:",err)return}//提取用户消息,去掉\nmsg:=string(buf[:n-1])this.BroadCast(user,msg)}}()}

逐行分析

新代码主要是://接收客户端发送的消息 业务逻辑的内容

  1. go func() { ... }()匿名函数。匿名体现在它没有名字,不像func() Handler…有名字。在这个例子中func()内部没有参数,说明它是无参方法,{}前也没有定义返回值,说明它没有返回值。最后的()表示立即调用。开始的go表示是开启一个协程去调用。
  2. 匿名函数最常用的地方在于它能“捕获”外部变量。这个匿名函数直接使用了外部的 user 变量,这种特性就叫闭包
  3. buf := make([]byte, 4096)这行中,byte[]是Slice 切片,它是一个引用类型,所以需要用make进行创建。大小是4096,一个byte在Go 中代表uint8,占一个字节,所以4096这里意味着在堆上开辟4096个byte缓冲区空间,占4KB。切片的创建语法是make([]T, len, cap),如果只有一个值,那么既是长度也是容量。
  4. n, err := conn.Read(buf)尝试从 TCP 连接的内核缓冲区中读取数据,并拷贝到 buf 切片中。如果连接中没有数据,当前代码阻塞。返回的n是字节Byte长度
  5. err != nil && err != io.EOFio.EOF 是 Go 语言中用来表示“数据读完了”的一个特殊标识,它并不是一个真正的“错误”。如果连接关闭,下一次再调用 Read,它就会返回 n=0 和 err=io.EOF。那么为什么前面判断的是if n== 0 而不是 if err== io.EOF?因为n == 0 更健壮。n==0是最终结局,如果判断err,其实有多种可能
  6. msg := string(buf[:n-1])中的buf[:n-1]是Go 中的切片重切语法:buf[low:high],low如果省略就是0。这是一个左闭右开区间[ l o w , h i g h ) [low, high)[low,high)。buf[:n-1] 只是在原来的内存块上重新定义了一个边界,没有任何内存拷贝发生。当发生string()时,把这块新内存包装成一个不可变的 string 对象赋值给 msg

一些问题

  1. 在Java 中,方法的调用是建立在对象上的,如果不考虑静态方法的话,因为方法是对象的。在Go的代码片中:this.BroadCast(user, “下线”) 这一句this是一个指针?那在Go中方法的调用是基于什么呢?
    之前有解释过,Go中的this不是一个保留的关键字,只是一个普通变量名。它在func (this *Server) Handler(conn net.Conn)
    这个方法中指代的是Server的内存地址。方法本质上是绑定到类型上的函数。 当你执行 s.BroadCast() 时,编译器实际上将其看作: BroadCast(s, user, msg) 它将调用者 s 作为第一个参数显式地传递给了函数。Go 的方法调用是基于 类型 (Type) 的。只要一个类型定义了某个方法,该类型的实例就能调用它。this.BroadCast 的意思是:“以当前这个指针指向的内存作为上下文,执行 BroadCast 逻辑”。
  2. Go 中数组和切片的区别是什么?
    在 Go 中,[4096]byte(带长度)叫数组,而 []byte(不带长度)叫切片。切片就是动态数组,数组是固定长度的。数组是值类型的,切片是引用类型的。切片可以动态扩容。通过内置的 append 函数,当容量不足时,Go 会自动申请更大的内存,并将旧数据搬迁过去。注意,因为数组是值传递,如果在方法中直接传数组,即为传入数组的拷贝,那么对原数组的修改是不会生效的。如果想通过方法修改原数组,要传入指针。这点比较像C++。当然也可以用Slice传递,不用数组传递,因为Slice就是引用类型了
  3. 创建Slice 时,长度小于容量的意义是什么?有怎样的使用场景?
    在 Go 语言中,将长度(len)设得比容量(cap)小,核心意义在于:预留空间,减少内存分配的次数。相当于性能预投资。就相当于Java 的HashMap,如果你创建的大一点,那么扩容发生的次数就比较少,扩容是很消耗资源的。
  4. 为什么是要开辟原容量2倍的内存,进行旧数据拷贝,而不是直接开辟一块新内存,原来的就内存不变?是因为Slice 需要连续内存空间吗?
    是的,因为Slice 的底层必须是连续的内存空间,连续内存空间是为了O(1)的访问效率。

本篇代码很少,但着重探讨了Slice这一Go语言特性。Go确实更像C++一些。从Java 转的话,还是需要一些思维的建立。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 11:04:53

CC2530射频调试工具使用:频谱仪与网络分析仪操作指南

玩转CC2530射频调试:用好频谱仪和网络分析仪,让Zigbee通信稳如磐石你有没有遇到过这样的情况?手里的CC2530模块明明烧录了标准Zigbee协议栈,天线也照着参考设计画了,可实际通信距离就是上不去——空旷环境下勉强撑5米&…

作者头像 李华
网站建设 2026/3/31 13:46:34

前端老铁别懵圈:RxJS到底是个啥?3天搞懂响应式编程还能顺手优化

前端老铁别懵圈:RxJS到底是个啥?3天搞懂响应式编程还能顺手优化 前端老铁别懵圈:RxJS到底是个啥?3天搞懂响应式编程还能顺手优化项目为啥每次点个按钮都得写一堆状态判断?第一次听说 RxJS 时,我以为是某种新…

作者头像 李华
网站建设 2026/4/16 0:10:41

自动资源调度AI工具:架构师降低云成本的8个使用技巧

自动资源调度AI工具:架构师降低云成本的8个实战技巧 副标题:从优化策略到落地实践,用AI帮你搞定云资源浪费 摘要/引言 作为云架构师,你是否经常遇到这样的困境: 业务峰值时资源不够用,导致服务延迟甚至宕机…

作者头像 李华
网站建设 2026/4/15 11:21:53

理解UDS诊断协议P2定时器管理:图解说明

深入理解UDS诊断中的P2定时器:从原理到实战的完整指南你有没有遇到过这样的情况——诊断仪发了一个请求,ECU明明“听见了”,却迟迟不回,结果诊断仪直接报超时失败?或者在刷写Bootloader时,刚进入编程会话就…

作者头像 李华
网站建设 2026/4/15 11:45:27

兆易创新明日上市:CPE小米TCL是基石 认购3亿美元

雷递网 雷建平 1月12日兆易创新科技集团股份有限公司(简称:“兆易创新”,股票代码:“3986”)将于2026年1月13日在港交所上市。兆易创新发行价162港元,发行2891.58万股,募资总额为46.82亿港元。兆…

作者头像 李华