簡體   English   中英

上下文無關的C ++ TCP服務器類

[英]Context independent C++ TCP Server Class

我正在基於I / O復用(選擇)方式對TCP Server類進行編碼。 下面的代碼段解釋了基本思想:

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

在這個版本中,主循環從套接字讀取,實例化正確類型的消息對象並調用適當的處理程序(某些東西可能無法正常工作,因為它可以編譯,但我尚未對其進行測試)。 如您所見,這允許程序員定義其消息類型和適當的處理程序,但是一旦主循環啟動,就無法執行任何操作。 我需要使服務器的這一部分更具可定制性,以使此類適合更多的問題。

MainLoop代碼

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 */
                }
           }
 }

您對執行此操作的好方法有任何建議嗎? 謝謝。

您的問題不僅需要堆棧溢出問題。 您可以在這些書中找到好的想法:

基本上,您想要做的是reactor 您可以找到實現此模式的開源庫。 例如:

如果您希望處理程序能夠進行更多處理,則可以為它們提供對TCPServer的引用,以及為以下事件注冊套接字的方式:

  • read ,套接字已准備好讀取
  • write ,套接字准備寫
  • accept ,監聽套接字已經准備好接受(用select讀取)
  • close ,插座關閉
  • timeout ,等待下一個事件的時間已過期( select允許以指定超時)

使處理程序可以實現各種協議half-duplexfull-duplex

  • 在您的示例中,處理程序無法回答收到的消息。 這是write事件的作用,它使處理程序知道何時可以在套接字上發送。
  • read事件也是如此。 它不應在主循環中,而應在套接字read處理程序中。
  • 您可能還希望增加使用timeout注冊事件處理程序的可能性,以便可以實現timers並刪除空閑連接。

這會導致一些問題:

  • 您的處理程序將必須實現一個state-machine ,以對網絡事件做出反應並更新它想要接收的事件。
  • 您的處理程序可能想要創建並連接新的套接字(想想一個Web代理服務器,一個帶有DCC的IRC客戶端,一個FTP服務器等等)。 為此,它必須有可能創建一個套接字並將其注冊到您的主循環中。 這意味着處理程序現在可以接收兩個套接字之一的回調,並且應該有一個參數告訴回調它是哪個套接字。 或者,您將必須為每個套接字實現一個處理程序,它們將與消息queue進行通信。 之所以需要該隊列,是因為一個套接字的就緒與另一個套接字的就緒無關。 而且您可能會在一個上閱讀某些內容,而沒有准備好在另一個上發送它。
  • 您將必須管理每個處理程序指定的超時,該超時可能有所不同。 您可能最終會遇到超時的priority queue

如您所見,這不是簡單的問題。 您可能希望減少框架的通用性以簡化其設計。 (例如僅處理half-duplex協議,例如簡單HTTP)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM