论坛首页 综合技术论坛

SPServer : 一个基于线程池(包括HAHS和LF)的高并发 server 框架

浏览 57557 次
该帖已经被评为精华帖
作者 正文
   发表时间:2007-08-08  
byjove 写道
SP_MsgDecoder 实现时,COPY缓冲能不能很好的解决?


是指解决什么问题?是说 SP_MsgDecoder 实现的时候,缓冲有问题?
0 请登录后投票
   发表时间:2007-08-08  
引用

是指解决什么问题?是说 SP_MsgDecoder 实现的时候,缓冲有问题?

int SP_DotTermMsgDecoder :: decode( SP_Buffer * inBuffer )
{
if( NULL != mBuffer ) free( mBuffer );

const char * pos = (char*)inBuffer->find( "\r\n.\r\n", 5 );

if( NULL == pos ) {
pos = (char*)inBuffer->find( "\n.\n", 3 );
}

if( NULL != pos ) {
int len = pos - (char*)inBuffer->getBuffer();

mBuffer = (char*)malloc( len + 1 );
memcpy( mBuffer, inBuffer->getBuffer(), len );
mBuffer[ len ] = '\0';

inBuffer->erase( len );

在这里重新分配了内存,有没有更好的办法?

还是就是如果有两个数据包同时被接收,后面一个包怎么处理?

不知道我说的对不对,还有几个问题想要请救,方便留个MSN?
我的是yinaesop@msn.com
0 请登录后投票
   发表时间:2007-08-08  
byjove 写道

int SP_DotTermMsgDecoder :: decode( SP_Buffer * inBuffer )
{
	if( NULL != mBuffer ) free( mBuffer );

	const char * pos = (char*)inBuffer->find( "\r\n.\r\n", 5 );	

	if( NULL == pos ) {
		pos = (char*)inBuffer->find( "\n.\n", 3 );
	}

	if( NULL != pos ) {
		int len = pos - (char*)inBuffer->getBuffer();

		mBuffer = (char*)malloc( len + 1 );
		memcpy( mBuffer, inBuffer->getBuffer(), len );
		mBuffer[ len ] = '\0';

		inBuffer->erase( len );

在这里重新分配了内存,有没有更好的办法?

还是就是如果有两个数据包同时被接收,后面一个包怎么处理?

不知道我说的对不对,还有几个问题想要请救,方便留个MSN?
我的是yinaesop@msn.com


对于一个 fd,如果有两个数据包同时被读入 inBuffer 里面,后面的数据包将在前一个数据包被处理完之后,继续被处理。这两个数据包不会被并行处理,因为 SP_Handler 和 fd 是一一对应的,如果并行处理数据包,将导致 SP_Handler 可能同时被多个线程调用,这样将使得 SP_Handler 非常难于实现。

关于重新分配内存的问题:inBuffer 是使用 read 系统调用从 fd 里面读入的。使用 read 的时候,是尽可能地读入数据,因此 inBuffer 里面的数据就不一定是逻辑上的一个数据包。由于 inBuffer 里面的数据不一定正好是一个逻辑上的数据包,也有可能多于一个逻辑上的数据包,而为了能够提供给应用层(SP_Handler::handle)方便地使用,因此不得不做了一次数据 copy 。如果 inBuffer 里面的数据不需要做逻辑上的 decode 操作,那么这次 copy 的确有浪费之嫌。如果 inBuffer 里面的数据需要经过 decode 操作(比如解压缩,解密,或者协议解释),那么这次 copy 操作就是无法避免的了。比如 http msg decoder 的实现

int SP_HttpRequestDecoder :: decode( SP_Buffer * inBuffer )
{
	if( inBuffer->getSize() > 0 ) {
		int len = mParser->append( inBuffer->getBuffer(), inBuffer->getSize() );

		inBuffer->erase( len );

		return mParser->isCompleted() ? eOK : eMoreData;
	} else {
		return eMoreData;
	}
}


另外有一点,上面的 decode( SP_Buffer * inBuffer ) 的实现是用一种最简单,但内存使用最多的方式来实现的。其实可以在 else 中把 inBuffer 复制到 mBuffer 中,不过前面的判断是否已经完整读取到一个数据包的逻辑就变得比较复杂了。这样可以避免把内容一直放在 inBuffer 。

int SP_DotTermMsgDecoder :: decode( SP_Buffer * inBuffer )
{
。。。。。。
	if( NULL != pos ) {
	} else {
		// 复制 inBuffer 的内容到 mBuffer,同时清空 inBuffer
	}


说到这里,可能就是一个关于 SPServer 适用范围的问题了,如果多出来的这次 copy 操作是无法接受的,那么可能就需要另外的专门的解决方案了。

关于 SP_MsgDecoder 的设计思路,当初主要是模仿 java 的 mina 框架。mina 框架号称
引用

如果想实现复杂的如LDAP这样的协议怎么办呢?它似乎是一个恶梦,因为IO层没有帮助你分离‘message解析’和‘实际的业务逻辑(比如访问一个目录数据库)’。MINA提供了一个协议层来解决这个问题。协议层将ByteBuffer事件转换成高层的POJO事件:

就像前面提到的,你只需撰写面向POJO的message而不是ByteBuffer的。ProtocolEncoder 将message对象解释成ByteBuffers以便IO层能够将他们输出到socket。
0 请登录后投票
   发表时间:2007-08-12  
byjove 写道
在这里重新分配了内存,有没有更好的办法?


想到了一个方法,就是为 SP_Buffer 增加一个 take 的方法,如下
class SP_Buffer {
public:
    SP_Buffer * take();
};


通过这个方法,可以直接把 SP_Buffer 底层控制的内存块拿出来用,同时 SP_Buffer 自身重新分配控制块。这个控制块很小,大概只有 50 bytes 。这样可以减少内存的复制操作。

使用的例子可以参考 spserver/openssl/sptunnelimpl.cpp 文件中的
int SP_TunnelDecoder :: decode( SP_Buffer * inBuffer ) 函数。
0 请登录后投票
   发表时间:2007-08-14  
好像没看到调用event_base_free,是不是因为没办法取消所有注册的event? 我刚好也遇到这个问题,总是不能很好地处理退出。
0 请登录后投票
   发表时间:2007-08-14  
引用
好像没看到调用event_base_free,是不是因为没办法取消所有注册的event? 我刚好也遇到这个问题,总是不能很好地处理退出。


SPServer内部保存了所有注册的 struct event,可以取消所有注册的 event 的。不过 libevent 1.1 和 1.2 中有 bug ,在 event_init 的时候,libevent 自身注册了一些 event ,这些 event 在 event_base_free 的时候,没有取消,导致 event_base_free 会被挂住。另外 event_base_free 是从 1.2 开始才有的,1.1 都没有 event_base_free ,所以目前在 SPServer 中都把 event_base_free 调用注释掉了。
0 请登录后投票
论坛首页 综合技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics