【问题标题】:winsock, message oriented networking, and type-casting the buffer from recvwinsock,面向消息的网络,以及从 recv 类型转换缓冲区
【发布时间】:2012-02-14 01:20:19
【问题描述】:

好吧,我实际上还没有代码,因为我暂时只是在挑选一个框架,但我仍然对我希望如何去做这件事感到有点困惑:.

服务器端,我希望有一个类,其中每个实例都有一个套接字和标识每个连接的各种信息。每个对象都有自己的线程来接收数据。我知道我将如何实现其中的大部分内容,但是当我开始在服务器和客户端之间实际传输数据时,我的困惑就开始了。对于特定情况,我想要一堆不同的消息结构(例如 CONNECT_MSG 、 DISCONNECT_MSG 、 POSTTEXT_MSG 等),然后我所要做的就是在该结构上有一个 char * 点,然后通过发送传递它() 函数。

但正如我所想的那样,在这一点上它变得有点复杂。可以发送任何这些不同的消息类型,并且在接收端,您将不知道应该将传入缓冲区转换为什么。我希望做的是,在每个连接对象的线程中,让它阻塞,直到它接收到带有消息的数据包,然后将其转储到由服务器管理的单个队列对象(互斥锁将防止贪婪),然后是服务器将独立于连接对象以 FIFO 顺序处理每条消息。

我还没有写任何东西,但让我写一些东西来说明我的设置。

#define CONNECT 1000

struct GENERIC_MESSAGE
{
    int id;
}

struct CONNECT_MESSAGE : public GENERIC_MESSAGE
{
    m_username;
}

void Connection::Thread()
{

    while(1)
    {
         char buffer[MAX_BUFFER_SIZE];    // some constant(probably 2048)
         recv(m_socket, buffer, MAX_BUFFER_SIZE, 0);
         MESSAGE_GENERIC * msg = reinterpret_cast<MESSAGE_GENERIC *> (buffer);
         server->queueMessage(msg);
    }

}

void Server::QueueMessage(MESSAGE_GENERIC * msg)
{
    messageQueue.push(msg);
}

void Server::Thread()
{
    while(1)
    {
         if(!messageQueue.empty())
              ProcessMessages();
         else
              Sleep(1);
    }
}

void Server::ProcessMessages()
{
     for(int i = 0; i < messageQueue.size(); i++)
     {
          switch(messageQueue.front()->id)
          {
                case CONNECT:
                {
                     // the part i REALLY don't like
                     CONNECT_MESSAGE * msg = static_cast<CONNECT_MESSAGE *>(messageQueue.front() );
                     // do the rest of the processing on connect
                     break;
                }
                // other cases for the other message types
          }
          messageQueue.pop();
     }
}

如果您一直跟进到现在,您就会意识到这是多么愚蠢和脆弱。它转换为基类,将该指针传递给队列,然后假设该指针在另一个线程中仍然有效,即使这样,派生类的其余部分的指针之后的剩余缓冲区是否总是之后对铸造有效,但我还没有找到正确的方法。我愿意接受任何建议,无论是使这项工作发挥作用,还是完全不同的消息传递设计。

【问题讨论】:

    标签: c++ visual-c++ winsock2


    【解决方案1】:

    在编写一行代码之前,请设计将在有线上使用的协议。在字节级别确定消息将包含哪些内容。决定谁先发送、消息是否被确认、接收者如何识别消息边界等等。决定如何保持连接处于活动状态(如果是的话),哪一方将首先关闭,等等。然后围绕规范编写代码。

    不要将您在内存中存储内容的方式与通过网络发送内容的方式紧密联系在一起。这是两个截然不同的事物,具有两组截然不同的要求。

    当然,您可以在编写代码时随意调整协议规范。

    【讨论】:

    • 我实际上已经解决了大部分问题。所有消息都将包含消息类型的 id,其余的将根据 id 是可变的。要么先发送,要么同时接收。它将是 tcp/ip,因此消息被确认,不确定消息边界是什么意思。 Connect 将保持活动状态,直到收到断开连接消息,并且客户端或服务器端都可以发送断开连接消息,他们将相应地处理它。目前,唯一的问题是发送消息的数据类型的可变性。
    • 在字节级别定义每个消息有效负载。定义接收者如何找到消息之间的边界(一条消息结束,下一条消息开始)。
    • 这实际上是我试图避免的转换和消息,我以前做过一些 winsock 编程,发现字节级别的精细解析方案和我一样不稳定现在尝试做。试图将单个 char * 缓冲区拆分为多个对象总是被击中和错过。现在我试图限制它,以便每个缓冲区只有一个消息对象,可以转换为一种类型,并且数据现在都布置在一个兼容的对象中,但这似乎也不起作用。
    • 你不想避免它。 TCP 连接是一个字节流。您需要根据字节定义协议。您可能会发现它命中或未命中,但至少可以对其进行调试——只需将生成的字节与您的协议规范进行比较。
    • 是的,我什至不会把我在那里拥有的东西称为协议规范:\。只是想知道类似的方法是否可行。但是,当您说字节流时,这使我认为也许我可以像对待流一样对待它,拥有一个缓冲区,然后是一个 char * 标记您当前的解析位置,然后从该点创建对象并将 char * 移动到刚刚解析出来的内容的结尾。我会看看我能不能解决一些问题。不过,感谢您的建设性批评,我认为我现在的心态是正确的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-04
    • 2012-10-24
    • 2012-02-25
    • 1970-01-01
    • 2012-04-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多