2. 使用golang特性编写的程序结构

所有的服务器都有相似的程序结构

net.Conn 的 Write 和 Read 方法都是阻塞式执行的,所以要 为每个 TCP 连接创建两个协程,分别用来接收和发送数据.在接收协程中计算生成的数据,要发送给对端时,常规做法是生成一个 []byte 切片 ------ buf := make( []byte,length),然后把计算生成的数据拷贝到 buf 中,再投递到类型是 chan []byte 的 channel 中.发送协程只做一件事,从 channel 中取出数据并发送给对端

在上面所述的接收协程中,buf := make( []byte,length) 这行代码可以用内存池进行优化,内存池的实现可以选用达达的开源代码: https://github.com/funny/slab
登录服务器 login 和网关服务器 gate 中,会建立数千个 TCP 连接数/协程数,在这两种服务器上选用的内存池类型是基于 golang 临时对象池的 slab.SyncPool;而大厅服务器 lobby 和路由服务器 route 的 TCP 连接数/协程数(几个至几十个不等)几乎是固定的,在这两种服务器上选用的内存池类型是基于 channel 的 slab.ChanPool;发送协程中把 buf 从 channel 中取出并发送完后,需要把 buf 回收进内存池

为了处理TCP的粘包情况,定义TCP数据逻辑包(以下简称逻辑包)格式为 包头 + 数据体;包头是两个 int32 字段,共计8字节,第一个 int32 字段表示协议号,第二个 int32 字段表示随后的数据体;比如客户端发送长度为 3 的帐号字符串 "abc" 给登录服务器login,则逻辑包包头的协议号字段可以约定填 1,数据长度填 3,逻辑包的数据体就是字符串 "abc"
逻辑包是自取的名字,在不同的团队可能有不同的称呼,是指在应用层提交的一段 TCP 数据,能够完整的表示上层业务逻辑意义,并非底层(网卡或者 TCP/IP 层)上的数据包.在不同的团队定义的格式也可能不同,但目的都是为了处理粘包


golang 中处理粘包可以使用 io.ReadFull 和 bufio.Reader,具体用法:

// c 表示刚创建的 net.Conn r := bufio.NewReaderSize( c,1024 ) recvBuf := make( []byte,1024 ) io.ReadFull( r,recvBuf[ :8 ] ) // 先读取包头,收到 8 个字节才继续往下执行,否则一直阻塞 dataBodyLen := binary.BigEndian.Uint32( recvBuf[4:]) // 读取包头中的数据体长度 io.ReadFull( read,recvBuf[ 8: 8 + dataBodyLen ] ) // 执行完这一句后,recvBuf 中保存的就是一个完整的逻辑包了,完整逻辑包的长度是 8 + dataBodyLen

相关文章

什么是Go的接口? 接口可以说是一种类型,可以粗略的理解为他...
1、Golang指针 在介绍Golang指针隐式间接引用前,先简单说下...
1、概述 1.1 Protocol buffers定义 Protocol buffe...
判断文件是否存在,需要用到"os"包中的两个函数: os.Stat(...
1、编译环境 OS :Loongnix-Server Linux release 8.3 CPU指...
1、概述 Golang是一种强类型语言,虽然在代码中经常看到i:=1...