Unity3D网络游戏实战——通用服务器框架

前言

网络游戏涉及客户端和服务端。服务端程序记录玩家数据,处理客户端发来的协议。本文就介绍一套通用客户端的实现。
该框架基于Select多路复用处理网络消息,具有粘包半包处理、心跳机制等功能,还是用MySQL数据库存储玩家数据,是一套功能较完备的C#服务端程序。一般单个服务端进程可以承载数百名玩家,如果更多就需要改为分布式架构。

7.1服务端架构

服务端两大核心是处理客户端的消息和存储玩家数据。
客户端与服务端通过TCP连接,使两者可以传递数据,服务端还连接着MySQL数据库,可将玩家数据保存到数据库中。

7.1.2模块划分

  • 逻辑层:消息处理,事件处理,存储结构。
  • 网络底层:连接客户端,消息处理,事件处理
  • 数据库底层:连接数据库,存储结构。

7.2Json编码解码

和客户端不同的是,因为服务端程序和Unity无关,无法使用JsonUtility,所以改用System.Web提供的方法实现。(需要手动引用System.web.Extensions)
在这里插入图片描述

新建一个控制台程序,将协议文件全部放在proto下。

7.2.3修改MsgBase

引入System.Web.Script.Serialization头文件后,和JsonUtility调用方法不同。因为JavaScriptSerializer不是静态类,需要定义一个该类型的对象,再让对象调用Serialize和Deserialize方法进行编码解码

7.3网络模块

7.3.1整体架构

  • 协议解析
  • 处理select多路复用的网络管理器NetManager(核心)
  • 定义客户端信息ClientState类
  • 处理网络消息的MsgHandler类,但是把不同消息类型的处理分拆到多个文件中。(BattleMsgHandler处理战斗协议、SysMsgHandler处理ping、pong协议)
  • 事件处理类EventHandler

7.3.2ClientState

客户端信息,每一个客户端连接对应一个ClientState,含有与客户端连接的套接字socket和读缓冲区readBuff,以及对应的玩家数据和最后一次收到ping的时间。

using System.Net.Sockets;

public class ClientState
{
   
   
	public Socket socket; 
	public ByteArray readBuff = new ByteArray(); 
	//Ping
	public long lastPingTime = 0;
	//玩家
	public Player player;
}

7.3.3开启监听和多路复用

客户端和服务端的NetManager功能相似,都是处理链接、粉发消息和网络事件。
但是为了管理多个连接,服务端采用了多路复用技术。

public static void StartLoop(int listenPort)
	{
   
   
		//Socket
		listenfd = new Socket(AddressFamily.InterNetwork,
			SocketType.Stream, ProtocolType.Tcp);
		//Bind
		IPAddress ipAdr = IPAddress.Parse("0.0.0.0");
		IPEndPoint ipEp = new IPEndPoint(ipAdr, listenPort);
		listenfd.Bind(ipEp);
		//Listen
		listenfd.Listen(0);
		Console.WriteLine("[服务器]启动成功");
		//循环
		while (true)
		{
   
   
			ResetCheckRead();  //重置checkRead
			Socket.Select(checkRead, null, null, 1000);
			//检查可读对象
			for (int i = checkRead.Count - 1; i >= 0; i--)
			{
   
   
				Socket s = checkRead[i];
				if (s == listenfd)
				{
   
   
					ReadListenfd(s);
				}
				else
				{
   
   
					ReadClientfd(s);
				}
			}
			//超时
			Timer();
		}
	}

服务端开启了端口监听后,进入循环。针对Select返回的列表,程序遍历它判断有新的客户端连接还是某个客户端发来消息,然后分别调用处理函数ReadListenfd和ReadClientfd。
当程序在Select有可读事件和超时都会调用Timer,空闲状态每秒调用一次。

处理监听消息以及处理客户端消息和前面写的都差不多,就不详细介绍了。

7.4心跳机制

我们在前面的clientstate已经加入了lastpingtime,注意是long,游戏客户端只运行几小时,unity提供的Time.time即可记录。但是服务端可能运行纪念,所以要用long保存。

7.4.2时间戳

时间戳是一种记录时间的方法,也就是1970年1月1日零点到现在的秒数。

//获取时间戳
	public static long GetTimeStamp()
	{
   
   
		TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
		return Convert.ToInt64(ts.TotalSeconds);
	}

7.4.3回应MsgPing协议

更新lastPingTime并回应

using System;

public partial class MsgHandler
{
   
   
    public static void MsgPing(ClientState c,MsgBase msgBase)
    {
   
   
        Console.WriteLine("MsgPing");
        c.lastPingTime = NetManager.GetTimeStamp();
        MsgPong msgPong = new MsgPong();
        NetManager.Send(c, msgPong);
    }
}

7.4.4超时处理

遍历客户端连接,太久没收到就断开连接,并删除clients列表元素。注意这是在遍历clients,删除后再遍历会出错,所以直接break。每次checkping最多断开一个连接。
在ontimer中调用,ontimer是在timer里通过反射调用,timer在startloop里调用

public static void OnTimer()
    {
   
   
        CheckPing();
    }

    //Ping检查
    public static void CheckPing()
    {
   
   
        //现在的时间戳
        long timeNow = NetManager.GetTimeStamp();
        //遍历,删除
        foreach (ClientState s in NetManager.clients.Values)
        {
   
   
            if (timeNow - s.lastPingTime > NetManager.pingInterval * 4)
            {
   
   
                Console.WriteLine
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值