用完成端口(IOCP)实现一个简单的服务器框架

83 views
Skip to first unread message

stephen.nil

unread,
May 24, 2008, 1:12:56 AM5/24/08
to dev4s...@googlegroups.com
用完成端口(IOCP)实现一个简单的服务器框架
 
IOCP 对于高并发的应用程序提供了良好的支持,使得开发高并发的应用程序的难度降低了很多。
IOCP 作为系统底层的 API ,保持了尽可能高的灵活性,对于很多复杂的情况,IOCP 也一样适用。
这种灵活性是一种双刃剑,对于复杂的情况,没有这种灵活性就没有办法完成工作。
但是对于简单的情况,这种灵活性就带来了额外的负担。
 
这里将要提到的这个服务器框架(spserver/iocp版),主要是针对以下的这几种典型的应用程序
1.echo/http 类型,server 不需要在多个 client 之间转发消息
2.chatroom 类型,server 在多个 client 之间转发消息
 
在使用 IOCP 来实现这两类应用程序的时候,会遇到一些共同的问题
1.当连接断开的时候,在 server 端为这个连接分配的资源如何妥善地进行释放
2.如何为每一个 IO 操作设定一个超时时间,以使得 server 能够更好地了解每个连接的当前情况
 
spserver 通过使用 hash 表的方式来解决资源释放的问题,具体的讨论可以参考
 
spserver 通过使用类似 libevent 的 min-heap 来解决 IO 操作超时的问题,具体可以参考
 
在上一个版本(0.9)中,spserver 在 windows 平台还是基于 libevent + pthread-win32 来实现的。
最新版本(0.9.1),在 windows 平台改成使用 IOCP + Windows Thread 来重新实现。
最新版本在 Windows 平台已经不再需要依赖第三方的库了。
 
主页和下载地址
 
下面来看一个使用 spserver 实现的简单的 line echo server 。 
 
得益于 IOCP 本身的 IO 能力,下面这个简单的 echo server ,如果在同一台机器上运行 server(testiocpecho.exe) 和 client(testiocpstress.exe) ,能够达到 recv 10万/秒 ,send 10万/秒 的速度;如果在两台用 100M 带宽相连的机器上分别执行 server 和 client ,在测试期间能够保持对带宽 90% 的利用率。
 
class SP_EchoHandler : public SP_Handler {  
public:  
  SP_EchoHandler(){}  
  virtual ~SP_EchoHandler(){}  
  
  // 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;          
    }             
  }       
  virtual void error( SP_Response * response ) {}  
  
  virtual void timeout( SP_Response * response ) {}  
  
  virtual void close() {}  
};  
  
class SP_EchoHandlerFactory : public SP_HandlerFactory {  
public:  
  SP_EchoHandlerFactory() {}  
  virtual ~SP_EchoHandlerFactory() {}  
  
  virtual SP_Handler * create() const {  
    return new SP_EchoHandler();  
  }  
};  
  
int main( int argc, char * argv[] )  
{  
  int port = 3333;  
  
  SP_Server server( "", port, new SP_EchoHandlerFactory() );  
  server.runForever();  
  
  return 0;  
}
 
在最简单的情况下,使用 spserver 实现一个 TCP server 需要实现两个类:SP_Handler 的子类 和 SP_HandlerFactory 的子类。
SP_Handler 的子类负责处理具体业务。
SP_HandlerFactory 的子类协助 spserver 为每一个连接创建一个 SP_Handler 子类实例。
 
1.SP_Handler 生命周期
SP_Handler 和 TCP 连接一对一,SP_Handler 的生存周期和 TCP 连接一样。
当 TCP 连接被接受之后,SP_Handler 被创建,当 TCP 连接断开之后,SP_Handler将被 destroy。
 
2.SP_Handler 函数说明
SP_Handler 有 5 个纯虚方法需要由子类来重载。这 5 个方法分别是:
start:当一个连接成功接受之后,将首先被调用。返回 0 表示继续,-1 表示结束连接。
handle:当一个请求包接收完之后,将被调用。返回 0 表示继续,-1 表示结束连接。
error:当在一个连接上读取或者发送数据出错时,将被调用。error 之后,连接将被关闭。
timeout:当一个连接在约定的时间内没有发生可读或者可写事件,将被调用。timeout 之后,连接将被关闭。
close:当一个 TCP 连接被关闭时,无论是正常关闭,还是因为 error/timeout 而关闭。
 
