登录代码提交
This commit is contained in:
109
trunk/game/common/clientMgr/clientMgr.go
Normal file
109
trunk/game/common/clientMgr/clientMgr.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package clientMgr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"goutil/debugUtil"
|
||||
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
clientMap = make(map[int32]IClient, 1024)
|
||||
mutex sync.RWMutex
|
||||
)
|
||||
|
||||
func RegisterClient(clientObj IClient) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
clientMap[clientObj.GetId()] = clientObj
|
||||
}
|
||||
|
||||
func UnregisterClient(clientObj IClient) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
delete(clientMap, clientObj.GetId())
|
||||
}
|
||||
|
||||
func GetClient(id int32) (clientObj IClient, exists bool) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
|
||||
clientObj, exists = clientMap[id]
|
||||
return
|
||||
}
|
||||
|
||||
func getClientCount() int {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
|
||||
return len(clientMap)
|
||||
}
|
||||
|
||||
func getExpiredClientList() (expiredList []IClient) {
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
|
||||
for _, item := range clientMap {
|
||||
if item.Expired() {
|
||||
expiredList = append(expiredList, item)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 绑定连接到玩家
|
||||
func BindClientAndPlayer(clientObj IClient, playerObj *Player) {
|
||||
// 发送在另一台设备登陆的信息
|
||||
sendLoginAnotherDeviceMsg := func(clientObj IClient) {
|
||||
responseObj := NewServerResponseObject()
|
||||
responseObj.SetMethodName("Login")
|
||||
responseObj.SetResultStatus(LoginOnAnotherDevice)
|
||||
|
||||
mes, _ := json.Marshal(responseObj)
|
||||
|
||||
if debugUtil.IsDebug() {
|
||||
logUtil.WarnLog("BindClientAndPlayer11BindClientAndPlayer:%v", string(mes))
|
||||
}
|
||||
|
||||
// 先发送消息,然后再断开连接
|
||||
clientObj.SendMessage(responseObj)
|
||||
clientObj.SendMessage(NewDisconnectServerResponseObject())
|
||||
}
|
||||
|
||||
// 立即更新活跃时间,避免在将要清除时,玩家又登陆的极端情况
|
||||
clientObj.Active()
|
||||
clientObj.ClearSendData()
|
||||
|
||||
// 判断是否重复登陆
|
||||
if playerObj.ClientId > 0 {
|
||||
if oldClientObj, exist := GetClient(playerObj.ClientId); exist {
|
||||
// 如果不是同一个客户端,则先给客户端发送在其他设备登陆信息,然后断开连接
|
||||
if clientObj != oldClientObj {
|
||||
sendLoginAnotherDeviceMsg(oldClientObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新客户端对象的玩家Id、以及玩家对象对应的客户端Id
|
||||
clientObj.PlayerLogin(playerObj.Id)
|
||||
playerObj.ClientLogin(clientObj.GetId())
|
||||
}
|
||||
|
||||
// Disconnect 清理断开的连接数据
|
||||
// / 连接对象
|
||||
func Disconnect(clientObj IClient) {
|
||||
if clientObj.GetPlayerId() != 0 {
|
||||
playerObj, exist, err := getPlayerHandler(clientObj.GetPlayerId(), false)
|
||||
if err == nil && exist && clientObj.GetId() == playerObj.ClientId {
|
||||
playerObj.ClientLogout()
|
||||
}
|
||||
}
|
||||
|
||||
clientObj.Close()
|
||||
UnregisterClient(clientObj)
|
||||
}
|
||||
41
trunk/game/common/clientMgr/expire.go
Normal file
41
trunk/game/common/clientMgr/expire.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package clientMgr
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"framework/goroutineMgr"
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 清理过期的客户端
|
||||
go clearExpiredClient()
|
||||
}
|
||||
|
||||
// 清理过期的客户端
|
||||
func clearExpiredClient() {
|
||||
// 处理goroutine数量
|
||||
goroutineName := "clientMgr.clearExpiredClient"
|
||||
goroutineMgr.Monitor(goroutineName)
|
||||
defer goroutineMgr.ReleaseMonitor(goroutineName)
|
||||
|
||||
for {
|
||||
// 休眠指定的时间(单位:秒)(放在此处是因为程序刚启动时并没有过期的客户端,所以先不用占用资源;并且此时LogPath尚未设置,如果直接执行后面的代码会出现panic异常)
|
||||
time.Sleep(5 * time.Minute)
|
||||
|
||||
// 获取过期的客户端列表
|
||||
expiredClientList := getExpiredClientList()
|
||||
expiredClientCount := len(expiredClientList)
|
||||
beforeClientCount := getClientCount()
|
||||
|
||||
// 客户端断开
|
||||
if expiredClientCount > 0 {
|
||||
for _, item := range expiredClientList {
|
||||
Disconnect(item)
|
||||
}
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
logUtil.DebugLog("清理前的客户端数量为:%d,本次清理不活跃的客户端数量为:%d", beforeClientCount, expiredClientCount)
|
||||
}
|
||||
}
|
||||
86
trunk/game/common/clientMgr/handleRequest.go
Normal file
86
trunk/game/common/clientMgr/handleRequest.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package clientMgr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
. "common/model"
|
||||
"goutil/debugUtil"
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
// HandleRequest 处理客户端请求
|
||||
// clientObj:对应的客户端对象
|
||||
// request:请求内容字节数组(json格式)
|
||||
// 返回值:无
|
||||
func HandleRequest(clientObj IClient, request []byte) {
|
||||
responseObj := NewServerResponseObject()
|
||||
|
||||
defer func() {
|
||||
// 如果是客户端数据错误,则将客户端请求数据记录下来
|
||||
if responseObj.ResultStatus == ClientDataError {
|
||||
logUtil.ErrorLog("client:%s\n请求的数据为:%s, 返回的结果为客户端数据错误", clientObj, string(request))
|
||||
}
|
||||
|
||||
// 调用发送消息接口
|
||||
clientObj.SendMessage(responseObj)
|
||||
|
||||
// 记录DEBUG日志
|
||||
if debugUtil.IsDebug() {
|
||||
result, _ := json.Marshal(responseObj)
|
||||
logUtil.DebugLog("client:%s\nRequest:%s\nResponse:%s", clientObj, string(request), string(result))
|
||||
}
|
||||
}()
|
||||
|
||||
// 解析请求字符串
|
||||
requestObj := new(ServerRequestObject)
|
||||
if err := json.Unmarshal(request, requestObj); err != nil {
|
||||
logUtil.ErrorLog("反序列化出错,字符串:%s,错误信息为:%s", string(request), err)
|
||||
responseObj.SetResultStatus(ClientDataError)
|
||||
return
|
||||
}
|
||||
|
||||
// 为参数赋值
|
||||
responseObj.SetMethodName(requestObj.MethodName)
|
||||
|
||||
// 对参数要特殊处理:将Client、Player特殊处理
|
||||
parameters := make([]interface{}, 0)
|
||||
if requestObj.MethodName == "Login" {
|
||||
parameters = append(parameters, interface{}(clientObj))
|
||||
parameters = append(parameters, requestObj.Parameters...)
|
||||
} else {
|
||||
// 判断玩家是否已经登陆
|
||||
if clientObj.GetPlayerId() == 0 {
|
||||
responseObj.SetResultStatus(NoLogin)
|
||||
return
|
||||
}
|
||||
|
||||
// 判断是否能找到玩家
|
||||
var playerObj *Player
|
||||
var exists bool
|
||||
var err error
|
||||
if getPlayerHandler == nil {
|
||||
panic("getPlayerHandler is nil, please set first")
|
||||
}
|
||||
playerObj, exists, err = getPlayerHandler(clientObj.GetPlayerId(), false)
|
||||
if err != nil {
|
||||
responseObj.SetResultStatus(DataError)
|
||||
return
|
||||
}
|
||||
|
||||
if !exists {
|
||||
responseObj.SetResultStatus(NoLogin)
|
||||
return
|
||||
}
|
||||
|
||||
parameters = append(parameters, interface{}(clientObj))
|
||||
parameters = append(parameters, interface{}(playerObj))
|
||||
parameters = append(parameters, requestObj.Parameters...)
|
||||
}
|
||||
|
||||
// 为参数赋值
|
||||
requestObj.ModuleName = "Chat"
|
||||
requestObj.Parameters = parameters
|
||||
|
||||
// 调用方法
|
||||
responseObj = callFunction(requestObj)
|
||||
}
|
||||
33
trunk/game/common/clientMgr/interface.go
Normal file
33
trunk/game/common/clientMgr/interface.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package clientMgr
|
||||
|
||||
import . "common/model"
|
||||
|
||||
// IClient 客户端连接接口
|
||||
type IClient interface {
|
||||
// GetId 获取客户端对象的唯一标识
|
||||
GetId() int32
|
||||
|
||||
// 获取玩家Id
|
||||
GetPlayerId() int64
|
||||
|
||||
// 玩家登录
|
||||
PlayerLogin(int64)
|
||||
|
||||
// 发送数据
|
||||
SendMessage(*ServerResponseObject)
|
||||
|
||||
// 清空待发送数据
|
||||
ClearSendData()
|
||||
|
||||
// 客户端活跃
|
||||
Active()
|
||||
|
||||
// 获取远程地址(IP_Port)
|
||||
GetRemoteAddr() string
|
||||
|
||||
// 客户端连接超时
|
||||
Expired() bool
|
||||
|
||||
// 客户端连接对象断开
|
||||
Close()
|
||||
}
|
||||
25
trunk/game/common/clientMgr/reflect_method.go
Normal file
25
trunk/game/common/clientMgr/reflect_method.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package clientMgr
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// 反射的方法和输入、输出参数类型组合类型
|
||||
type methodAndInOutTypes struct {
|
||||
// 反射出来的对应方法对象
|
||||
Method reflect.Value
|
||||
|
||||
// 反射出来的方法的输入参数的类型集合
|
||||
InTypes []reflect.Type
|
||||
|
||||
// 反射出来的方法的输出参数的类型集合
|
||||
OutTypes []reflect.Type
|
||||
}
|
||||
|
||||
func newmethodAndInOutTypes(_method reflect.Value, _inTypes []reflect.Type, _outTypes []reflect.Type) *methodAndInOutTypes {
|
||||
return &methodAndInOutTypes{
|
||||
Method: _method,
|
||||
InTypes: _inTypes,
|
||||
OutTypes: _outTypes,
|
||||
}
|
||||
}
|
||||
356
trunk/game/common/clientMgr/reflect_mgr.go
Normal file
356
trunk/game/common/clientMgr/reflect_mgr.go
Normal file
@@ -0,0 +1,356 @@
|
||||
package clientMgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
. "common/model"
|
||||
"goutil/debugUtil"
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
const (
|
||||
// 供客户端访问的模块的后缀
|
||||
con_ModuleSuffix = "Module"
|
||||
|
||||
// 定义用于分隔模块名称和方法名称的分隔符
|
||||
con_DelimeterOfObjAndMethod = "_"
|
||||
)
|
||||
|
||||
var (
|
||||
// 定义存放所有方法映射的变量
|
||||
methodMap = make(map[string]*methodAndInOutTypes)
|
||||
)
|
||||
|
||||
// 获取结构体类型的名称
|
||||
// structType:结构体类型
|
||||
// 返回值:
|
||||
// 结构体类型的名称
|
||||
func getStructName(structType reflect.Type) string {
|
||||
reflectTypeStr := structType.String()
|
||||
reflectTypeArr := strings.Split(reflectTypeStr, ".")
|
||||
|
||||
return reflectTypeArr[len(reflectTypeArr)-1]
|
||||
}
|
||||
|
||||
// 获取完整的模块名称
|
||||
// moduleName:模块名称
|
||||
// 返回值:
|
||||
// 完整的模块名称
|
||||
func getFullModuleName(moduleName string) string {
|
||||
return moduleName + con_ModuleSuffix
|
||||
}
|
||||
|
||||
// 获取完整的方法名称
|
||||
// structName:结构体名称
|
||||
// methodName:方法名称
|
||||
// 返回值:
|
||||
// 完整的方法名称
|
||||
func getFullMethodName(structName, methodName string) string {
|
||||
return structName + con_DelimeterOfObjAndMethod + methodName
|
||||
}
|
||||
|
||||
// 解析方法的输入输出参数
|
||||
// method:方法对应的反射值
|
||||
// 返回值:
|
||||
// 输入参数类型集合
|
||||
// 输出参数类型集合
|
||||
func resolveMethodInOutParams(method reflect.Value) (inTypes []reflect.Type, outTypes []reflect.Type) {
|
||||
methodType := method.Type()
|
||||
for i := 0; i < methodType.NumIn(); i++ {
|
||||
inTypes = append(inTypes, methodType.In(i))
|
||||
}
|
||||
|
||||
for i := 0; i < methodType.NumOut(); i++ {
|
||||
outTypes = append(outTypes, methodType.Out(i))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 将需要对客户端提供方法的对象进行注册
|
||||
// structObject:对象
|
||||
func RegisterFunction(structObject interface{}) {
|
||||
// 获取structObject对应的反射 Type 和 Value
|
||||
reflectValue := reflect.ValueOf(structObject)
|
||||
reflectType := reflect.TypeOf(structObject)
|
||||
|
||||
// 提取对象类型名称
|
||||
structName := getStructName(reflectType)
|
||||
|
||||
// 获取structObject中返回值为responseObject的方法
|
||||
for i := 0; i < reflectType.NumMethod(); i++ {
|
||||
// 获得方法名称
|
||||
methodName := reflectType.Method(i).Name
|
||||
|
||||
// 判断是否为导出的方法
|
||||
|
||||
// 获得方法及其输入参数的类型列表
|
||||
method := reflectValue.MethodByName(methodName)
|
||||
inTypes, outTypes := resolveMethodInOutParams(method)
|
||||
|
||||
// 判断输出参数数量是否正确
|
||||
if len(outTypes) != 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 判断返回值是否为responseObject
|
||||
outType := outTypes[0]
|
||||
if _, ok := outType.FieldByName("Code"); !ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := outType.FieldByName("Message"); !ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := outType.FieldByName("Data"); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// 添加到列表中
|
||||
methodMap[getFullMethodName(structName, methodName)] = newmethodAndInOutTypes(method, inTypes, outTypes)
|
||||
|
||||
debugUtil.Println(fmt.Sprintf("%s_%s注册成功,当前共%d个方法", structName, methodName, len(methodMap)))
|
||||
}
|
||||
}
|
||||
|
||||
// 调用方法
|
||||
// requestObj:请求对象
|
||||
func callFunction(requestObj *ServerRequestObject) *ServerResponseObject {
|
||||
responseObj := NewServerResponseObject()
|
||||
var methodAndInOutTypes *methodAndInOutTypes
|
||||
var exists bool
|
||||
|
||||
// 根据传入的ModuleName和MethodName找到对应的方法对象
|
||||
key := getFullMethodName(getFullModuleName(requestObj.ModuleName), requestObj.MethodName)
|
||||
if methodAndInOutTypes, exists = methodMap[key]; !exists {
|
||||
logUtil.ErrorLog("找不到指定的方法:%v,%s", methodMap, key)
|
||||
return responseObj.SetResultStatus(NoTargetMethod)
|
||||
}
|
||||
|
||||
// 判断参数数量是否相同
|
||||
inTypesLength := len(methodAndInOutTypes.InTypes)
|
||||
paramLength := len(requestObj.Parameters)
|
||||
if paramLength != inTypesLength {
|
||||
logUtil.ErrorLog("传入的参数数量不符,本地方法%s的参数数量:%d,传入的参数数量为:%d", key, inTypesLength, paramLength)
|
||||
return responseObj.SetResultStatus(ParamNotMatch)
|
||||
}
|
||||
|
||||
// 构造参数
|
||||
in := make([]reflect.Value, inTypesLength)
|
||||
for i := 0; i < inTypesLength; i++ {
|
||||
inTypeItem := methodAndInOutTypes.InTypes[i]
|
||||
paramItem := requestObj.Parameters[i]
|
||||
|
||||
// 已支持类型:Client,Player(非基本类型)
|
||||
// 已支持类型:Bool,Int,Int8,Int16,Int32,Int64,Uint,Uint8,Uint16,Uint32,Uint64,Float32,Float64,String
|
||||
// 已支持类型:以及上面所列出类型的Slice类型
|
||||
// 未支持类型:Uintptr,Complex64,Complex128,Array,Chan,Func,Interface,Map,Ptr,Struct,UnsafePointer
|
||||
// 由于byte与int8同义,rune与int32同义,所以并不需要单独处理
|
||||
// 枚举参数的类型,并进行类型转换
|
||||
switch inTypeItem.Kind() {
|
||||
case reflect.Interface:
|
||||
if param_client, ok := paramItem.(IClient); ok {
|
||||
in[i] = reflect.ValueOf(param_client)
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if param_player, ok := paramItem.(*Player); ok {
|
||||
in[i] = reflect.ValueOf(param_player)
|
||||
}
|
||||
case reflect.Bool:
|
||||
if param_bool, ok := paramItem.(bool); ok {
|
||||
in[i] = reflect.ValueOf(param_bool)
|
||||
}
|
||||
case reflect.Int:
|
||||
if param_float64, ok := paramItem.(float64); ok {
|
||||
in[i] = reflect.ValueOf(int(param_float64))
|
||||
}
|
||||
case reflect.Int8:
|
||||
if param_float64, ok := paramItem.(float64); ok {
|
||||
in[i] = reflect.ValueOf(int8(param_float64))
|
||||
}
|
||||
case reflect.Int16:
|
||||
if param_float64, ok := paramItem.(float64); ok {
|
||||
in[i] = reflect.ValueOf(int16(param_float64))
|
||||
}
|
||||
case reflect.Int32:
|
||||
if param_float64, ok := paramItem.(float64); ok {
|
||||
in[i] = reflect.ValueOf(int32(param_float64))
|
||||
}
|
||||
case reflect.Int64:
|
||||
if param_float64, ok := paramItem.(float64); ok {
|
||||
in[i] = reflect.ValueOf(int64(param_float64))
|
||||
}
|
||||
case reflect.Uint:
|
||||
if param_float64, ok := paramItem.(float64); ok {
|
||||
in[i] = reflect.ValueOf(uint(param_float64))
|
||||
}
|
||||
case reflect.Uint8:
|
||||
if param_float64, ok := paramItem.(float64); ok {
|
||||
in[i] = reflect.ValueOf(uint8(param_float64))
|
||||
}
|
||||
case reflect.Uint16:
|
||||
if param_float64, ok := paramItem.(float64); ok {
|
||||
in[i] = reflect.ValueOf(uint16(param_float64))
|
||||
}
|
||||
case reflect.Uint32:
|
||||
if param_float64, ok := paramItem.(float64); ok {
|
||||
in[i] = reflect.ValueOf(uint32(param_float64))
|
||||
}
|
||||
case reflect.Uint64:
|
||||
if param_float64, ok := paramItem.(float64); ok {
|
||||
in[i] = reflect.ValueOf(uint64(param_float64))
|
||||
}
|
||||
case reflect.Float32:
|
||||
if param_float64, ok := paramItem.(float64); ok {
|
||||
in[i] = reflect.ValueOf(float32(param_float64))
|
||||
}
|
||||
case reflect.Float64:
|
||||
if param_float64, ok := paramItem.(float64); ok {
|
||||
in[i] = reflect.ValueOf(param_float64)
|
||||
}
|
||||
case reflect.String:
|
||||
if param_string, ok := paramItem.(string); ok {
|
||||
in[i] = reflect.ValueOf(param_string)
|
||||
}
|
||||
case reflect.Slice:
|
||||
// 如果是Slice类型,则需要对其中的项再次进行类型判断及类型转换
|
||||
if param_interface, ok := paramItem.([]interface{}); ok {
|
||||
switch inTypeItem.String() {
|
||||
case "[]bool":
|
||||
params_inner := make([]bool, len(param_interface))
|
||||
for i := 0; i < len(param_interface); i++ {
|
||||
if param_bool, ok := param_interface[i].(bool); ok {
|
||||
params_inner[i] = param_bool
|
||||
}
|
||||
}
|
||||
in[i] = reflect.ValueOf(params_inner)
|
||||
case "[]int":
|
||||
params_inner := make([]int, len(param_interface))
|
||||
for i := 0; i < len(param_interface); i++ {
|
||||
if param_float64, ok := param_interface[i].(float64); ok {
|
||||
params_inner[i] = int(param_float64)
|
||||
}
|
||||
}
|
||||
in[i] = reflect.ValueOf(params_inner)
|
||||
case "[]int8":
|
||||
params_inner := make([]int8, len(param_interface))
|
||||
for i := 0; i < len(param_interface); i++ {
|
||||
if param_float64, ok := param_interface[i].(float64); ok {
|
||||
params_inner[i] = int8(param_float64)
|
||||
}
|
||||
}
|
||||
in[i] = reflect.ValueOf(params_inner)
|
||||
case "[]int16":
|
||||
params_inner := make([]int16, len(param_interface))
|
||||
for i := 0; i < len(param_interface); i++ {
|
||||
if param_float64, ok := param_interface[i].(float64); ok {
|
||||
params_inner[i] = int16(param_float64)
|
||||
}
|
||||
}
|
||||
in[i] = reflect.ValueOf(params_inner)
|
||||
case "[]int32":
|
||||
params_inner := make([]int32, len(param_interface))
|
||||
for i := 0; i < len(param_interface); i++ {
|
||||
if param_float64, ok := param_interface[i].(float64); ok {
|
||||
params_inner[i] = int32(param_float64)
|
||||
}
|
||||
}
|
||||
in[i] = reflect.ValueOf(params_inner)
|
||||
case "[]int64":
|
||||
params_inner := make([]int64, len(param_interface))
|
||||
for i := 0; i < len(param_interface); i++ {
|
||||
if param_float64, ok := param_interface[i].(float64); ok {
|
||||
params_inner[i] = int64(param_float64)
|
||||
}
|
||||
}
|
||||
in[i] = reflect.ValueOf(params_inner)
|
||||
case "[]uint":
|
||||
params_inner := make([]uint, len(param_interface))
|
||||
for i := 0; i < len(param_interface); i++ {
|
||||
if param_float64, ok := param_interface[i].(float64); ok {
|
||||
params_inner[i] = uint(param_float64)
|
||||
}
|
||||
}
|
||||
in[i] = reflect.ValueOf(params_inner)
|
||||
// case "[]uint8": 特殊处理
|
||||
case "[]uint16":
|
||||
params_inner := make([]uint16, len(param_interface))
|
||||
for i := 0; i < len(param_interface); i++ {
|
||||
if param_float64, ok := param_interface[i].(float64); ok {
|
||||
params_inner[i] = uint16(param_float64)
|
||||
}
|
||||
}
|
||||
in[i] = reflect.ValueOf(params_inner)
|
||||
case "[]uint32":
|
||||
params_inner := make([]uint32, len(param_interface))
|
||||
for i := 0; i < len(param_interface); i++ {
|
||||
if param_float64, ok := param_interface[i].(float64); ok {
|
||||
params_inner[i] = uint32(param_float64)
|
||||
}
|
||||
}
|
||||
in[i] = reflect.ValueOf(params_inner)
|
||||
case "[]uint64":
|
||||
params_inner := make([]uint64, len(param_interface))
|
||||
for i := 0; i < len(param_interface); i++ {
|
||||
if param_float64, ok := param_interface[i].(float64); ok {
|
||||
params_inner[i] = uint64(param_float64)
|
||||
}
|
||||
}
|
||||
in[i] = reflect.ValueOf(params_inner)
|
||||
case "[]float32":
|
||||
params_inner := make([]float32, len(param_interface))
|
||||
for i := 0; i < len(param_interface); i++ {
|
||||
if param_float64, ok := param_interface[i].(float64); ok {
|
||||
params_inner[i] = float32(param_float64)
|
||||
}
|
||||
}
|
||||
in[i] = reflect.ValueOf(params_inner)
|
||||
case "[]float64":
|
||||
params_inner := make([]float64, len(param_interface))
|
||||
for i := 0; i < len(param_interface); i++ {
|
||||
if param_float64, ok := param_interface[i].(float64); ok {
|
||||
params_inner[i] = param_float64
|
||||
}
|
||||
}
|
||||
in[i] = reflect.ValueOf(params_inner)
|
||||
case "[]string":
|
||||
params_inner := make([]string, len(param_interface))
|
||||
for i := 0; i < len(param_interface); i++ {
|
||||
if param_string, ok := param_interface[i].(string); ok {
|
||||
params_inner[i] = param_string
|
||||
}
|
||||
}
|
||||
in[i] = reflect.ValueOf(params_inner)
|
||||
}
|
||||
} else if inTypeItem.String() == "[]uint8" { // 由于[]uint8在传输过程中会被转化成字符串,所以单独处理;
|
||||
if param_string, ok := paramItem.(string); ok {
|
||||
param_uint8 := ([]uint8)(param_string)
|
||||
in[i] = reflect.ValueOf(param_uint8)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否有无效的参数(传入的参数类型和方法定义的类型不匹配导致没有赋值)
|
||||
for _, item := range in {
|
||||
if reflect.Value.IsValid(item) == false {
|
||||
logUtil.ErrorLog("type:%v,value:%v.方法%s传入的参数%v无效", reflect.TypeOf(item), reflect.ValueOf(item), key, requestObj.Parameters)
|
||||
return responseObj.SetResultStatus(ParamInValid)
|
||||
}
|
||||
}
|
||||
|
||||
// 传入参数,调用方法
|
||||
out := methodAndInOutTypes.Method.Call(in)
|
||||
|
||||
// 由于只有一个返回值,所以取out[0]
|
||||
if retResponseObj, ok := (out[0]).Interface().(ServerResponseObject); ok {
|
||||
responseObj.SetMethodName(requestObj.MethodName)
|
||||
responseObj.SetResultStatus(retResponseObj.ResultStatus)
|
||||
responseObj.SetData(retResponseObj.Data)
|
||||
} else {
|
||||
logUtil.ErrorLog("(&out[0]).Interface()转换类型失败")
|
||||
}
|
||||
|
||||
return responseObj
|
||||
}
|
||||
101
trunk/game/common/config/baseConfig.go
Normal file
101
trunk/game/common/config/baseConfig.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"goutil/configUtil"
|
||||
"goutil/debugUtil"
|
||||
)
|
||||
|
||||
// 基础配置对象
|
||||
type BaseConfig struct {
|
||||
// ChatCenter监听地址
|
||||
ChatCenterAddress string
|
||||
|
||||
// ManageCenter的API的域名地址
|
||||
ManageCenterDomain string
|
||||
|
||||
// 通信协议tcp/websocket
|
||||
Protocol string
|
||||
|
||||
// 内网地址
|
||||
PrivateIP string
|
||||
|
||||
// 公网地址
|
||||
PublicIP string
|
||||
|
||||
// 为ChatServer提价服务的端口
|
||||
ChatServerPort string
|
||||
|
||||
// 为GameServer提供服务的端口
|
||||
GameServerPort string
|
||||
|
||||
// 为GameServer提供服务的Web端口
|
||||
GameServerWebPort string
|
||||
|
||||
// 是否压缩返回给客户端的数据
|
||||
IfCompressData bool
|
||||
|
||||
// GoPs监控程序监听地址
|
||||
GopsAddr string
|
||||
}
|
||||
|
||||
func (this *BaseConfig) GetPrivateChatServerAddress() string {
|
||||
return fmt.Sprintf("%s:%s", this.PrivateIP, this.ChatServerPort)
|
||||
}
|
||||
|
||||
func (this *BaseConfig) GetPrivateGameServerAddress() string {
|
||||
return fmt.Sprintf("%s:%s", this.PrivateIP, this.GameServerPort)
|
||||
}
|
||||
|
||||
func (this *BaseConfig) GetPrivateGameServerWebAddress() string {
|
||||
return fmt.Sprintf("%s:%s", this.PrivateIP, this.GameServerWebPort)
|
||||
}
|
||||
|
||||
func (this *BaseConfig) GetPrivateGopsAddress() string {
|
||||
return fmt.Sprintf("%s:%s", this.PrivateIP, this.GopsAddr)
|
||||
}
|
||||
|
||||
func (this *BaseConfig) GetPublicChatServerAddress() string {
|
||||
return fmt.Sprintf("%s:%s", this.PublicIP, this.ChatServerPort)
|
||||
}
|
||||
|
||||
func (this *BaseConfig) GetPublicGameServerAddress() string {
|
||||
return fmt.Sprintf("%s:%s", this.PublicIP, this.GameServerPort)
|
||||
}
|
||||
|
||||
func (this *BaseConfig) GetPublicGameServerWebAddress() string {
|
||||
return fmt.Sprintf("http://%s:%s/API/player/login", this.PublicIP, this.GameServerWebPort)
|
||||
}
|
||||
|
||||
func (this *BaseConfig) String() string {
|
||||
bytes, _ := json.Marshal(this)
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
var (
|
||||
baseConfig *BaseConfig
|
||||
)
|
||||
|
||||
func initBaseConfig(config *configUtil.XmlConfig) error {
|
||||
tempConfig := new(BaseConfig)
|
||||
err := config.Unmarshal("root/BaseConfig", tempConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
baseConfig = tempConfig
|
||||
debugUtil.Printf("baseConfig:%v\n", baseConfig)
|
||||
|
||||
if baseConfig.Protocol != "tcp" && baseConfig.Protocol != "websocket" {
|
||||
panic("Protocol Error, it should be either tcp or websocket")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBaseConfig 获取服务器基础配置
|
||||
func GetBaseConfig() *BaseConfig {
|
||||
return baseConfig
|
||||
}
|
||||
56
trunk/game/common/config/config.go
Normal file
56
trunk/game/common/config/config.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"goutil/configUtil"
|
||||
"goutil/debugUtil"
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
configManager = configMgr.NewConfigManager()
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 优先加基础配置
|
||||
configManager.RegisterInitFunc(initBaseConfig)
|
||||
configManager.RegisterInitFunc(initDBConfig)
|
||||
configManager.RegisterInitFunc(initMonitorConfig)
|
||||
}
|
||||
|
||||
func init() {
|
||||
// 设置日志文件的存储目录
|
||||
logUtil.SetLogPath("LOG")
|
||||
|
||||
if err := reload(); err != nil {
|
||||
panic(fmt.Errorf("初始化配置文件失败,错误信息为:%s", err))
|
||||
}
|
||||
|
||||
// 注册重新加载的方法
|
||||
reloadMgr.RegisterReloadFunc("config.reload", reload)
|
||||
}
|
||||
|
||||
func reload() error {
|
||||
// 读取配置文件内容
|
||||
configObj := configUtil.NewXmlConfig()
|
||||
err := configObj.LoadFromFile("config.xml")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
debug, err := configObj.Bool("root/DEBUG", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 设置debugUtil的状态
|
||||
debugUtil.SetDebug(debug)
|
||||
|
||||
// 调用所有已经注册的配置初始化方法
|
||||
if err := configManager.Init(configObj); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
29
trunk/game/common/config/dbConfig.go
Normal file
29
trunk/game/common/config/dbConfig.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"goutil/configUtil"
|
||||
"goutil/debugUtil"
|
||||
"goutil/mysqlUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
dbConfig *mysqlUtil.DBConfig
|
||||
)
|
||||
|
||||
func initDBConfig(config *configUtil.XmlConfig) error {
|
||||
tempConfig := new(mysqlUtil.DBConfig)
|
||||
err := config.Unmarshal("root/DBConnection", tempConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dbConfig = tempConfig
|
||||
debugUtil.Printf("dbConfig:%v\n", dbConfig)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDBConfig 获取mysql数据库配置
|
||||
func GetDBConfig() *mysqlUtil.DBConfig {
|
||||
return dbConfig
|
||||
}
|
||||
27
trunk/game/common/config/monitorConfig.go
Normal file
27
trunk/game/common/config/monitorConfig.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"goutil/configUtil"
|
||||
"goutil/debugUtil"
|
||||
)
|
||||
|
||||
var (
|
||||
monitorConfig *monitorMgr.MonitorConfig
|
||||
)
|
||||
|
||||
func initMonitorConfig(config *configUtil.XmlConfig) error {
|
||||
tempConfig := new(monitorMgr.MonitorConfig)
|
||||
err := config.Unmarshal("root/MonitorConfig", tempConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
monitorConfig = tempConfig
|
||||
debugUtil.Printf("monitorConfig:%v\n", monitorConfig)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMonitorConfig 获监测配置
|
||||
func GetMonitorConfig() *monitorMgr.MonitorConfig {
|
||||
return monitorConfig
|
||||
}
|
||||
13
trunk/game/common/go.mod
Normal file
13
trunk/game/common/go.mod
Normal file
@@ -0,0 +1,13 @@
|
||||
module common
|
||||
|
||||
go 1.22.10
|
||||
|
||||
replace (
|
||||
framework => ../../framework
|
||||
goutil => ../../goutil
|
||||
)
|
||||
|
||||
require (
|
||||
framework v0.0.0-20230425160006-b2d0b0a0b0b0
|
||||
goutil v0.0.0-20230425160006-b2d0b0a0b0b0
|
||||
)
|
||||
6
trunk/game/common/go.sum
Normal file
6
trunk/game/common/go.sum
Normal file
@@ -0,0 +1,6 @@
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
63
trunk/game/common/model/resultStatus.go
Normal file
63
trunk/game/common/model/resultStatus.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package model
|
||||
|
||||
// 返回结果状态
|
||||
type ResultStatus struct {
|
||||
// 状态值(成功是0,非成功以负数来表示)
|
||||
Code int
|
||||
|
||||
// 状态信息
|
||||
Message string
|
||||
}
|
||||
|
||||
func newResultStatus(code int, message string) *ResultStatus {
|
||||
return &ResultStatus{
|
||||
Code: code,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// 定义所有的响应结果的状态枚举值
|
||||
var (
|
||||
Success = newResultStatus(0, "成功")
|
||||
DataError = newResultStatus(-1, "数据错误")
|
||||
DBError = newResultStatus(-2, "数据库错误")
|
||||
MethodNotDefined = newResultStatus(-3, "方法未定义")
|
||||
ParamIsEmpty = newResultStatus(-4, "参数为空")
|
||||
ParamNotMatch = newResultStatus(-5, "参数不匹配")
|
||||
ParamTypeError = newResultStatus(-6, "参数类型错误")
|
||||
OnlySupportPOST = newResultStatus(-7, "只支持POST")
|
||||
APINotDefined = newResultStatus(-8, "API未定义")
|
||||
APIParamError = newResultStatus(-9, "API参数错误")
|
||||
InvalidIP = newResultStatus(-10, "IP无效")
|
||||
PlayerNotExists = newResultStatus(-11, "玩家不存在")
|
||||
NoAvailableServer = newResultStatus(-12, "没有可用的服务器")
|
||||
ClientDataError = newResultStatus(-13, "客户端数据错误")
|
||||
TokenInvalid = newResultStatus(-14, "令牌无效")
|
||||
ChannelNotDefined = newResultStatus(-15, "聊天频道未定义")
|
||||
NoTargetMethod = newResultStatus(-16, "找不到目标方法")
|
||||
ParamInValid = newResultStatus(-17, "参数无效")
|
||||
NoLogin = newResultStatus(-18, "尚未登陆")
|
||||
NotInUnion = newResultStatus(-19, "不在公会中")
|
||||
NotInShimen = newResultStatus(-20, "不在师门中")
|
||||
NotFoundTarget = newResultStatus(-21, "未找到目标玩家")
|
||||
PlayerNotExist = newResultStatus(-22, "玩家不存在")
|
||||
ServerGroupNotExist = newResultStatus(-23, "服务器组不存在")
|
||||
NotInTeam = newResultStatus(-24, "不在队伍中")
|
||||
LoginOnAnotherDevice = newResultStatus(-25, "在另一台设备上登录")
|
||||
CantSendMessageToSelf = newResultStatus(-26, "不能给自己发消息")
|
||||
ResourceNotEnough = newResultStatus(-27, "资源不足")
|
||||
NetworkError = newResultStatus(-28, "网络错误")
|
||||
ContainForbiddenWord = newResultStatus(-29, "含有屏蔽词语")
|
||||
SendMessageTooFast = newResultStatus(-30, "发送消息太快")
|
||||
LvIsNotEnough = newResultStatus(-31, "等级不足,系统未开放")
|
||||
RepeatTooMuch = newResultStatus(-32, "重复次数太多")
|
||||
CantCrossServerTalk = newResultStatus(-33, "不能跨服私聊")
|
||||
InSilent = newResultStatus(-34, "您的账号已被禁言,请联系客服反馈。")
|
||||
NotInCountry = newResultStatus(-35, "不在国家中")
|
||||
CantSendMessageToGM = newResultStatus(-36, "当前GM对话已结束,如有疑问请联系客服QQ咨询")
|
||||
DisconnectStatus = newResultStatus(-37, "DisconnectStatus")
|
||||
LanguageToTextError = newResultStatus(-38, "语音转换失败!")
|
||||
VoiceCloudNotDefined = newResultStatus(-15, "语音识别云商不存在")
|
||||
ContainForbiddenWordForQCGreen = newResultStatus(-39, "内容含有屏蔽词语")
|
||||
ContainForbiddenWordForZSYDun = newResultStatus(-59, "内容中含有屏蔽词语")
|
||||
)
|
||||
29
trunk/game/common/model/serverRequestObject.go
Normal file
29
trunk/game/common/model/serverRequestObject.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package model
|
||||
|
||||
// 请求对象
|
||||
type ServerRequestObject struct {
|
||||
// 请求的唯一标识,是需要通过截取请求数据前4位得到并进行手动赋值的(暂时未使用)
|
||||
RequestId int64
|
||||
|
||||
// 请求的模块名称
|
||||
ModuleName string
|
||||
|
||||
// 请求的方法名称
|
||||
MethodName string
|
||||
|
||||
// 请求的参数数组
|
||||
Parameters []interface{}
|
||||
|
||||
// 客户端发送请求的时间
|
||||
SendTime int64
|
||||
|
||||
ReachTime int64
|
||||
|
||||
InQueueTime int64
|
||||
|
||||
HandleStartTime int64
|
||||
|
||||
HandleEndTime int64
|
||||
|
||||
ReturnTime int64
|
||||
}
|
||||
89
trunk/game/common/model/serverResponseData.go
Normal file
89
trunk/game/common/model/serverResponseData.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 服务器响应对象
|
||||
type ServerResponseData struct {
|
||||
// Id
|
||||
Id int
|
||||
|
||||
// 聊天频道
|
||||
Channel string
|
||||
|
||||
// 聊天消息
|
||||
Message string
|
||||
|
||||
// 语音信息
|
||||
Voice string
|
||||
|
||||
// 语音文字
|
||||
VoiceText string
|
||||
|
||||
// 发送人
|
||||
FromPlayer string
|
||||
FromPlayerId string
|
||||
|
||||
// 接收人
|
||||
ToPlayer string `json:"ToPlayer,omitempty"`
|
||||
ToPlayerId string `json:"ToPlayerId,omitempty"`
|
||||
|
||||
// 创建的时间戳
|
||||
TimeStamp int64
|
||||
|
||||
//是否是离线消息
|
||||
IfOffline bool
|
||||
|
||||
//是否已读消息
|
||||
IfRead bool
|
||||
}
|
||||
|
||||
// 创建新的服务器响应对象
|
||||
func NewServerResponseData(id int, channel, message, voice, voiceText string, from, to *Player, ifOffline, ifRead int32) *ServerResponseData {
|
||||
var fromPlayer, toPlayer string
|
||||
var fromPlayerId, toPlayerId int64
|
||||
if from != nil {
|
||||
fromPlayerId = from.Id
|
||||
fromPlayer = from.String()
|
||||
}
|
||||
|
||||
if to != nil {
|
||||
toPlayerId = to.Id
|
||||
toPlayer = to.String()
|
||||
}
|
||||
|
||||
return &ServerResponseData{
|
||||
Id: id,
|
||||
Channel: channel,
|
||||
Message: message,
|
||||
Voice: voice,
|
||||
VoiceText: voiceText,
|
||||
FromPlayer: fromPlayer,
|
||||
FromPlayerId: strconv.FormatInt(fromPlayerId, 10),
|
||||
ToPlayer: toPlayer,
|
||||
ToPlayerId: strconv.FormatInt(toPlayerId, 10),
|
||||
TimeStamp: time.Now().Unix(),
|
||||
IfOffline: ifOffline == 1,
|
||||
IfRead: ifRead == 1,
|
||||
}
|
||||
}
|
||||
|
||||
// 从其它类型转化为服务器响应对象
|
||||
func ConvertToServerResponseData(id int, channel, message, voice, voiceText, fromPlayer string, fromPlayerId int64, toPlayer string, toPlayerId int64, timeStamp int64, ifOffline, ifRead bool) *ServerResponseData {
|
||||
return &ServerResponseData{
|
||||
Id: id,
|
||||
Channel: channel,
|
||||
Message: message,
|
||||
Voice: voice,
|
||||
VoiceText: voiceText,
|
||||
FromPlayer: fromPlayer,
|
||||
FromPlayerId: strconv.FormatInt(fromPlayerId, 10),
|
||||
ToPlayer: toPlayer,
|
||||
ToPlayerId: strconv.FormatInt(toPlayerId, 10),
|
||||
TimeStamp: timeStamp,
|
||||
IfOffline: ifOffline,
|
||||
IfRead: ifRead,
|
||||
}
|
||||
}
|
||||
80
trunk/game/common/model/serverResponseObject.go
Normal file
80
trunk/game/common/model/serverResponseObject.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"compress/zlib"
|
||||
"encoding/json"
|
||||
"goutil/zlibUtil"
|
||||
)
|
||||
|
||||
// ChatServer的响应对象
|
||||
type ServerResponseObject struct {
|
||||
// 响应结果的状态值
|
||||
*ResultStatus
|
||||
|
||||
// 响应结果的数据
|
||||
Data interface{} `json:"Data,omitempty"`
|
||||
|
||||
// 响应结果对应的请求的方法名称
|
||||
MethodName string
|
||||
|
||||
// 压缩后的字节
|
||||
DataByte []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (this *ServerResponseObject) SetResultStatus(rs *ResultStatus) *ServerResponseObject {
|
||||
this.ResultStatus = rs
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *ServerResponseObject) SetData(data interface{}) *ServerResponseObject {
|
||||
this.Data = data
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *ServerResponseObject) SetMethodName(methodName string) *ServerResponseObject {
|
||||
this.MethodName = methodName
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *ServerResponseObject) IsDisconnect() bool {
|
||||
return this.ResultStatus.Code == DisconnectStatus.Code
|
||||
}
|
||||
|
||||
// Compress
|
||||
//
|
||||
// @Description: 压缩数据
|
||||
// parameter:
|
||||
// receiver this
|
||||
// ifCompress
|
||||
// return:
|
||||
func (this *ServerResponseObject) Compress(ifCompress bool) {
|
||||
// 序列化发送的数据
|
||||
contentObj, _ := json.Marshal(this)
|
||||
|
||||
// 进行zlib压缩
|
||||
if ifCompress {
|
||||
contentObj, _ = zlibUtil.Compress(contentObj, zlib.DefaultCompression)
|
||||
}
|
||||
|
||||
//赋值
|
||||
this.DataByte = contentObj
|
||||
}
|
||||
|
||||
func NewServerResponseObject() *ServerResponseObject {
|
||||
return &ServerResponseObject{
|
||||
ResultStatus: Success,
|
||||
Data: nil,
|
||||
MethodName: "",
|
||||
}
|
||||
}
|
||||
|
||||
func NewDisconnectServerResponseObject() *ServerResponseObject {
|
||||
return &ServerResponseObject{
|
||||
ResultStatus: DisconnectStatus,
|
||||
Data: nil,
|
||||
MethodName: "",
|
||||
}
|
||||
}
|
||||
176
trunk/game/common/server_http/context.go
Normal file
176
trunk/game/common/server_http/context.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package server_http
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"framework/ipMgr"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
. "common/model"
|
||||
"goutil/debugUtil"
|
||||
"goutil/logUtil"
|
||||
"goutil/webUtil"
|
||||
"goutil/zlibUtil"
|
||||
)
|
||||
|
||||
// 请求上下文对象
|
||||
type Context struct {
|
||||
// 请求对象
|
||||
*http.Request
|
||||
|
||||
// 应答写对象
|
||||
http.ResponseWriter
|
||||
|
||||
// 请求数据
|
||||
requestBytes []byte
|
||||
|
||||
// 数据是否已经解析数据
|
||||
ifDataParsed bool
|
||||
|
||||
// Form的数据是否已经解析
|
||||
ifFormParsed bool
|
||||
}
|
||||
|
||||
// 检查IP是否合法
|
||||
func (this *Context) checkIP() *ResultStatus {
|
||||
if debugUtil.IsDebug() == false && ipMgr.IsIpValid(webUtil.GetRequestIP(this.Request)) == false {
|
||||
return InvalidIP
|
||||
}
|
||||
|
||||
return Success
|
||||
}
|
||||
|
||||
func (this *Context) GetFormValue(key string) (value string, exists bool) {
|
||||
defer func() {
|
||||
this.ifFormParsed = true
|
||||
}()
|
||||
|
||||
if !this.ifFormParsed {
|
||||
this.Request.ParseForm()
|
||||
}
|
||||
|
||||
values := this.Form[key]
|
||||
if values != nil && len(values) > 0 {
|
||||
value = values[0]
|
||||
exists = true
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 转换内容
|
||||
func (this *Context) parseContent() error {
|
||||
defer func() {
|
||||
this.Body.Close()
|
||||
this.ifDataParsed = true
|
||||
}()
|
||||
|
||||
data, err := ioutil.ReadAll(this.Body)
|
||||
if err != nil {
|
||||
logUtil.ErrorLog("url:%s,读取数据出错,错误信息为:%s", this.RequestURI, err)
|
||||
return err
|
||||
}
|
||||
|
||||
this.requestBytes = data
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取请求字节数据
|
||||
// 返回值:
|
||||
// []byte:请求字节数组
|
||||
// error:错误信息
|
||||
func (this *Context) GetRequestBytes(isCompressed bool) (result []byte, exists bool, err error) {
|
||||
if this.ifDataParsed == false {
|
||||
this.parseContent()
|
||||
}
|
||||
|
||||
data := this.requestBytes
|
||||
if data == nil || len(data) <= 0 {
|
||||
return
|
||||
} else {
|
||||
exists = true
|
||||
}
|
||||
|
||||
if isCompressed {
|
||||
result, err = zlibUtil.Decompress(data)
|
||||
if err != nil {
|
||||
logUtil.ErrorLog("解压缩请求数据失败:%s", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
result = data
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 获取请求字符串数据
|
||||
// 返回值:
|
||||
// 请求字符串数据
|
||||
func (this *Context) GetRequestString(isCompressed bool) (result string, exists bool, err error) {
|
||||
var data []byte
|
||||
data, exists, err = this.GetRequestBytes(isCompressed)
|
||||
if err != nil || !exists {
|
||||
return
|
||||
}
|
||||
|
||||
result = string(data)
|
||||
exists = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Unmarshal 反序列化
|
||||
// moduleName:模块名称
|
||||
// obj:反序列化结果数据
|
||||
// isCompressed:数据是否已经被压缩
|
||||
// 返回值:
|
||||
// 错误对象
|
||||
func (this *Context) Unmarshal(moduleName string, obj interface{}, isCompressed bool) (exists bool, err error) {
|
||||
var data []byte
|
||||
data, exists, err = this.GetRequestBytes(isCompressed)
|
||||
if err != nil || !exists {
|
||||
return
|
||||
}
|
||||
|
||||
// 反序列化
|
||||
if err = json.Unmarshal(data, &obj); err != nil {
|
||||
logUtil.ErrorLog("Module:%s, 反序列化%s出错,错误信息为:%s", moduleName, string(data), err)
|
||||
return
|
||||
}
|
||||
|
||||
debugUtil.Printf("接收到GM登录数据data:%v", string(data))
|
||||
return
|
||||
}
|
||||
|
||||
// 输出字符串
|
||||
func (this *Context) WriteString(result string) {
|
||||
this.ResponseWriter.Write([]byte(result))
|
||||
}
|
||||
|
||||
// 输出json数据
|
||||
func (this *Context) WriteJson(result interface{}) {
|
||||
if bytes, err := json.Marshal(result); err == nil {
|
||||
this.ResponseWriter.Write(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转到其它页面
|
||||
func (this *Context) RedirectTo(url string) {
|
||||
this.ResponseWriter.Header().Set("Location", url)
|
||||
this.ResponseWriter.WriteHeader(301)
|
||||
}
|
||||
|
||||
// 新建API上下文对象
|
||||
// request:请求对象
|
||||
// responseWriter:应答写对象
|
||||
// 返回值:
|
||||
// *Context:上下文
|
||||
func newContext(request *http.Request, responseWriter http.ResponseWriter) *Context {
|
||||
return &Context{
|
||||
Request: request,
|
||||
ResponseWriter: responseWriter,
|
||||
}
|
||||
}
|
||||
37
trunk/game/common/server_http/handlerMgr.go
Normal file
37
trunk/game/common/server_http/handlerMgr.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package server_http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
. "common/model"
|
||||
)
|
||||
|
||||
type Handler func(*Context) *ServerResponseObject
|
||||
|
||||
var (
|
||||
// 处理函数集合
|
||||
handlerMap map[string]Handler
|
||||
)
|
||||
|
||||
func init() {
|
||||
handlerMap = make(map[string]Handler, 8)
|
||||
}
|
||||
|
||||
// RegisterHandler 详细的注册一个WebAPI处理函数
|
||||
// pattern:路由地址
|
||||
// handler:处理函数
|
||||
// paramInfo:参数列表
|
||||
func RegisterHandler(pattern string, handler Handler) {
|
||||
if _, exist := handlerMap[pattern]; exist {
|
||||
panic(fmt.Errorf("存在重复的webapi注册:%s", pattern))
|
||||
}
|
||||
|
||||
// 添加处理对象
|
||||
handlerMap[pattern] = handler
|
||||
}
|
||||
|
||||
// 获取处理函数
|
||||
func getHandler(pattern string) (handler Handler, exists bool) {
|
||||
handler, exists = handlerMap[pattern]
|
||||
return
|
||||
}
|
||||
71
trunk/game/common/server_http/server.go
Normal file
71
trunk/game/common/server_http/server.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package server_http
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
. "common/model"
|
||||
"goutil/debugUtil"
|
||||
"goutil/logUtil"
|
||||
"goutil/webUtil"
|
||||
)
|
||||
|
||||
// http服务对象
|
||||
type httpServer struct{}
|
||||
|
||||
// http应答处理
|
||||
// response:应答对象
|
||||
// request:请求对象
|
||||
func (this *httpServer) ServeHTTP(response http.ResponseWriter, request *http.Request) {
|
||||
context := newContext(request, response)
|
||||
responseObj := NewServerResponseObject()
|
||||
|
||||
defer func() {
|
||||
if data := recover(); data != nil {
|
||||
logUtil.LogUnknownError(data)
|
||||
return
|
||||
}
|
||||
|
||||
// 特殊路径进行特别处理
|
||||
if request.URL.Path == "/" || request.URL.Path == "/favicon.ico" {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取输入参数的字符串形式
|
||||
parameter := ""
|
||||
if len(request.Form) > 0 {
|
||||
parameter_byte, _ := json.Marshal(request.Form)
|
||||
parameter = string(parameter_byte)
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
if debugUtil.IsDebug() || responseObj.ResultStatus != Success {
|
||||
result, _ := json.Marshal(responseObj)
|
||||
logUtil.DebugLog("%s-->IP:%s;Request:%v;Response:%s;", request.URL.Path, webUtil.GetRequestIP(request), parameter, string(result))
|
||||
}
|
||||
}()
|
||||
|
||||
// 特殊路径进行特别处理
|
||||
if request.URL.Path == "/" || request.URL.Path == "/favicon.ico" {
|
||||
context.WriteString("ok")
|
||||
return
|
||||
}
|
||||
|
||||
// 验证IP
|
||||
if rs := context.checkIP(); rs != Success {
|
||||
context.WriteJson(responseObj.SetResultStatus(rs))
|
||||
return
|
||||
}
|
||||
|
||||
var handler Handler
|
||||
var exists bool
|
||||
if handler, exists = getHandler(request.URL.Path); !exists {
|
||||
logUtil.ErrorLog("访问的页面不存在,RequestAddr: %s request.URL.Path: %s", request.RemoteAddr, request.URL.Path)
|
||||
http.Error(response, "访问的页面不存在", 404)
|
||||
return
|
||||
}
|
||||
|
||||
// 调用处理方法,并返回结果
|
||||
responseObj = handler(context)
|
||||
context.WriteJson(responseObj)
|
||||
}
|
||||
44
trunk/game/common/server_http/start.go
Normal file
44
trunk/game/common/server_http/start.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package server_http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// 启动Web服务器
|
||||
// wg:WaitGroup对象
|
||||
// address:服务器地址
|
||||
func Start(wg *sync.WaitGroup, address string) {
|
||||
defer func() {
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
//// 开启服务
|
||||
//serverInstance := http.Server{
|
||||
// Addr: address,
|
||||
// Handler: new(httpServer),
|
||||
//}
|
||||
|
||||
//// 开启监听
|
||||
//msg := fmt.Sprintf("server_http begins to listen on: %s...", address)
|
||||
//fmt.Println(msg)
|
||||
//logUtil.InfoLog(msg)
|
||||
|
||||
// 启动Web服务器监听
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/", &httpServer{})
|
||||
mux.HandleFunc("/debug/pprof/", pprof.Index)
|
||||
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
||||
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||
|
||||
if err := http.ListenAndServe(address, mux); err != nil {
|
||||
panic(fmt.Sprintf("server_http ListenAndServe Error:%v", err))
|
||||
}
|
||||
//if err := serverInstance.ListenAndServe(); err != nil {
|
||||
// panic(fmt.Sprintf("server_http ListenAndServe Error:%v", err))
|
||||
//}
|
||||
}
|
||||
347
trunk/game/common/server_tcp/client.go
Normal file
347
trunk/game/common/server_tcp/client.go
Normal file
@@ -0,0 +1,347 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
55
trunk/game/common/server_tcp/start.go
Normal file
55
trunk/game/common/server_tcp/start.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package server_tcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"framework/goroutineMgr"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"goutil/debugUtil"
|
||||
|
||||
"common/clientMgr"
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
// Start 启动服务器
|
||||
func Start(wg *sync.WaitGroup, address string) {
|
||||
defer func() {
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
// 处理goroutine数量
|
||||
goroutineName := "server_tcp.Start"
|
||||
goroutineMgr.Monitor(goroutineName)
|
||||
defer goroutineMgr.ReleaseMonitor(goroutineName)
|
||||
|
||||
msg := fmt.Sprintf("server_tcp begins to listen on:%s...", address)
|
||||
fmt.Println(msg)
|
||||
logUtil.InfoLog(msg)
|
||||
|
||||
// 监听指定的端口
|
||||
listener, err := net.Listen("tcp", address)
|
||||
|
||||
//debug模式下打印日志
|
||||
if debugUtil.IsDebug() {
|
||||
logUtil.DebugLog(fmt.Sprintf("收到客户的连接请求,ip:%v", listener.Addr()))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("server_tcp listen Error: %s", err))
|
||||
}
|
||||
|
||||
for {
|
||||
// 阻塞直至新连接到来
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
logUtil.ErrorLog("server_tcp accept error: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 创建客户端对象
|
||||
clientObj := newClient(conn)
|
||||
clientObj.start()
|
||||
clientMgr.RegisterClient(clientObj)
|
||||
}
|
||||
}
|
||||
302
trunk/game/common/server_webSocket/client.go
Normal file
302
trunk/game/common/server_webSocket/client.go
Normal file
@@ -0,0 +1,302 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
63
trunk/game/common/server_webSocket/start.go
Normal file
63
trunk/game/common/server_webSocket/start.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package server_webSocket
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"common/clientMgr"
|
||||
"github.com/gorilla/websocket"
|
||||
"goutil/debugUtil"
|
||||
"goutil/logUtil"
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
func handleConn(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
|
||||
//是否debug模式
|
||||
if debugUtil.IsDebug() {
|
||||
logUtil.DebugLog(fmt.Sprintf("收到客户的websockte连接,ip%v", conn.RemoteAddr()))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logUtil.ErrorLog("websocket.handleConn获取连接出错,err:%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建客户端对象
|
||||
clientObj := newClient(conn)
|
||||
clientObj.start()
|
||||
clientMgr.RegisterClient(clientObj)
|
||||
|
||||
debugUtil.Printf("收到连接请求:remoteAdd:%s\n", conn.RemoteAddr())
|
||||
}
|
||||
|
||||
// Start 启动服务器
|
||||
func Start(wg *sync.WaitGroup, address string, isUseTSL bool) {
|
||||
defer wg.Done()
|
||||
|
||||
msg := fmt.Sprintf("server_websocket begins to listen on:%s...", address)
|
||||
fmt.Println(msg)
|
||||
logUtil.InfoLog(msg)
|
||||
|
||||
http.HandleFunc("/", handleConn)
|
||||
if isUseTSL {
|
||||
err := http.ListenAndServeTLS(address, "tlsFile/7qule.com.pem", "tlsFile/7qule.com.key", nil)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("server_websocket.ListenAndServeTLS, err:%v", err))
|
||||
}
|
||||
} else {
|
||||
err := http.ListenAndServe(address, nil)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("server_websocket.ListenAndServe, err:%v", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user