【问题标题】:Handling client/server messages?处理客户端/服务器消息?
【发布时间】:2012-10-07 16:18:41
【问题描述】:

我从我的客户端/服务器接收序列化数据,一旦数据被反序列化,它就会进入一个命令处理程序,其中receivedData.ActionClientMessage

Command._handlers[receivedData.Action].Handle(receivedData.Profile);

命令处理程序将处理客户端消息并返回应给予客户端的响应。

我有一个客户端消息的枚举如下:

public enum ClientMessage
{
    INIT = 1,
    NEW_PROFILE,
    UPDATE_PROFILE_EMAIL,
    UPDATE_PROFILE_PASSWORD,
    UPDATE_PROFILE_PHONE,
    UPDATE_PROFILE_DATE,
    UPDATE_PROFILE_SECRET_ANSWER,
    UPDATE_PROFILE_POSTAL_CODE,
    UPDATE_SUCCESS,
    PING,
    PONG,
    QUIT
}

我遇到的困难是如何编写所有动作,例如:

  • 我应该为客户端发送的内容和服务器应回复的内容分别设置一个枚举吗?
  • 或者我应该有一个包含所有消息的枚举并按照要求进行操作?
  • 或者我应该如何定义和处理消息?

这是我的服务器/客户端目前所做的,只是为了让您有更好的视野:

  1. 服务器启动
  2. 客户端连接
  3. 客户端向服务器发送身份验证
  4. 服务器验证客户端并发送连接的批准消息
  5. 客户端将从那里开始向服务器发送和更新配置文件

这只是一个大概的例子。

IPacketHandler

public interface IPacketHandler
{
    MyCommunicationData Handle(ProfileData profile);
}

命令

public class Command
{
    public static Dictionary<ClientMessage, IPacketHandler> _handlers = new Dictionary<ClientMessage, IPacketHandler>()
    {
        {ClientMessage.INIT, new Init()},
        {ClientMessage.NEW_PROFILE, new NewProfile()},
        {ClientMessage.UPDATE_PROFILE_EMAIL, new UpdateEmail()},
        {ClientMessage.UPDATE_PROFILE_PASSWORD, new UpdatePassword()},
        {ClientMessage.UPDATE_PROFILE_PHONE, new UpdatePhone()},
        {ClientMessage.UPDATE_PROFILE_DATE, new UpdateDate()},
        {ClientMessage.UPDATE_PROFILE_SECRET_ANSWER, new UpdateSecretAnswer()},
        {ClientMessage.UPDATE_PROFILE_POSTAL_CODE, new UpdatePostalCode()},
        {ClientMessage.UPDATE_SUCCESS, new Success()},
        {ClientMessage.PING, new Ping()},
        {ClientMessage.PONG, new Pong()},
        {ClientMessage.QUIT, new Quit()},
    };
}

初始化示例

public class Init : IPacketHandler
{
    public MyCommunicationData Handle(ProfileData profile)
    {
        // Some verification to auth the client here 
        // bla bla
        // return response
        return new MyCommunicationData() { Action = ClientMessage.CONNECTED };
    }
}

PS:如果我的标题已关闭并且您有更好的建议,请告诉我,或者如果您可以继续更新它,我不知道如何用英文描述。

【问题讨论】:

  • 我不会用客户端-服务器来标记这个问题,它与面向对象的设计相比,它必须比客户端-服务器做更多的事情。

标签: c# .net-4.0 client-server ooad


【解决方案1】:

如果您的问题是关于如何按照我的理解设计类和交互,那么我会 - 它完全取决于您的应用程序的具体情况 - 将这个大的 Enumerations 类型分成单独的、更小的、更具描述性的 Enumerations 类型他们做什么,以及你的意图,例如,ProfileActionActionResultPingStatus 等。然后当你使用这些枚举时,你要确保你得到你正在做的编译时检查它是正确的,否则,你所做的几乎就像只是传递字符串。

这也与 OO 设计中坚持单一职责原则有关:一个对象应该有单一职责。你现在的枚举有不止一项责任。

对于此类问题,我发现了解 .NET 框架的作用很有帮助:例如查看 Ping class 以及它如何使用 PingStatus 枚举和其他枚举。

【讨论】:

  • 我的问题主要是关于我应该如何计算出威胁客户端/服务器请求和响应的枚举。例如客户端连接到服务器,然后它向服务器发送一个身份验证请求,服务器将回复一个身份验证响应命令。我可以在一个枚举中完成所有操作,以便可以使用用于发送数据的同一类来接收,或者使用 2 个不同的枚举,这将导致 2 个不同的类或来自 MyCommunicationData 的派生类以响应客户端。不确定我是否清楚我遇到的困难。
  • 没问题。然后我的回答是,我认为你应该有尽可能多的枚举,因为它们在逻辑上是有意义的,然后方法应该使用它需要的枚举。拥有额外的类,方法比将不相关的概念放在一个类中要好。它是面向对象设计的 SOLID 原则中的 S 单一责任原则。
【解决方案2】:

我完全不确定我会使用枚举。它们在一小段代码中很棒,暴露为传达的价值,它们远没有那么好。

对我来说,每条消息都有一个不同的类,而不是一条具有上帝属性的消息。

【讨论】:

  • 不确定我是否明白你的意思,但在某种程度上我确实为每个消息类型设置了一个类
  • 除非我误解了你的每条消息都有一个带有 messagetype 属性的类的实例。我是说抛弃 messagetype 属性并拥有 PingMessage、PongMessage 等。
  • 所以您建议删除通讯状态(我的枚举)并按需执行?我目前正在使用枚举来跟踪上次发送的内容以及接下来要接收的内容等。
  • 从这里很难说,我只是在抛出可能性。例如,您可以传递状态机并让它“跟踪”自己,而不是跟踪状态...
  • 我不确定我是否理解,您是否愿意发布一些虚构的样本,以便我可以得到它的图片(“黑客”本身)?