3.SP_Handler 函数调用时机
当需要调用 SP_Handler 的 start/handle/error/timeout 方法时,相关的参数将被放入队列,然后由线程池来负责执行 SP_Handler 对应的方法。因此在 start/handle/error/timeout/close 中可以使用同步
操作来编程,可以直接使用阻塞型 I/O 。
 
 

kevinlynx

unread,
May 26, 2008, 9:07:05 PM5/26/08
to 高性能网络编程邮件列表
昨天稍微看了下例子(testiocpchat)代码和库相关源码,在完全没有文档的情况下,小弟看着有点吃力。

关中刀客

unread,
May 26, 2008, 9:50:34 PM5/26/08
to 高性能网络编程邮件列表
不知道这个超时机制带来的好处和坏处那个多一些?有没有很大的必要来实现这个东西?


On 5月24日, 下午1时12分, "stephen.nil" <stephen....@gmail.com> wrote:
> 用完成端口(IOCP)实现一个简单的服务器框架
>
> IOCP 对于高并发的应用程序提供了良好的支持,使得开发高并发的应用程序的难度降低了很多。
> IOCP 作为系统底层的 API ,保持了尽可能高的灵活性,对于很多复杂的情况,IOCP 也一样适用。
> 这种灵活性是一种双刃剑,对于复杂的情况,没有这种灵活性就没有办法完成工作。
> 但是对于简单的情况,这种灵活性就带来了额外的负担。
>
> 这里将要提到的这个服务器框架(spserver/iocp版),主要是针对以下的这几种典型的应用程序
> 1.echo/http 类型,server 不需要在多个 client 之间转发消息
> 2.chatroom 类型,server 在多个 client 之间转发消息
>
> 在使用 IOCP 来实现这两类应用程序的时候,会遇到一些共同的问题
> 1.当连接断开的时候,在 server 端为这个连接分配的资源如何妥善地进行释放
> 2.如何为每一个 IO 操作设定一个超时时间,以使得 server 能够更好地了解每个连接的当前情况
>
> spserver 通过使用 hash 表的方式来解决资源释放的问题,具体的讨论可以参考http://groups.google.com/group/dev4server/browse_thread/thread/29a910...
>
> spserver 通过使用类似 libevent 的 min-heap 来解决 IO 操作超时的问题,具体可以参考http://groups.google.com/group/dev4server/browse_thread/thread/0978e2...
>
> 在上一个版本(0.9)中,spserver 在 windows 平台还是基于 libevent + pthread-win32 来实现的。
> 最新版本(0.9.1),在 windows 平台改成使用 IOCP + Windows Thread 来重新实现。
> 最新版本在 Windows 平台已经不再需要依赖第三方的库了。
>
> 主页和下载地址http://code.google.com/p/spserver/http://spserver.googlecode.com/files/spserver-0.9.1.src.tar.gz

jinbiao xu

unread,
May 26, 2008, 10:58:00 PM5/26/08
to dev4s...@googlegroups.com
严重支持 坚持下去!
 
up 

 
在08-5-27,关中刀客 <guanzho...@gmail.com> 写道:

jerry

unread,
May 27, 2008, 3:23:51 AM5/27/08
to 高性能网络编程邮件列表
学习中,会一直关注的

