65.9K
CodeProject 正在变化。 阅读更多。
Home

简单易用的 IOCP 服务器框架

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.43/5 (8投票s)

2008年6月30日

LGPL3

3分钟阅读

viewsIcon

45131

downloadIcon

1725

SPServer 框架介绍

1. 引言

SPServer 是一个基于 Windows IOCP 的简单易用的服务器框架库。 它包括一个基本的 TCP 服务器框架和一个简单的 HTTP 服务器框架。它内置支持类似聊天室的应用程序。 SPServer 可以简化 TCP 服务器的构建。

2. 背景

IOCP 很好,但是当我们使用 IOCP 来实现一个稳定的服务器时,我们仍然需要做一些复杂的事情。 通常,我们需要处理以下事情:

  1. 处理部分读取,我们需要进行多次 WSARecv 才能读取完整的请求消息。
  2. 处理部分写入,我们需要进行多次 WSASend 才能写入完整的响应消息。
  3. 如果我们想实现一个类似聊天室的应用程序,我们需要同时处理多个套接字。 如果我们使用线程池来运行 IOCP,我们需要仔细处理这种情况。

SPServer 是一个尝试封装上述复杂问题的框架,并为应用程序员提供一个简单易用的接口。

3. 使用代码

下面描述了一个简单的行回显服务器。 这个回显服务器与一般的回显服务器不同:它在读取到行终止符 -- CRLF 之前不会响应。

这个示例展示了框架的基本概念。 要使用这个框架,你需要实现一个应用程序指定的处理器和处理器工厂。

如果也使用 CRLF 作为请求消息的分隔符,可以重用 SP_LineMsgDecoder;否则,你需要实现一个应用程序指定的消息解码器。

class SP_LineMsgDecoder : public SP_MsgDecoder {
public:
    virtual int decode( SP_Buffer * inBuffer );

    const char * getMsg();
};

class SP_EchoHandler : public SP_Handler {
public:
    // return -1 : terminate session, 0 : continue
    virtual int start( SP_Request * request, SP_Response * response ) {
        request->setMsgDecoder( new SP_LineMsgDecoder() );
        response->getReply()->getMsg()->append(
            "Welcome to line echo server, enter 'quit' to quit.\r\n" );

        return 0;
    }

    // return -1 : terminate session, 0 : continue
    virtual int handle( SP_Request * request, SP_Response * response ) {
        SP_LineMsgDecoder * decoder = (SP_LineMsgDecoder*)request->getMsgDecoder();

        if( 0 != strcasecmp( (char*)decoder->getMsg(), "quit" ) ) {
            response->getReply()->getMsg()->append( (char*)decoder->getMsg() );
            response->getReply()->getMsg()->append( "\r\n" );
            return 0;
        } else {
            response->getReply()->getMsg()->append( "Byebye\r\n" );
            return -1;
        }
    }
};

class SP_EchoHandlerFactory : public SP_HandlerFactory {
public:
    virtual SP_Handler * create() const {
        return new SP_EchoHandler();
    }
};

int main( int argc, char * argv[] )
{
    const char * host = "127.0.0.1";
    int port = 3333;

    SP_IocpServer server( host, port, new SP_EchoHandlerFactory() );
    server.runForever();

    return 0;
}

4. 类接口

行回显服务器引用了一些类

  • SP_MsgDecoder
  • SP_Request
  • SP_Message
  • SP_Response
  • SP_Buffer
  • SP_Handler

4.1 SP_MsgDecoder 和 SP_Request

class SP_MsgDecoder {
public:
    enum { eOK, eMoreData };
    virtual int decode( SP_Buffer * inBuffer ) = 0;
};

/* act as the SP_MsgDecoder's container */
class SP_Request {
public:
    SP_MsgDecoder * getMsgDecoder();

    void setMsgDecoder( SP_MsgDecoder * decoder );
};

4.2 SP_Message 和 SP_Response

