1

我正在用 C# 制作服务器,并且我有一个单例来处理数组中的所有登录用户。该数组是一个名为 UserSession 的类的数组。

这个 UserSession 类有一个在处理该用户传入数据包的单独线程上运行的方法。

好吧,考虑一下这段代码:

class UserSession
{
    public UserSession(TcpClient client)
    {
        var thread = new Thread(new ParameterizedThreadStart(HandleComm);
        thread.Start((object)client);
    }

    public void HandleComm(object tcpClient)
    {
        TcpClient client = (TcpClient)tcpClient;
        NetworkStream stream = client.GetStream();
        while(1 == 1)
        {
            byte[] buffer = new byte[4096];
            stream.Read(buffer, 0, buffer.Length);
            int testShort = Convert.ToInt32(buffer);
            switch((ActionEnum)testShort)
            {
                case ActionEnum.Something:
                    // here is my problem!
                    // do some more parsing of the message
                    this.DoSomething(SomethingStruct argument); // argument taken from the byte array
                    break;
                case ActionEnum.AnotherSomething:
                    // the same as before
                    break;
                // and this goes on and on
            }
        }
    }
}

处理所有这些单独的枚举而不在该类上重复使用 80 多个方法的最佳方法是什么?(ActionEnum 是一个带有当前用户特定操作的枚举)。

我序列化了那个缓冲区,我只是让代码快速让你有一个想法。

4

2 回答 2

2

所以:你基本上试图这样做,所以将数字代码转换为方法的调用。

有些事情您无法获得,例如自动为您完成此转换。如果您改为传输方法的名称,则可以使用反射来查找该方法,然后调用它。因此,您必须做一些手动工作来建立映射。

你必须决定的是将数字映射到方法的最佳方式。

一种方法是您现在使用的方法。这里的映射发生在调度周期内(获取编号、翻译、调用)。问题是映射代码掩盖了调度代码。还涉及相当多的样板代码。

您可以使用命令模式和哈希映射的组合,如下所示:

在设置期间:

  1. 为您的命令创建一个通用接口(或使用闭包,因为您使用的是 C#)
  2. 创建一个实现接口 pr 的对象实例。您要映射的方法
  3. 将其添加到哈希图中。

在调度循环中:

  1. 取号
  2. 在哈希表中查找数字
  3. 如果找到,调用映射对象,否则失败

对于更灵活的方法,将数字视为必须处理的消息,并使用模式责任链。

命令模式http://en.wikipedia.org/wiki/Command_pattern

责任链http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern

于 2012-07-10T07:11:38.227 回答
1

一如既往,有很多可能的答案。我在构建客户端/服务器游戏时遇到了这个问题。我从几十个用于动作/事件/消息的枚举开始,然后意识到当你进入大量动作时它不是很可持续。

所以这就是我用伪代码粗略地做的事情。

    NetworkClass 
    {
       RegisterChannel(objUsingChannel,typeOfChannel, callbackForChannel, expectedReturnType){/*...*/};

       PushChannelMsg(channelID, dataToSend)

       ReceiveMessageFromNetwork(msg){  Read data from network, which also contains channelID, and send it to any matching channelID}  
       //If there is no channel registered for a data that is received,  just ignore it
    }

EnemyTurretObject
{
   //Register the rotation channel and listen for rotation changes
   NetworkClass.RegisterChannel(this, channels.TurretRotated + this.ID, callback=HandleTurretRotated, doubleReturnType)

   HandleTurretRotated(double rot)
   {  rotate the turret  }
}

FriendlyTurretObject
{
   //Register two channels that we'll send data across
   NetworkClass.RegisterChannel(this, channels.TurretFired + this.ID, callback=null, MissleFiredData)
   NetworkClass.RegisterChannel(this, channels.TurretRotated + this.ID, callback=null, doubleDataType)

   FireMissle()
    {
       NetworkClass.PushChannelMsg(channels.TurretFired + this.ID, new MissleFiredData(x,y)) 
    }

    RotateTurret()
    {
      NetworkClass.PushChannelMsg(channels.TurretRotated + this.ID, newTurretRotationValue) 
    }

}

我基本上避免了大量的枚举,并进行了更通用的设置,以便每个对象负责它自己的数据、通道等。这是一种更加灵活的方法,并且更改一个枚举并不会破坏一切。网络类甚至不需要知道正在发送的数据中有什么,它现在只是一个管道。

于 2012-07-10T04:47:50.480 回答