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

来源:互联网 发布:吉首大学教务网络管理 编辑:程序博客网 时间:2024/05/29 04:53
    所有的服务器都有相似的程序结构

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

    在上面所述的接收协程中,buf := make( []byte, 0, 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
0 0
原创粉丝点击