348 lines
7.7 KiB
Go
348 lines
7.7 KiB
Go
|
|
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,
|
|||
|
|
}
|
|||
|
|
}
|