type
status
date
slug
summary
tags
category
icon
password
fullWidth
fullWidth

服务端部分

服务端是.NET控制台应用程序。

1.使用说明

运行控制台应用

  1. 开发环境(Visual Studio)中运行
      • 打开项目后,点击工具栏中的绿色箭头(启动按钮)或按F5键运行程序。
      • 程序将以调试模式启动,控制台窗口会显示Hello, World!,并等待用户输入命令。
  1. 生产环境(可执行文件)运行
      • 在项目生成目录(如bin\Release\netx.x)中找到生成的exe文件。
      • 双击exe文件运行程序,控制台窗口会显示Hello, World!,并等待用户输入命令。

命令行输入操作

程序启动后,可以通过命令行输入以下命令来操作服务器:
  1. 启动服务器
      • 输入命令:Start [--ip <IP地址>] [--port <端口号>] [--maxNum <最大客户端数量>]
      • 示例:
        • Start:使用默认IP地址(192.168.1.5)、默认端口号(8080)和默认最大客户端数量(10)启动服务器。
        • Start --ip 127.0.0.1 --port 8888 --maxNum 5:使用IP地址127.0.0.1、端口号8888和最大客户端数量5启动服务器。
      • 成功启动后,控制台会显示:服务器开启成功,IP地址: <IP地址>,端口:<端口号>
  1. 关闭服务器
      • 输入命令:Quit
      • 成功关闭后,控制台会显示:服务器关闭
  1. 其他输入
      • 如果输入的命令无效,程序会忽略并继续等待下一个有效命令

2.功能拆解

连接管理

连接管理模块负责处理客户端的连接和断开,确保服务器的稳定运行。具体功能包括:
  1. 客户端连接
      • 当客户端连接到服务器时,服务器会为其分配一个唯一的 clientID,并将其加入客户端字典 clientDict
      • 服务器会开启心跳检测线程,定期检查客户端是否在线。
  1. 客户端断开
      • 当客户端断开连接时,服务器会将其从客户端字典 clientDict 和大厅用户字典 hallClientDict 中移除。
      • 服务器会通知所有在线玩家更新大厅用户列表。
  1. 心跳检测
      • 服务器会定期检查客户端的心跳消息,如果客户端长时间未发送心跳消息,则判定其已断开连接。
      • 服务器会清理断开连接的客户端,并释放相关资源。

联机大厅

联机大厅是玩家点击【进入大厅】后的第一个界面,用于管理玩家的状态、匹配对手以及查看当前在线玩家列表。
  1. 玩家进入大厅
      • 玩家成功连接到服务器后,发送 EnterHall 消息,服务器记录玩家的用户名和客户端ID,将其加入大厅用户字典 hallClientDict
      • 服务器向该玩家发送 AllowEnterHall 消息,确认其成功进入大厅。
      • 同时,服务器通知所有在线玩家更新大厅用户列表。
  1. 玩家退出大厅
      • 当玩家主动退出大厅时,发送 QuitHall 消息,服务器会将其从大厅用户字典 hallClientDict 中移除。
      • 服务器通知所有在线玩家更新大厅用户列表。
  1. 大厅用户列表更新
      • 当有玩家进入或退出大厅时,服务器向所有在线玩家广播最新的 HallClients 消息,更新大厅用户列表。
      • 玩家可以随时发送 RequestHallClients 消息,请求获取当前大厅用户列表。
  1. 玩家状态管理
      • 玩家在大厅中的状态分为“空闲”和“繁忙”。空闲状态的玩家可以接收对战请求,繁忙状态的玩家则不能。
      • 服务器通过 SetHallClientIdle 方法更新玩家的状态,并在状态变化时通知所有在线玩家。

联网对战

联网对战是游戏的核心功能,玩家可以在大厅中发起对战请求,与其他玩家进行实时对战。
  1. 发起对战请求
      • 玩家可选择大厅中的其他空闲玩家,向其发送 SendBattleRequest 消息。
      • 服务器验证目标玩家的状态,如果目标玩家空闲,则转发对战请求,并将其状态设置为“繁忙”。
  1. 处理对战请求
      • 目标玩家收到对战请求后,可以选择接受或拒绝。
        • 如果目标玩家拒绝,服务器将其状态重置为“空闲”,并通知发起玩家对战请求被拒绝。
        • 如果目标玩家接受,服务器创建一个新的战局,并将双方玩家的状态设置为“繁忙”。
  1. 进入对战
      • 服务器会向双方玩家发送 EnterRound 消息,通知他们进入对战界面。
      • 服务器会为对战分配一个唯一的 onlineRoundIndex,并初始化对战状态 OnlineRoundState,记录双方的玩家ID和棋盘状态。
  1. 对战过程
      • 对战过程中,玩家每次落子会发送 MoveInfo 消息,服务器会更新对战状态,并将落子信息转发给对手。
      • 服务器不检查对战状态,判断对战是否结束的逻辑写在客户端;服务器只负责最后校验。

