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

303 lines
6.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_webSocket
import (
"common/clientMgr"
"common/config"
"common/model"
"encoding/binary"
"fmt"
"framework/goroutineMgr"
"github.com/gorilla/websocket"
"goutil/logUtil"
"goutil/timeUtil"
"strings"
"sync"
"sync/atomic"
"time"
)
const (
// 消息每次读取的上限值
con_MaxMessageSize = 2048 * 2
// 客户端失效的秒数
con_CLIENT_EXPIRE_SECONDS = 20
)
var (
// 全局客户端的id从1开始进行自增
globalClientId int32 = 0
// 字节的大小端顺序
byterOrder = binary.LittleEndian
)
// Client 实现IConnection接口的所有方法
// 定义客户端对象,以实现对客户端连接的封装
type Client struct {
// 唯一标识
id int32
// The websocket connection.
conn *websocket.Conn
// 接收到的消息内容
receiveData []byte
// 待发送的数据
sendData []*model.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 *model.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 *model.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([]*model.ServerResponseObject, 0, 16)
}
// 向客户端发送消息
// responseObj:返回值对象
func (this *Client) sendResponseObject(responseObj *model.ServerResponseObject) error {
beforeTime := time.Now().Unix()
//// 序列化发送的数据
//content, _ := json.Marshal(responseObj)
//
////debug模式下打印日志
//if debugUtil.IsDebug() {
//
// //记录日志,方便查询
// logUtil.WarnLog("向客户端发送消息%v", string(content))
//}
//
//// 进行zlib压缩
//if config.GetBaseConfig().IfCompressData {
// content, _ = zlibUtil.Compress(content, zlib.DefaultCompression)
//}
//检查是否需要压缩
if len(responseObj.DataByte) <= 0 {
responseObj.Compress(config.GetBaseConfig().IfCompressData)
}
// 发送消息
if err := this.sendMessage(responseObj.DataByte); err != nil {
return err
}
// 如果发送的时间超过3秒则记录下来
if time.Now().Unix()-beforeTime > 3 {
logUtil.WarnLog("消息Size:%d, UseTime:%d", len(responseObj.DataByte), time.Now().Unix()-beforeTime)
}
return nil
}
func (this *Client) sendMessage(message []byte) error {
this.mutex.Lock()
defer this.mutex.Unlock()
if err := this.conn.WriteMessage(websocket.BinaryMessage, 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) handleReceiveData() {
// 处理goroutine数量
goroutineName := "server_webSocket.handleSendData"
goroutineMgr.MonitorZero(goroutineName)
defer goroutineMgr.ReleaseMonitor(goroutineName)
defer clientMgr.Disconnect(this)
// 无限循环,不断地读取数据,解析数据,处理数据
for {
if this.closed {
break
}
_, message, err := this.conn.ReadMessage()
if err != nil {
break
}
// 更新activeTime
this.Active()
// 约定len(message) == 0,为心跳请求
if len(message) == 0 {
if err := this.sendMessage([]byte{}); err != nil {
return
}
continue
}
clientMgr.HandleRequest(this, message)
}
}
// 处理向客户端发送的数据
func (this *Client) handleSendData() {
// 处理goroutine数量
goroutineName := "server_webSocket.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 *websocket.Conn) *Client {
conn.SetReadLimit(con_MaxMessageSize)
// 获得自增的id值
getIncrementId := func() int32 {
atomic.AddInt32(&globalClientId, 1)
return globalClientId
}
return &Client{
id: getIncrementId(),
conn: conn,
receiveData: make([]byte, 0, 1024),
sendData: make([]*model.ServerResponseObject, 0, 16),
activeTime: time.Now().Unix(),
playerId: 0,
}
}