On 5月27日, 上午10时58分, "jinbiao xu" <xjb...@gmail.com> wrote:
> 严重支持 坚持下去!
>
> up
>
> 在08-5-27,关中刀客 <guanzhongda...@gmail.com> 写道:
>
>
>
>
>
> > 不知道这个超时机制带来的好处和坏处那个多一些?有没有很大的必要来实现这个东西?
>
> > On 5月24日, 下午1时12分, "stephen.nil" <stephen....@gmail.com> wrote:
> > > 用完成端口(IOCP)实现一个简单的服务器框架
>
> > > IOCP 对于高并发的应用程序提供了良好的支持,使得开发高并发的应用程序的难度降低了很多。
> > > IOCP 作为系统底层的 API ,保持了尽可能高的灵活性,对于很多复杂的情况,IOCP 也一样适用。
> > > 这种灵活性是一种双刃剑,对于复杂的情况,没有这种灵活性就没有办法完成工作。
> > > 但是对于简单的情况,这种灵活性就带来了额外的负担。
>
> > > 这里将要提到的这个服务器框架(spserver/iocp版),主要是针对以下的这几种典型的应用程序
> > > 1.echo/http 类型,server 不需要在多个 client 之间转发消息
> > > 2.chatroom 类型,server 在多个 client 之间转发消息
>
> > > 在使用 IOCP 来实现这两类应用程序的时候,会遇到一些共同的问题
> > > 1.当连接断开的时候,在 server 端为这个连接分配的资源如何妥善地进行释放
> > > 2.如何为每一个 IO 操作设定一个超时时间,以使得 server 能够更好地了解每个连接的当前情况
>
> > > spserver 通过使用 hash 表的方式来解决资源释放的问题,具体的讨论可以参考
> >http://groups.google.com/group/dev4server/browse_thread/thread/29a910...
>
> > > spserver 通过使用类似 libevent 的 min-heap 来解决 IO 操作超时的问题,具体可以参考
> >http://groups.google.com/group/dev4server/browse_thread/thread/0978e2...
>
> > > 在上一个版本(0.9)中,spserver 在 windows 平台还是基于 libevent + pthread-win32 来实现的。
> > > 最新版本(0.9.1),在 windows 平台改成使用 IOCP + Windows Thread 来重新实现。
> > > 最新版本在 Windows 平台已经不再需要依赖第三方的库了。
>
> > > 主页和下载地址
> >http://code.google.com/p/spserver/http://spserver.googlecode.com/file...
> > > 操作来编程,可以直接使用阻塞型 I/O 。- 隐藏被引用文字 -
>
> - 显示引用的文字 -

stephen.nil

unread,
May 27, 2008, 12:01:38 PM5/27/08
to 高性能网络编程邮件列表

通常一个 client 连到 server 上,server 都会相应分配一些资源,用于为这个 client 提供服务。
常见的比如生成一个 session 对象,用于记录这个 client 的一些状态。

在异常情况下,有一个简单的超时控制机制,有利于尽快回收这些资源。
比如 《unix 网络编程(第1卷第二版中文版)》 5.14 节中提到
如果 client 端意外崩溃,源于 Berkeley 的实现由于尝试重传而会导致有约 9 分钟的等待时间。
如果在能够在做一个 IO 操作的时候,指定一个超时时间,有利于在这些异常情况下,加快发现问题,并回收资源。

Best regards,

stephen.nil
2008-05-27

>>>
>不知道这个超时机制带来的好处和坏处那个多一些?有没有很大的必要来实现这个东西?
>

stephen.nil

unread,
May 27, 2008, 12:11:43 PM5/27/08
to 高性能网络编程邮件列表

testiocpchat 这个例子是用于示范怎么来通过 server 在多个 client 之间中转消息。

这个例子的功能是模仿常见的聊天室功能,每个 telnet 上来的用户会受到当前在线用户发送的消息,他发出的消息也会发送给所有人。

SP_OnlineSidList 用于记录当前在线用户的 Sid (可以认为是 session id),sid 是 server 内部用于标识不同用户的 key 。
SP_ChatHandler 这个用于实现具体的 chat 功能的逻辑
SP_ChatCompletionHandler
这个比较特别一点。用户发出的消息在被处理了之后,最后会有这个类负责善后的工作。
因为在 SP_ChatHandler::handle 里面做了逻辑处理(指定要转发的 sid)之后,这个消息就有 server 框架负责处理。
SP_ChatHandler::handle 里面暂时是无法知道这个处理结果的(是全部人都发送成功了,还是只有部分人发送成功)。
只有等 server 框架处理了之后,才能得到这个结果。有了这个结果之后(即所有接受者都尝试了之后),
就会把这个结果交给 SP_ChatCompletionHandler 来处理。对于全部成功的情况,如果不需要记录的话,只需要简单地 delete 消息就行了。
如果有发送失败的话,那么可以在这里进行一些处理(比如重新传输,或者仅仅只进行记录)。

Best regards,

stephen.nil
2008-05-28

>>>
>昨天稍微看了下例子(testiocpchat)代码和库相关源码,在完全没有文档的情况下,小弟看着有点吃力。
>>


Reply all
Reply to author
Forward
0 new messages