typedef struct tagSP_Sid SP_Sid_t;

class SP_SidList {
public:
    int getCount() const;
    void add( SP_Sid_t sid );
    SP_Sid_t get( int index ) const;
    SP_Sid_t take( int index );

    int find( SP_Sid_t sid ) const;
};

class SP_Message {
public:
    SP_SidList * getToList();
    SP_Buffer * getMsg();
};

/* act as the SP_Message's container */
class SP_Response {
public:
    SP_Message * getReply();

    void addMessage( SP_Message * msg );
    SP_Message * peekMessage();
    SP_Message * takeMessage();
};

4.3 SP_Handler 和 SP_Buffer

class SP_Handler {
public:
    // return -1 : terminate session, 0 : continue
    virtual int start( SP_Request * request, SP_Response * response ) = 0;

    // return -1 : terminate session, 0 : continue
    virtual int handle( SP_Request * request, SP_Response * response ) = 0;

    virtual void error( SP_Response * response ) = 0;

    virtual void timeout( SP_Response * response ) = 0;

    virtual void close() = 0;
};

class SP_Buffer {
public:
    int append( const void * buffer, int len = 0 );
    int append( const SP_Buffer * buffer );
    void erase( int len );
    void reset();
    const void * getBuffer() const;
    size_t getSize() const;
    int take( char * buffer, int len );

    char * getLine();
    const void * find( const void * key, size_t len );

    SP_Buffer * take();
};

5. 框架的关键流程

5.1 处理部分读取

行回显服务器使用 SP_LineMsgDecoder 来处理部分读取。 SP_LineMsgDecoderSP_MsgDecoder 的子类。

框架的处理步骤如下:

  1. 当框架接受一个客户端时,它调用 SP_Handler::start,我们可以在此时创建应用程序指定的 MsgDecoder -- SP_LineMsgDecoder
  2. 框架为套接字发出 WSARecv
  3. 当框架从套接字读取一些数据时,它调用 SP_MsgDecoder::decode 方法。
  4. 如果 inBuffer 中保存的数据代表一个完整的请求消息,则它返回 SP_MsgDecoder::eOK 并转到第 5 步,否则它返回 SP_MsgDecoder::eMoreData 并转到第 2 步。
  5. 将数据复制到你自己的缓冲区,然后从 inBuffer 中擦除数据。
  6. 框架调用 SP_Handler::handle 方法。 我们可以检索 SP_LineMsgDecoder,并从 SP_LineMsgDecoder 获取请求消息。

5.2 处理部分写入

框架使用 SP_ResponseSP_Message 来处理部分写入。 SP_ResponseSP_Message 的容器。 SP_Message 用于保存响应消息。

框架的处理步骤如下:

  1. 框架调用 SP_Handler::handle 方法。 请求消息被处理,然后生成响应消息。 响应消息被附加到 SP_Response
  2. 框架为套接字发出 WSASend
  3. WSASend 完成时,框架检查 TransferredBytes,如果仍然有一些数据要发送,则转到第 2 步。

5.3 内置支持类似聊天室的应用程序

当客户端连接时,框架为每个客户端分配一个 uuid。 当我们生成响应时,我们可以通过将客户端的 uuid 添加到 SP_Message::mToList 来向客户端发送响应消息。 该框架封装了同时为多个套接字发送响应消息的复杂性。

在源代码包中有一个聊天室演示 -- testiocpdispatcher.cpp。 更多详细信息,请参考本文顶部提供的源代码。

6. 其他

SPServer 的核心部分在 Windows 平台上是独立的,它不依赖于任何第三方库。 因此,你可以仅使用 Visual Studio (VC++6 或更高版本) 构建库和演示程序。

SPServer 实现了一个插件机制来支持 SSL 通信。 如果你需要使用 SSL,你必须安装 OpenSSL 或 XySSL。

7. 参考文献

8. 历史

  • 2008-06-28:第一个版本
© . All rights reserved.