战局信息

  1. 在线战局状态管理:
      • 使用onlineRoundDict字典管理在线战局状态。
      • 收到客户端发来的表示落子信息的MoveInfo消息后:
        • GetRiverClientID从中获取某个客户端的对手客户端ID,并给对手客户端发送落子信息。
        • UpdateOnlineRoundState更新指定战局中某个格子的状态。
  1. 联机对战结果:
      • 使用onlineRoundResultDict字典管理客户端发送过来的联机对战结果。
      • 接收到客户端发来的 OnlineRoundResult 消息后,解析对战结果数据。
      • 根据 roundID 判断是否已经存在对应的战局信息:
        • 如果不存在,创建新的 Round 对象并存入字典。
        • 如果存在,进行校验(resultsteps),校验通过后保存到文件并删除字典中的键值对。
  1. 序列化和反序列化:
      • 使用 RoundManager 静态类对战局信息进行序列化和反序列化:
        • 序列化:SaveRoundInfoRound 对象保存到文件中。
        • 反序列化:GetRoundList从文件中读取所有战局信息并返回 Round 数组。

3.协议处理

协议类型

消息ID
数值
方向
关键行为
EnterHall
1
客户端 → 服务端
携带用户名初始化连接,触发服务端分配clientID
QuitHall
2
客户端 → 服务端
清除用户在大厅的在线状态
SendBattleRequest
3
客户端 ↔ 服务端
请求方发送目标clientID,服务端转发请求并标记目标为繁忙
ReplyBattleRequest
4
客户端 → 服务端
包含接受/拒绝标志,触发战局创建或状态重置
EnterRound
5
服务端 → 客户端
携带先手标识(isPrevPlayer)和战局索引(onlineRoundIndex)
Round
6
客户端 → 服务端
持久化保存到RoundManager管理的txt文件
RequestRoundList
7
客户端 → 服务端
触发服务端从文件加载历史数据
ProvideRoundList
8
服务端 → 客户端
携带所有历史战局的Round对象数组
AllowEnterHall
11
服务端 → 客户端
返回分配的clientID(数值型标识)
HallClients
12
服务端 → 客户端
包含大厅用户字典(clientID-用户名映射)
RequestHallClients
13
客户端 → 服务端
手动请求刷新大厅列表
MoveInfo
18
客户端 ↔ 服务端
包含棋子位置(pos)和战局索引(onlineRoundIndex)
ClientQuit
99
客户端 → 服务端
显式断开连接指令
HeartMessage
100
客户端 → 服务端
维持TCP连接活性
OnlineRoundResult
9
客户端 → 服务端
战局结束后,双方客户端自动向服务端发送,包含战局ID(roundID)、落子信息(steps)、胜负结果(result)

消息处理

在 ClientSocket->HandleMessage 方法中实现,根据不同的 MessageID 来处理不同的消息类型。
  1. 战局管理
      • RoundInfo (6) 
      → 解析战局数据 → 调用RoundManager.SaveRoundInfo()持久化到txt文件
      • RequestRoundList (7) 
      → 调用RoundManager.GetRoundList()读取文件 → 发送ProvideRoundList消息给客户端
      • OnlineRoundResult (9)
      → 解析对战结果 → 根据 roundID 是否在onlineRoundResultDict中决定新增或校验 → 校验通过后保存到文件。
  1. 大厅管理
      • EnterHall (1) 
      → 记录用户名 → 分配clientID → 发送AllowEnterHall → 加入大厅字典
      • QuitHall (2) 
      → 从大厅字典移除 → 触发全厅广播更新
      • RequestHallClients (13) 
      → 立即发送当前大厅用户字典给客户端
  1. 对战流程
      • SendBattleRequest (3) 
      → 验证目标状态 → 转发请求 → 标记目标为繁忙
      • ReplyBattleRequest (4)
        • 拒绝:重置双方为空闲
        • 接受: → 创建OnlineRoundState对象 → 分配全局onlineRoundIndex → 标记双方为繁忙 → 发送EnterRound(先手标记不同) → 创建战局
      • MoveInfo (18) 
      → 校验对战合法性 → 更新棋盘状态/战局信息 → 转发给对手客户端
  1. 连接管理
      • ClientQuit (99) 
      → 清除数据(大厅&战局) → 关闭Socket
      • HeartMessage (100) 
      → 更新lastHeartbeatTime时间戳

