Example #1
0
// 启动服务器
// wg:WaitGroup
// serverAddress:服务器监听地址
// getPlayerFunc:获取玩家对象的方法
func StartServer(wg *sync.WaitGroup, serverAddress string, getPlayerFunc func(*Client, string) (*player.Player, ResultStatus)) {
	defer func() {
		wg.Done()
	}()

	// 设置配置参数
	SetRPCParam(serverAddress, getPlayerFunc)

	logUtil.Log("Socket服务器开始监听...", logUtil.Info, true)

	// 监听指定的端口
	listener, err := net.Listen("tcp", ServerAddress())
	if err != nil {
		panic(fmt.Errorf("Listen Error: %s", err))
	} else {
		msg := fmt.Sprintf("Got listener for client. (local address: %s)", listener.Addr())

		// 记录和显示日志,并且判断是否需要退出
		logUtil.Log(msg, logUtil.Info, true)
		fmt.Println(msg)
	}

	for {
		// 阻塞直至新连接到来
		conn, err := listener.Accept()
		if err != nil {
			logUtil.Log(fmt.Sprintf("Accept Error: %s", err), logUtil.Error, true)
			continue
		}

		// 启动一个新协程来处理链接(每个客户端对应一个协程)
		go handleConn(conn)
	}
}
Example #2
0
// 启动服务器
// wg:WaitGroup
func StartServer(wg *sync.WaitGroup) {
	defer func() {
		wg.Done()
	}()

	logUtil.Log("Socket服务器开始监听...", logUtil.Info, true)

	// 监听指定的端口
	msg := ""
	listener, err := net.Listen("tcp", ServerAddress())
	if err != nil {
		msg = fmt.Sprintf("Listen Error: %s", err)
	} else {
		msg = fmt.Sprintf("Got listener for the server. (local address: %s)", listener.Addr())
	}

	// 记录和显示日志,并且判断是否需要退出
	logUtil.Log(msg, logUtil.Info, true)
	fmt.Println(msg)
	if err != nil {
		return
	}

	for {
		// 阻塞直至新连接到来
		conn, err := listener.Accept()
		if err != nil {
			logUtil.Log(fmt.Sprintf("Accept Error: %s", err), logUtil.Error, true)
			continue
		}

		// 启动一个新协程来处理链接
		go handleConn(conn)
	}
}
Example #3
0
// 将通道里面的所有内容写入到文件中
func SaveToFile() {
	logUtil.Log(fmt.Sprintf("通道中尚未被处理的数据量为:%d", len(dataChannel)), logUtil.Debug, true)

	// 组装数据到dataSlice中,并保存到日志文件
	dataSlice := make([]string, 0, 1024)

	logUtil.Log("开始取通道中的数据", logUtil.Debug, true)
	if len(dataChannel) > 0 {
	Outer:
		for {
			select {
			case data := <-dataChannel:
				dataSlice = append(dataSlice, data)
				dataSlice = append(dataSlice, stringUtil.GetNewLineString())
			default:
				break Outer
			}
		}
	}
	logUtil.Log("取通道中的数据结束", logUtil.Debug, true)

	logUtil.Log("开始保存数据到文件", logUtil.Debug, true)
	if len(dataSlice) > 0 {
		fileUtil.WriteFile(config.SAVE_DATA_PATH, fmt.Sprintf("%s.sql", timeUtil.Format(time.Now(), "yyyy-MM-dd")), true, dataSlice...)
	}
	logUtil.Log("保存数据到文件结束", logUtil.Debug, true)
}
Example #4
0
// 启动客户端
func startClient(ch chan int, loginSucceedCh chan int, currId int) {
	// 处理内部未处理的异常,以免导致主线程退出,从而导致系统崩溃
	defer func() {
		if r := recover(); r != nil {
			logUtil.Log(fmt.Sprintf("通过recover捕捉到的未处理异常:%v", r), logUtil.Error, true)
		}
	}()

	conn, err := net.DialTimeout("tcp", ServerAddress, 5*time.Second)
	if err != nil {
		fmt.Printf("第%d个客户端启动失败,请检查\n", currId)
		ch <- currId
		return
	}

	defer func() {
		conn.Close()
		ch <- currId
	}()

	// 创建客户端对象,并添加到列表中
	clientObj := client.NewClient(&conn, conn)
	ClientList[clientObj.Id] = false

	// 登陆
	go login(clientObj, currId)

	// 发送心跳包
	go heartBeat(clientObj, loginSucceedCh)

	// 死循环,不断地读取数据,解析数据,发送数据
	for {
		// 先读取数据,每次读取1024个字节
		readBytes := make([]byte, 1024)
		n, err := conn.Read(readBytes)
		if err != nil {
			var errMsg string

			// 判断是连接关闭错误,还是普通错误
			if err == io.EOF {
				errMsg = fmt.Sprintf("另一端关闭了连接:%s", err)
			} else {
				errMsg = fmt.Sprintf("读取数据错误:%s", err)
			}

			logUtil.Log(errMsg, logUtil.Error, true)

			break
		}

		// 将读取到的数据追加到已获得的数据的末尾
		clientObj.AppendContent(readBytes[:n])

		// 已经包含有效的数据,处理该数据
		handleClient(clientObj, loginSucceedCh)
	}
}
Example #5
0
// 启动客户端
func startClient(ch chan int, clientCh chan *client.Client, loginSucceedCh chan int) {
	// 处理内部未处理的异常,以免导致主线程退出,从而导致系统崩溃
	defer func() {
		if r := recover(); r != nil {
			logUtil.Log(fmt.Sprintf("通过recover捕捉到的未处理异常:%v", r), logUtil.Error, true)
		}
	}()

	conn, err := net.DialTimeout(SERVER_NETWORK, ServerAddress, 2*time.Second)
	if err != nil {
		fmt.Printf("Dial Error: %s\n", err)
		ch <- 0
		return
	}
	defer func() {
		conn.Close()
		ch <- 1
	}()

	// 创建客户端对象
	clientObj := client.NewClient(&conn, conn)

	// 打印日志
	fmt.Printf("Connected to server. (remote address: %s, local address: %s)\n", conn.RemoteAddr(), conn.LocalAddr())

	// 写入1表示启动成功,则main线程可以继续往下进行
	ch <- 1
	clientCh <- clientObj

	// 死循环,不断地读取数据,解析数据,发送数据
	for {
		// 先读取数据,每次读取1024个字节
		readBytes := make([]byte, 1024)
		n, err := conn.Read(readBytes)
		if err != nil {
			var errMsg string

			// 判断是连接关闭错误,还是普通错误
			if err == io.EOF {
				errMsg = fmt.Sprintf("另一端关闭了连接:%s", err)
			} else {
				errMsg = fmt.Sprintf("读取数据错误:%s", err)
			}

			logUtil.Log(errMsg, logUtil.Error, true)

			break
		}

		// 将读取到的数据追加到已获得的数据的末尾
		clientObj.AppendContent(readBytes[:n])

		// 已经包含有效的数据,处理该数据
		handleClient(clientObj, loginSucceedCh)
	}
}
// 保存历史消息
func SaveHistoryMessage() {
	logUtil.Log("开始保存历史消息...", logUtil.Info, true)

	// 保存世界历史消息
	saveWorldHistoryMessage()

	// 保存公会历史消息
	saveUnionHistoryMessage()

	logUtil.Log("保存历史消息完成...", logUtil.Info, true)
}
Example #7
0
func writeRequestLog(apiName string, r *http.Request) error {
	log, err := json.Marshal(r.Form)
	if err != nil {
		logUtil.Log(fmt.Sprintf("序列化数据错误,原始数据位:%v,错误信息为:%s", r.Form, err), logUtil.Error, true)
		return err
	}

	logInfo := fmt.Sprintf("Web服务器收到请求。ApiName:%s, Param:%s", apiName, string(log))
	logUtil.Log(logInfo, logUtil.Debug, true)

	return nil
}
// 保存世界历史消息
func saveWorldHistoryMessage() {
	logUtil.Log("开始保存世界历史消息...", logUtil.Info, true)

	worldHistoryMessageMutex.RLock()
	defer worldHistoryMessageMutex.RUnlock()

	if bytes, err := json.Marshal(worldHistoryMessageList); err == nil {
		fileUtil.WriteFile(con_HistoryPath, getWorldHistoryMessageFileName(), false, string(bytes))
	}

	logUtil.Log("保存世界历史消息完成...", logUtil.Info, true)
}
Example #9
0
// 停止服务器
func StopServer() {
	logUtil.Log("1、开始停止服务器", logUtil.Debug, true)

	// 断开客户端连接
	logUtil.Log("2、开始断掉所有客户端连接", logUtil.Debug, true)
	for _, value := range ClientList() {
		value.conn.Close()
	}
	logUtil.Log("3、断掉所有客户端连接结束", logUtil.Debug, true)

	// 保存数据
	logUtil.Log("4、开始保存未处理的数据", logUtil.Debug, true)
	data.SaveToFile()
	logUtil.Log("5、保存未处理的数据结束", logUtil.Debug, true)
}
// 保存公会历史消息
func saveUnionHistoryMessage() {
	logUtil.Log("开始保存公会历史消息...", logUtil.Info, true)

	unionHistoryMessageMutex.RLock()
	defer unionHistoryMessageMutex.RUnlock()

	// 遍历所有的历史消息,每个公会保存为一个文件
	for unionId, messageList := range unionHistoryMessageList {
		if bytes, err := json.Marshal(messageList); err == nil {
			fileUtil.WriteFile(con_HistoryPath, getUnionHistoryMessageFileName(unionId), false, string(bytes))
		}
	}

	logUtil.Log("保存公会历史消息完成...", logUtil.Info, true)
}
Example #11
0
func handleClientContent(clientObj *Client) {
	validMessageList := make([]string, 0)

	for {
		content, ok := clientObj.GetValieMessage()
		if !ok {
			break
		}

		// 处理数据,如果长度为0则表示心跳包
		if len(content) == 0 {
			continue
		} else {
			atomic.AddInt64(&totalSize, int64(len(content)))

			// 先进行解压缩
			content, err := zlibUtil.Decompress(content)
			if err != nil {
				logUtil.Log(fmt.Sprintf("zlib解压缩错误,错误信息为:%s", err), logUtil.Error, true)
				break
			}

			validMessageList = append(validMessageList, string(content))
		}
	}

	// 添加数据
	if len(validMessageList) > 0 {
		data.AddData(validMessageList)
	}
}
Example #12
0
func GetPlayer(id string) (playerObj *player.Player, exists bool, err error) {
	command := "SELECT Name, UnionId, ExtraMsg, RegisterTime, LoginTime, IsForbidden, SilentEndTime FROM player WHERE Id = ?;"

	var name string
	var unionId string
	var extraMsg string
	var registerTime time.Time
	var loginTime time.Time
	var isForbidden bool
	var silentEndTime time.Time
	if err = dal.ChatDB().QueryRow(command, id).Scan(&name, &unionId, &extraMsg, &registerTime, &loginTime, &isForbidden, &silentEndTime); err != nil {
		if err == sql.ErrNoRows {
			// 重置err,使其为nil;因为这代表的是没有查找到数据,而不是真正的错误
			err = nil
			return
		} else {
			logUtil.Log(fmt.Sprintf("Scan失败,错误信息:%s,command:%s", err, command), logUtil.Error, true)
			return
		}
	}

	playerObj = player.NewPlayer(id, name, unionId, extraMsg, registerTime, loginTime, isForbidden, silentEndTime)
	exists = true

	return
}
Example #13
0
func GetGamePlayer(id string) (name string, unionId string, exists bool, err error) {
	command := "SELECT p.Name, g.GuildId FROM p_player p LEFT JOIN p_guild_info g ON p.Id = g.PlayerId WHERE p.Id = ?;"

	var guildId interface{}
	if err = dal.GameDB().QueryRow(command, id).Scan(&name, &guildId); err != nil {
		if err == sql.ErrNoRows {
			// 重置err,使其为nil;因为这代表的是没有查找到数据,而不是真正的错误
			err = nil
			return
		} else {
			logUtil.Log(fmt.Sprintf("Scan失败,错误信息:%s,command:%s", err, command), logUtil.Error, true)
			return
		}
	}
	exists = true

	// 处理公会Id
	if guildId != nil {
		if unionIdArr, ok := guildId.([]byte); ok {
			unionId = string(unionIdArr)
		}
	}

	return
}
Example #14
0
func clearExpiredClient() {
	// 处理内部未处理的异常,以免导致主线程退出,从而导致系统崩溃
	defer func() {
		if r := recover(); r != nil {
			logUtil.LogUnknownError(r)
		}
	}()

	for {
		// 休眠指定的时间(单位:秒)(放在此处是因为程序刚启动时并没有过期的客户端,所以先不用占用资源;)
		time.Sleep(CheckExpiredInterval() * time.Second)

		// 清理之前的客户端数量和玩家数量
		beforeClientCount := len(clientList)
		beforePlayerCount := len(playerList)

		// 获取本次清理的客户端数量
		expiredClientCount := 0

		// 开始清理
		for _, item := range clientList {
			if item.HasExpired() {
				expiredClientCount++
				item.Quit()
			}
		}

		// 记录日志
		if expiredClientCount > 0 {
			logUtil.Log(fmt.Sprintf("清理前的客户端数量为:%d, 清理前的玩家数量为:%d, 本次清理不活跃的数量为:%d", beforeClientCount, beforePlayerCount, expiredClientCount), logUtil.Debug, true)
		}
	}
}
Example #15
0
func init() {
	// 设置日志文件的存储目录
	logUtil.SetLogPath(filepath.Join(fileUtil.GetCurrentPath(), LOG_PATH_SUFFIX))

	// 记录启动成功日志
	logUtil.Log("客户端启动成功", logUtil.Info, true)
}
Example #16
0
func request(clientObj *client.Client, requestMap map[string]interface{}) {
	b, err := json.Marshal(requestMap)
	if err != nil {
		logUtil.Log(fmt.Sprintf("序列化请求数据%v出错", requestMap), logUtil.Error, true)
	} else {
		clientObj.SendByteMessage(b)
	}
}
Example #17
0
// 提交事务
// tx:事务对象
// 返回值:
// 错误对象
func CommitTransaction(tx *sql.Tx) error {
	err := tx.Commit()
	if err != nil {
		logUtil.Log(fmt.Sprintf("提交事务失败,错误信息:%s", err), logUtil.Error, true)
	}

	return err
}
Example #18
0
// 开始事务
// 返回值:
// 事务对象
// 错误对象
func BeginTransaction() (*sql.Tx, error) {
	tx, err := GameDB.Begin()
	if err != nil {
		logUtil.Log(fmt.Sprintf("开启事务失败,错误信息:%s", err), logUtil.Error, true)
	}

	return tx, err
}
Example #19
0
// 发送响应结果
// clientObj:客户端对象
// responseObject:响应对象
func responseResult(clientObj *client.Client, responseObj *responseDataObject.SocketResponseObject) {
	b, err := json.Marshal(responseObj)
	if err != nil {
		logUtil.Log(fmt.Sprintf("序列化输出结果%v出错", responseObj), logUtil.Error, true)
	} else {
		clientObj.SendByteMessage(b)
	}
}
Example #20
0
// 处理系统信号
func signalProc() {
	sigs := make(chan os.Signal)
	signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT)

	for {
		// 准备接收信息
		<-sigs

		logUtil.Log("收到退出程序的命令,开始退出……", logUtil.Info, true)

		// 做一些收尾的工作

		logUtil.Log("收到退出程序的命令,退出完成……", logUtil.Info, true)

		// 一旦收到信号,则表明管理员希望退出程序,则先保存信息,然后退出
		os.Exit(0)
	}
}
Example #21
0
func responseResult(w http.ResponseWriter, responseObj *ResponseObject) {
	b, err := json.Marshal(responseObj)
	if err != nil {
		logUtil.Log(fmt.Sprintf("序列化输出结果%v出错", responseObj), logUtil.Error, true)
		return
	}

	fmt.Fprintln(w, string(b))
}
Example #22
0
func writeRequestLog(apiName string, r *http.Request) error {
	log, err := json.Marshal(r.Form)
	if err != nil {
		logUtil.Log(fmt.Sprintf("序列化数据错误,原始数据位:%v,错误信息为:%s", r.Form, err), logUtil.Error, true)
		return err
	}

	return requestLogDAL.Insert(apiName, string(log))
}
Example #23
0
// 每分钟ping一次数据库
func ping() {
	for {
		time.Sleep(time.Minute)

		if err := GameDB.Ping(); err != nil {
			logUtil.Log(fmt.Sprintf("游戏数据库Ping失败,错误信息为:%s", err), logUtil.Error, true)
		}
	}
}
Example #24
0
func UpdateInfo(player *player.Player) error {
	command := "UPDATE player SET Name = ?, UnionId = ?, ExtraMsg = ? WHERE Id = ?"
	stmt, err := dal.ChatDB().Prepare(command)
	if err != nil {
		logUtil.Log(fmt.Sprintf("Prepare失败,错误信息:%s,command:%s", err, command), logUtil.Error, true)
		return err
	}

	// 最后关闭
	defer stmt.Close()

	if _, err = stmt.Exec(player.Name, player.UnionId, player.ExtraMsg, player.Id); err != nil {
		logUtil.Log(fmt.Sprintf("Exec失败,错误信息:%s,command:%s", err, command), logUtil.Error, true)
		return err
	}

	return nil
}
Example #25
0
func UpdateForbiddenStatus(player *player.Player) error {
	command := "UPDATE player SET IsForbidden = ? WHERE Id = ?"
	stmt, err := dal.ChatDB().Prepare(command)
	if err != nil {
		logUtil.Log(fmt.Sprintf("Prepare失败,错误信息:%s,command:%s", err, command), logUtil.Error, true)
		return err
	}

	// 最后关闭
	defer stmt.Close()

	if _, err = stmt.Exec(player.IsForbidden, player.Id); err != nil {
		logUtil.Log(fmt.Sprintf("Exec失败,错误信息:%s,command:%s", err, command), logUtil.Error, true)
		return err
	}

	return nil
}
Example #26
0
// 回滚事务
// tx:事务对象
// playerObj:玩家对象
// rollbackPlayer:回滚玩家数据的方法
func RollbackTransaction(tx *sql.Tx, playerObj *player.Player, rollbackPlayer func(*player.Player)) {
	err := tx.Rollback()
	if err != nil && err != sql.ErrTxDone {
		logUtil.Log(fmt.Sprintf("回滚事务失败,错误信息:%s", err), logUtil.Error, true)
	}

	// 并且回滚玩家数据
	rollbackPlayer(playerObj)
}
Example #27
0
func Insert(apiName, content string) error {
	command := "INSERT INTO request_log(APIName, ServerGroupId, Content, Crdate) VALUES(?, ?, ?, ?);"
	stmt, err := dal.ChatDB().Prepare(command)
	if err != nil {
		logUtil.Log(fmt.Sprintf("Prepare失败,错误信息:%s,command:%s", err, command), logUtil.Error, true)
		return err
	}

	// 最后关闭
	defer stmt.Close()

	if _, err = stmt.Exec(apiName, dal.ServerGroupId, content, time.Now()); err != nil {
		logUtil.Log(fmt.Sprintf("Exec失败,错误信息:%s,command:%s", err, command), logUtil.Error, true)
		return err
	}

	return nil
}
Example #28
0
// 处理系统信号
func signalProc() {
	sigs := make(chan os.Signal)
	signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT)

	for {
		// 准备接收信息
		<-sigs

		logUtil.Log("接收到退出的系统命令,准备退出", logUtil.Info, true)

		// 做一些收尾工作
		chatBLL.SaveHistoryMessage()

		logUtil.Log("接收到退出的系统命令,完成收尾工作,正式退出", logUtil.Info, true)

		// 一旦收到信号,则表明管理员希望退出程序,则先保存信息,然后退出
		os.Exit(0)
	}
}
Example #29
0
// 显示数据大小信息(每5分钟更新一次)
func displayDataSize() {
	for {
		// 刚启动时不需要显示信息,故将Sleep放在前面,而不是最后
		time.Sleep(5 * time.Minute)

		// 组装需要记录的信息
		msg := fmt.Sprintf("总共收到%s,发送%s.\t", mathUtil.GetSizeDesc(client.TotalReceiveSize()), mathUtil.GetSizeDesc(client.TotalSendSize()))
		msg += fmt.Sprintf("当前客户端数量:%d, 玩家数量:%d", clientBLL.GetClientCount(), playerBLL.GetPlayerCount())
		logUtil.Log(msg, logUtil.Debug, true)
	}
}
Example #30
0
// Goroutine调度
func dispatch() {
	for {
		// 每分钟进行一次调度
		time.Sleep(time.Minute)

		// 动态调整Goroutine的数量
		dynamicAdjustGoroutine()

		logUtil.Log(fmt.Sprintf("当前Goroutine数量为:%d,收到的总数量为:%d,未处理的数据量为:%d", goroutineCount, totalDataCount, len(dataChannel)), logUtil.Debug, true)
	}
}