goProject/trunk/game/common/server_tcp/client.go
2025-01-15 17:36:12 +08:00

348 lines
7.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package server_tcp
import (
"compress/zlib"
"encoding/binary"
"encoding/json"
"fmt"
"net"
"strings"
"sync"
"sync/atomic"
"time"
"common/clientMgr"
"common/config"
. "common/model"
"framework/goroutineMgr"
"goutil/intAndBytesUtil"
"goutil/logUtil"
"goutil/timeUtil"
"goutil/zlibUtil"
)
const (
// 包头的长度
con_HEADER_LENGTH = 4
// 客户端失效的秒数
con_CLIENT_EXPIRE_SECONDS int64 = 300
)
var (
// 全局客户端的id从1开始进行自增
globalClientId int32 = 0
// 字节的大小端顺序
byterOrder = binary.LittleEndian
)
// 定义客户端对象,以实现对客户端连接的封装
type Client struct {
// 唯一标识
id int32
// 客户端连接对象
conn net.Conn
// 接收到的消息内容
receiveData []byte
// 待发送的数据
sendData []*ServerResponseObject
// 连接是否关闭(通过此字段来协调receiveData和sendData方法)
closed bool
// 锁对象用于控制对sendDatap的并发访问receiveData不需要因为是同步访问
mutex sync.Mutex
// 玩家Id
playerId int64
// 上次活跃时间
activeTime int64
}
// 获取唯一标识
func (this *Client) GetId() int32 {
return this.id
}
// 获取玩家Id
// 返回值:
// 玩家Id
func (this *Client) GetPlayerId() int64 {
return this.playerId
}
// 玩家登陆
// playerId玩家Id
// 返回值:无
func (this *Client) PlayerLogin(playerId int64) {
this.playerId = playerId
}
// 获取远程地址IP_Port
func (this *Client) GetRemoteAddr() string {
items := strings.Split(this.conn.RemoteAddr().String(), ":")
return fmt.Sprintf("%s_%s", items[0], items[1])
}
// 获取远程地址IP
func (this *Client) getRemoteShortAddr() string {
items := strings.Split(this.conn.RemoteAddr().String(), ":")
return items[0]
}
// 获取待发送的数据
// 返回值:
// 待发送数据项
// 是否含有有效数据
func (this *Client) getSendData() (responseObj *ServerResponseObject, exists bool) {
this.mutex.Lock()
defer this.mutex.Unlock()
// 如果没有数据则直接返回
if len(this.sendData) == 0 {
return
}
// 取出第一条数据,并为返回值赋值
responseObj = this.sendData[0]
exists = true
// 删除已经取出的数据
this.sendData = this.sendData[1:]
return
}
// 发送数据
// sendDataItemObj:待发送数据项
// 返回值:无
func (this *Client) SendMessage(responseObj *ServerResponseObject) {
this.mutex.Lock()
defer this.mutex.Unlock()
this.sendData = append(this.sendData, responseObj)
}
// 清空待发送数据
func (this *Client) ClearSendData() {
this.mutex.Lock()
defer this.mutex.Unlock()
this.sendData = make([]*ServerResponseObject, 0, 16)
}
// 发送字节数组消息
// responseObj:返回值对象
func (this *Client) sendResponseObject(responseObj *ServerResponseObject) error {
beforeTime := time.Now().Unix()
// 序列化发送的数据
content, _ := json.Marshal(responseObj)
// 进行zlib压缩
if config.GetBaseConfig().IfCompressData {
content, _ = zlibUtil.Compress(content, zlib.DefaultCompression)
}
// 获得数据内容的长度
contentLength := len(content)
// 将长度转化为字节数组
header := intAndBytesUtil.Int32ToBytes(int32(contentLength), byterOrder)
// 将头部与内容组合在一起
message := append(header, content...)
// 发送消息
if err := this.sendMessage(message); err != nil {
return err
}
// 如果发送的时间超过3秒则记录下来
if time.Now().Unix()-beforeTime > 3 {
logUtil.WarnLog("消息Size:%d, UseTime:%d", contentLength, time.Now().Unix()-beforeTime)
}
return nil
}
func (this *Client) sendMessage(message []byte) error {
this.mutex.Lock()
defer this.mutex.Unlock()
if _, err := this.conn.Write(message); err != nil {
return err
}
return nil
}
// 客户端活跃
// 返回值:无
func (this *Client) Active() {
atomic.StoreInt64(&this.activeTime, time.Now().Unix())
}
// 判断客户端是否超时超过300秒不活跃算作超时
// 返回值:是否超时
func (this *Client) Expired() bool {
return time.Now().Unix() > this.activeTime+con_CLIENT_EXPIRE_SECONDS
}
// 客户端连接对象断开
// 返回值:无
func (this *Client) Close() {
this.conn.Close()
this.closed = true
this.playerId = 0
}
// 格式化
func (this *Client) String() string {
return fmt.Sprintf("{Id:%d, RemoteAddr:%s, activeTime:%s, playerId:%s}", this.id, this.GetRemoteAddr(), timeUtil.Format(time.Unix(this.activeTime, 0), "yyyy-MM-dd HH:mm:ss"), this.playerId)
}
// -----------------------------实现IConnection接口方法end-------------------------
// 客户端启动函数
func (this *Client) start() {
go this.handleReceiveData()
go this.handleSendData()
}
// 获取有效的消息
// 返回值:
// 消息内容
// 是否含有有效数据
func (this *Client) getReceiveData() (message []byte, exists bool) {
// 判断是否包含头部信息
if len(this.receiveData) < con_HEADER_LENGTH {
return
}
// 获取头部信息
header := this.receiveData[:con_HEADER_LENGTH]
// 将头部数据转换为内部的长度
contentLength := intAndBytesUtil.BytesToInt32(header, byterOrder)
// 约定len(message) == 0,为心跳请求
if contentLength == 0 {
// 将对应的数据截断,以得到新的内容,并返回心跳内容
this.receiveData = this.receiveData[con_HEADER_LENGTH:]
if err := this.sendMessage([]byte{}); err != nil {
return
}
return
}
// 判断长度是否满足
if len(this.receiveData) < con_HEADER_LENGTH+int(contentLength) {
return
}
// 提取消息内容
message = this.receiveData[con_HEADER_LENGTH : con_HEADER_LENGTH+contentLength]
exists = true
// 将对应的数据截断,以得到新的数据
this.receiveData = this.receiveData[con_HEADER_LENGTH+contentLength:]
return
}
// 处理客户端收到的数据
func (this *Client) handleReceiveData() {
// 处理goroutine数量
goroutineName := "server_tcp.handleReceiveData"
goroutineMgr.MonitorZero(goroutineName)
defer goroutineMgr.ReleaseMonitor(goroutineName)
defer clientMgr.Disconnect(this)
// 无限循环,不断地读取数据,解析数据,处理数据
for {
if this.closed {
break
}
// 先读取数据每次读取1024个字节
readBytes := make([]byte, 2048*2)
// Read方法会阻塞所以不用考虑异步的方式
n, err := this.conn.Read(readBytes)
if err != nil {
break
}
// 将读取到的数据追加到已获得的数据的末尾并更新activeTime
this.receiveData = append(this.receiveData, readBytes[:n]...)
this.Active()
// 处理数据
for {
message, exists := this.getReceiveData()
if !exists {
break
}
clientMgr.HandleRequest(this, message)
}
}
}
// 处理需要客户端发送的数据
func (this *Client) handleSendData() {
// 处理goroutine数量
goroutineName := "server_tcp.handleSendData"
goroutineMgr.MonitorZero(goroutineName)
defer goroutineMgr.ReleaseMonitor(goroutineName)
defer clientMgr.Disconnect(this)
for {
if this.closed {
break
}
// 如果发送出现错误,表示连接已经断开,则退出方法;
if sendDataItemObj, exists := this.getSendData(); exists {
// 判断是否为断开客户端连接的数据
if sendDataItemObj.IsDisconnect() {
break
}
if err := this.sendResponseObject(sendDataItemObj); err != nil {
return
}
} else {
time.Sleep(5 * time.Millisecond)
}
}
}
// 新建客户端对象
// conn连接对象
// 返回值:客户端对象的指针
func newClient(conn net.Conn) *Client {
// 获得自增的id值
getIncrementId := func() int32 {
atomic.AddInt32(&globalClientId, 1)
return globalClientId
}
return &Client{
id: getIncrementId(),
conn: conn,
receiveData: make([]byte, 0, 2048*2),
sendData: make([]*ServerResponseObject, 0, 16),
activeTime: time.Now().Unix(),
playerId: 0,
}
}