技术细节

  • 状态同步:大厅用户变化(新增、删除、闲忙状态改变)时,自动向全体在线用户广播HallClients消息
    • 心跳检测:在客户端连入后,立即开启心跳检测,用独立线程每0.1秒检查时间戳,60秒未更新则强制断开
      • 战局索引:通过自增ONLINE_ROUND_INDEX确保全局唯一性。
        • 线程锁:多个线程可能会同时访问共享资源(如clientDicthallClientDict),导致foreach报错;使用了线程锁(lock)来确保同一时间只有一个线程可以访问这些共享资源。

          4.异常处理

          客户端断线重连

          //TODO

          客户端部分

          客户端用Unity引擎开发,其中网络模块的核心是NetManager.cs。

          1.NetManager功能拆解

          核心类NetManager作为单例组件,挂载于Unity游戏物体,且过场景不销毁,负责管理客户端全生命周期网络交互,包括连接管理、消息路由、状态同步及异常处理。

          单例模式

          NetManager使用单例模式确保全局只有一个实例,方便在游戏的其他模块中调用。

          客户端状态记录

          NetManager中的变量记录客户端的状态,包括客户端ID、用户名、是否先手、当前对战ID等。

          消息处理

          NetManager字典messageHandlers存储和调用不同消息ID对应的回调函数。
          • 注册消息回调函数:RegisterHandler方法注册消息回调函数。
          • 解绑消息回调函数:UnregisterHandler方法解绑消息回调函数。
          • 触发消息回调函数:InvokeMessageCallback方法触发消息回调函数,支持延迟触发。

          网络连接管理

          NetManager负责与服务器的连接、断开连接以及心跳消息的发送。
          • 启动客户端:StartClient方法启动客户端并连接服务器。
          • 关闭客户端:CloseClient方法关闭客户端并发送退出消息。

          发送心跳消息

          • SendHeartMessage方法定期心跳消息。
          • Awake生命周期函数中开启InvokeRepeating定期发送心跳消息。

          消息发送与接收

          NetManager使用两个队列sendMsgQueuereceiveMsgQueue来管理消息的发送和接收;在 StartClient方法中将这两个方法加入线程池
          • 发送消息:
          Send 方法将消息加入发送队列。
          线程方法遍历消息队列,发送消息
          • 接收消息:线程方法 ReceiveMsg 方法接收消息并加入接收队列
          • 消息处理: Update 生命周期函数中处理接收到的消息,并根据消息ID调用相应的回调函数。

          2. OnlineGameController功能拆解

          OnlineGameController是客户端联机对战场景的核心控制器,负责管理对战过程中的棋盘状态、玩家交互、胜负判定及结果处理;与网络模块NetManager深度集成,实现实时对战数据的同步。

          棋盘状态管理

          通过OnlineGrid数组来管理棋盘的状态;OnlineGrid对象代表格子。
          • 更新棋盘状态:当玩家或对手落子时,OnlineGameController会更新对应格子的状态,并在UI上显示相应的棋子。

          玩家交互

          通过canClick变量来控制玩家是否可以点击棋盘格子。当轮到玩家落子时,canClicktrue,玩家可以点击棋盘上的空格子进行落子。
          • 玩家落子:当玩家点击一个空格子时,OnlineGameController会调用Move方法,更新棋盘状态,并将落子信息发送给服务器。
          • 对手落子:当接收到对手的落子信息时,OnlineGameController更新棋盘状态,并恢复玩家的点击权限。

          胜负判定

          从第5步开始,在每一步落子后都会检查当前棋盘状态,判断是否有玩家获胜或是否平局。
          • 胜负检查:CheckWin检查棋盘的所有可能获胜情况,包括横向、纵向和斜向。
          • 处理胜负结果:当检测到有玩家获胜或平局时,调用CheckAndHandleGameResult处理游戏结束的逻辑,包括显示结果面板、发送结果给服务器。

          游戏结束处理

          当游戏结束时,显示游戏结束面板,并根据胜负结果更新UI。
          • 显示游戏结束面板:ShowGameOverPanel方法会在0.5秒后显示游戏结束面板,并根据胜负结果更新Text_Result的文本内容。
          • 保存对战数据:玩家可以点击保存按钮,将当前对战的数据保存到本地或发送给服务器。
           
          CrossChess-井字棋单机小游戏游戏AI行为决策-分层任务网络(HTN)的简单应用
          Loading...
          Latest posts
          游戏算法-Floyd搜索算法知识梳理和通用框架
          2025-4-2
          游戏算法-A*搜索算法知识梳理和通用框架
          2025-4-2
          游戏AI行为决策-目标导向行为规划(GOAP)通用框架
          2025-3-23
          自制Python任务调度模块-MySchedule
          2025-3-20
          学习笔记:23种设计模式
          2025-3-19
          学习笔记:计算机网络(自顶向下方法)课程笔记
          2025-3-15