1。分析socket讀寫流程
void SP_EventCallback :: onRead( int fd, short events, void * arg )
{
SP_Session * session = (SP_Session*)arg;
session->setReading( 0 );
SP_Sid_t sid = session->getSid();
if( EV_READ & events ) {
int len = session->getIOChannel()->receive( session );
if( len > 0 ) {
session->addRead( len );
if( 0 == session->getRunning() ) {
SP_MsgDecoder * decoder = session->getRequest()->getMsgDecoder();
if( SP_MsgDecoder::eOK == decoder->decode( session->getInBuffer() ) ) {
SP_EventHelper::doWork( session );
}
}
addEvent( session, EV_READ, -1 );
} else {
int saved = errno;
if( 0 != errno ) {
sp_syslog( LOG_WARNING, "session(%d.%d) read error, errno %d, status %d",
sid.mKey, sid.mSeq, errno, session->getStatus() );
}
if( EAGAIN != saved ) {
if( 0 == session->getRunning() ) {
SP_EventHelper::doError( session );
} else {
sp_syslog( LOG_NOTICE, "session(%d.%d) busy, process session error later",
sid.mKey, sid.mSeq );
// If this session is running, then onResponse will add write event for this session.
// It will be processed as write fail at the last. So no need to re-add event here.
}
} else {
addEvent( session, EV_READ, -1 );
}
}
} else {
if( 0 == session->getRunning() ) {
SP_EventHelper::doTimeout( session );
} else {
sp_syslog( LOG_NOTICE, "session(%d.%d) busy, process session timeout later",
sid.mKey, sid.mSeq );
// If this session is running, then onResponse will add write event for this session.
// It will be processed as write fail at the last. So no need to re-add event here.
}
}
}
拿到讀事件之後。int len = session->getIOChannel()->receive( session ); 通過IOChannel到底層 socket中讀取資料,此時的IOChannel實際上轉入SP_DefaultIOChannel的receive方法,該方法執行libevent中的evbuffer_read方法讀資料。
注意此時讀資料是由onRead觸發的,所以不會阻塞,讀完資料後,執行SP_MsgDecoder::eOK == decoder->decode( session->getInBuffer(),此時會轉入到協議處理流程,由協議解碼器去解碼緩衝區,得到相關協議。我們來看一個典型的解碼器寫法
int SP_LineMsgDecoder :: decode( SP_Buffer * inBuffer )
{
if( NULL != mLine ) free( mLine );
mLine = inBuffer->getLine();
return NULL == mLine ? eMoreData : eOK;
}
底層緩衝區是inBuffer,從底層緩衝讀一個完整的行,讀到之後就將ret=eOK.
SP_EventHelper::doWork( session ); 回去找協議處理器去執行。我們看一個典型的代碼。
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_MultiLineMsgDecoder() );
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_MultiLineMsgDecoder * decoder = (SP_MultiLineMsgDecoder*)request->getMsgDecoder();
SP_CircleQueue * queue = decoder->getQueue();
int ret = 0;
for( ; NULL != queue->top(); ) {
char * line = (char*)queue->pop();
if( 0 != strcasecmp( line, "quit" ) ) {
response->getReply()->getMsg()->append( line );
response->getReply()->getMsg()->append( "/r/n" );
} else {
response->getReply()->getMsg()->append( "Byebye/r/n" );
ret = -1;
}
free( line );
}
return ret;
}
virtual void error( SP_Response * response ) {}
virtual void timeout( SP_Response * response ) {}
virtual void close() {}
};
這是 testecho伺服器的代碼,SP_EventHelper::doWork( session ); 最終會定位到virtual int handle( SP_Request * request, SP_Response * response )來執行
具體執行過程如下
void SP_EventHelper :: doWork( SP_Session * session )
{
if( SP_Session::eNormal == session->getStatus() ) {
session->setRunning( 1 );
SP_EventArg * eventArg = (SP_EventArg*)session->getArg();
eventArg->getInputResultQueue()->push( new SP_SimpleTask( worker, session, 1 ) );
} else {
SP_Sid_t sid = session->getSid();
char buffer[ 16 ] = { 0 };
session->getInBuffer()->take( buffer, sizeof( buffer ) );
sp_syslog( LOG_WARNING, "session(%d.%d) status is %d, ignore [%s...] (%dB)",
sid.mKey, sid.mSeq, session->getStatus(), buffer, session->getInBuffer()->getSize() );
session->getInBuffer()->reset();
}
}
SP_EventHelper::doWork( session ) 轉到 eventArg->getInputResultQueue()->push( new SP_SimpleTask( worker, session, 1 ) ); 執行
eventArg內部包含InputResultQueue隊列,我們回到int SP_Server :: start()
int SP_Server :: start()
{
#ifdef SIGPIPE
/* Don't die with SIGPIPE on remote read shutdown. That's dumb. */
signal( SIGPIPE, SIG_IGN );
#endif
int ret = 0;
int listenFD = -1;
ret = SP_IOUtils::tcpListen( mBindIP, mPort, &listenFD, 0 );
if( 0 == ret ) {
SP_EventArg eventArg( mTimeout );
// Clean close on SIGINT or SIGTERM.
struct event evSigInt, evSigTerm;
signal_set( &evSigInt, SIGINT, sigHandler, this );
event_base_set( eventArg.getEventBase(), &evSigInt );
signal_add( &evSigInt, NULL);
signal_set( &evSigTerm, SIGTERM, sigHandler, this );
event_base_set( eventArg.getEventBase(), &evSigTerm );
signal_add( &evSigTerm, NULL);
SP_AcceptArg_t acceptArg;
memset( &acceptArg, 0, sizeof( SP_AcceptArg_t ) );
if( NULL == mIOChannelFactory ) {
mIOChannelFactory = new SP_DefaultIOChannelFactory();
}
acceptArg.mEventArg = &eventArg;
acceptArg.mHandlerFactory = mHandlerFactory;
acceptArg.mIOChannelFactory = mIOChannelFactory;
acceptArg.mReqQueueSize = mReqQueueSize;
acceptArg.mMaxConnections = mMaxConnections;
acceptArg.mRefusedMsg = mRefusedMsg;
struct event evAccept;
event_set( &evAccept, listenFD, EV_READ|EV_PERSIST,
SP_EventCallback::onAccept, &acceptArg );
event_base_set( eventArg.getEventBase(), &evAccept );
event_add( &evAccept, NULL );
SP_Executor workerExecutor( mMaxThreads, "work" );
SP_Executor actExecutor( 1, "act" );
SP_CompletionHandler * completionHandler = mHandlerFactory->createCompletionHandler();
/* Start the event loop. */
while( 0 == mIsShutdown ) {
event_base_loop( eventArg.getEventBase(), EVLOOP_ONCE );
for( ; NULL != eventArg.getInputResultQueue()->top(); ) {
SP_Task * task = (SP_Task*)eventArg.getInputResultQueue()->pop();
workerExecutor.execute( task );
}
for( ; NULL != eventArg.getOutputResultQueue()->top(); ) {
SP_Message * msg = (SP_Message*)eventArg.getOutputResultQueue()->pop();
void ** arg = ( void** )malloc( sizeof( void * ) * 2 );
arg[ 0 ] = (void*)completionHandler;
arg[ 1 ] = (void*)msg;
actExecutor.execute( outputCompleted, arg );
}
}
delete completionHandler;
……
}
SP_Executor workerExecutor( mMaxThreads, "work" ); SP_Executor actExecutor( 1, "act" );是兩個重要的執行線程池。
在 for( ; NULL != eventArg.getInputResultQueue()->top(); ) {
SP_Task * task = (SP_Task*)eventArg.getInputResultQueue()->pop();
workerExecutor.execute( task );
}
迴圈中, eventArg.getInputResultQueue將任務出隊,然後使用workerExecutor.execute( task );執行任務,而task封裝了virtual int handle( SP_Request * request, SP_Response * response )
總結:到此為止,我們分析清楚,用戶端資料到達socket,觸發onRead事件,調用decoder判斷是否協議解碼成功(eOK),解碼成功則將handel方法封裝到Task,送入InputResultQueue隊列,由workerExecutor線程池出隊並執行。
資料寫出與這個過程類似。
(未完待續……)