2012-07-10 92 views
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是具有当前用户特定操作的枚举)。

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

+1

链如何出现在你的类'UserSession'是单身?它有一个公共构造函数 – Habib 2012-07-10 04:24:36

+0

我希望你处理任何stream.Read()返回你的生产代码。至于这个问题,有很多方法有什么问题?如果你只做了几行代码,处理可能会停留在案例本身;否则它可能值得一种方法。 upd:如果你的对象是一致的序列化的,它实际上可能是一个方法,其中一些描述传入消息的格式作为参数。 – 2012-07-10 04:28:38

+0

@ Habib.OSU我在一个名为LoggedUsers的类里面有一个UserSession数组,这是一个单身人士 – Pacha 2012-07-10 04:31:23

回答

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) 
    } 

} 

我基本上避免枚举整个巨大肿块,并提出了更广义的设置,使每个对象负责它自己的数据,渠道等。这是更灵活的方法,更改一个枚举不会破坏所有内容。网络类甚至不需要知道正在发送的数据是什么,现在它只是一个管道。

+0

当你收到用户的数据包时你会怎么做?假设你有不同的行为,比如“连接”或“断开连接”。你有很多不同的连接吗? – Pacha 2012-07-10 04:50:44

+0

更具体地处理连接和断开连接。这些类型的案例不一定要在我上面描述的消息传递系统中。是的,我可以支持数百个连接。在收到消息时,我基本读出ChannelID,然后查看信道期望的数据类型,然后将有效载荷读入预期返回类型的新实例。 – 2012-07-10 04:52:26

+0

转向此方法需要相当多的重构/重写你的网络代码。但是,我可以说,改变我的代码时获得的价值非常巨大。现在添加新的动作/类型/事件非常容易,整个网络代码已经被减少了一半,并且简单性使其更容易调试和管理 – 2012-07-10 04:56:00

2

所以:你基本上正在努力做到这一点,所以将数字代码转换为方法的调用。

有些东西你不能得到,比如自动为你完成这个转换。如果您传送方法的名称,则可以使用反射来查找方法,然后调用它。因此,您必须执行一些手动工作来建立映射。

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

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

你可以使用类似这样的命令模式的组合和散列映射:

在安装过程中:

  1. 创建你的命令的通用接口(或使用闭包,因为 您使用C#)
  2. 创建实现接口pr的对象实例。你想要映射的方法
  3. 将它添加到散列表。

在调度循环:

  1. 取数
  2. 查找号码在哈希表
  3. 如果找到,调用对应的对象,否则失败

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

Command模式http://en.wikipedia.org/wiki/Command_pattern

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

+0

这就是我所要做的。有一张映射将枚举值映射到方法并查找并调用它们。 – 2012-07-11 07:50:37

+0

这也是我对这种情况的解决方案。它很好地分离了这些问题,并促进了Command模式的使用。 – 2012-07-11 08:45:26