c++ - 上下文无关的 C++ TCP 服务器类

标签 c++ oop network-programming function-pointers

我正在编写一个基于 I/O 多路复用(选择)方式的 TCP 服务器类。 这段代码解释了基本思想:

GenericApp.cpp

TServer *server = new Tserver(/*parameters*/);
server->mainLoop();

目前服务器的行为独立于上下文,但在某种程度上我需要改进。

实际状态

 receive(sockFd , buffer);
 MSGData * msg=     MSGFactory::getInstance()->createMessage(Utils::getHeader(buffer,1024));
 EventHandler * rightHandler =eventBinder->getHandler(msg->type());
 rightHandler->callback(msg);

在这个版本中,主循环从套接字读取,实例化正确类型的消息对象并调用适当的处理程序(有些东西可能无法正常工作,因为它编译但我没有测试过)。 正如您所注意到的,这允许程序员定义他的消息类型和适当的处理程序,但是一旦主循环启动,就什么也做不了了。 我需要使服务器的这一部分更具可定制性,以使此类适应更大的 问题的数量。

主循环代码

void TServer::mainLoop()
{

    int sockFd;
    int connFd;
    int maxFd;
    int maxi;
    int i;
    int nready; 

    maxFd = listenFd;
    maxi = -1;

     for(i = 0 ; i< FD_SETSIZE ; i++) clients[i] = -1; //Should be in the constructor?

     FD_ZERO(&allset); //Should be in the constructor?
     FD_SET(listenFd,&allset); //Should be in the constructor?


         for(;;)
          {
             rset = allset;
             nready = select (maxFd + 1 , &rset , NULL,NULL,NULL);

             if(FD_ISSET( listenFd , &rset ))
             {
                cliLen = sizeof(cliAddr);
                connFd = accept(listenFd , (struct sockaddr *) &cliAddr, &cliLen);

                 for (i = 0; i < FD_SETSIZE; i++)
                 {  
                     if (clients[i] < 0) 
                    {
                         clients[i] = connFd;   /* save descriptor */
                         break;
                    }
                 }

                 if (i == FD_SETSIZE) //!!HANDLE ERROR

                 FD_SET(connFd, &allset);   /* add new descriptor to set */

                 if (connFd > maxFd) maxFd = connFd;            /* for select */

                 if (i > maxi) maxi = i;                /* max index in client[] array  */

                 if (--nready <= 0)  continue;  
             }

            for (i = 0; i <= maxi; i++)     
             {  
                /* check all clients for data */
                if ( (sockFd = clients[i]) < 0) continue;

                if (FD_ISSET(sockFd, &rset)) 
                {
                    //!!SHOULD CLEAN BUFFER BEFORE READ
                    receive(sockFd , buffer);
                    MSGData * msg =  MSGFactory::getInstance()->createMessage(Utils::getHeader(buffer,1024));
                    EventHandler * rightHandler =eventBinder->getHandler(msg->type());
                    rightHandler->callback(msg);
                }
                   if (--nready <= 0)   break;              /* no more readable descriptors */
                }
           }
 }

您对执行此操作的好方法有什么建议吗? 谢谢。

最佳答案

您的问题不仅仅是一个堆栈溢出问题。你可以在这些书中找到好的想法:

基本上您要做的是一个 react 器。您可以找到实现此模式的开源库。例如:

如果您希望您的处理程序有可能进行更多处理,您可以为他们提供对您的 TCPServer 的引用以及为以下事件注册套接字的方法:

  • read,套接字准备好读取
  • write,套接字准备好写入
  • accept,监听套接字准备接受(用select读取)
  • close,套接字关闭
  • timeout,等待下一个事件到期的时间(select允许指定超时)

以便处理程序可以实现各种协议(protocol)半双工全双工:

  • 在您的示例中,处理程序无法回答收到的消息。这是 write 事件的作用,让处理程序知道它何时可以在套接字上发送。
  • read 事件也是如此。它不应在您的主循环中,而应在套接字 read 处理程序中。
  • 您可能还想添加为具有超时的事件注册处理程序的可能性,以便您可以实现计时器并丢弃空闲连接。

这会导致一些问题:

  • 您的处理程序必须实现一个状态机来对网络事件使用react并更新它想要接收的事件。
  • 您的处理程序可能想要创建和连接新套接字(考虑 Web 代理服务器、带 DCC 的 IRC 客户端、FTP 服务器等...)。为此,它必须能够创建一个套接字并将其注册到您的主循环中。这意味着处理程序现在可以接收两个套接字之一的回调,并且应该有一个参数告诉回调它是哪个套接字。或者您必须为每个套接字实现一个处理程序,它们将与消息的 queue 进行通信。队列是必需的,因为一个套接字的就绪状态独立于另一个套接字的就绪状态。您可能会在一个上阅读某些内容,但还没有准备好在另一个上发送它。
  • 您必须管理每个处理程序指定的超时时间,这些处理程序可能不同。您最终可能会遇到超时的优先级队列

如您所见,这不是一个简单的问题。您可能希望减少框架的通用性以简化其设计。 (例如仅处理 half-duplex 协议(protocol),如简单的 HTTP)

关于c++ - 上下文无关的 C++ TCP 服务器类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24755809/

相关文章:

c++ - CMake 安装到/bin

python - 在 R6 的初始化中调用 R6 类的函数

javascript - 闭包如何访问被破坏的变量?

使用系统(命令)时,C++ 代码在 AWS 中失败,错误为 "cannot allocate memory"

c++ - CppUnit - 注册多个套件以在单个主要功能中运行

sockets - SOCK_STREAM VS SOCK_SEQPACKET

java - 可用的最大套接字连接数

考虑char实际大小的linux socket编程

c++ - "xxx.exe is not a valid Win32 application"在 VS 刚刚构建之后

python - 对自定义容器对象进行排序