狐狸大叔,这名字是我一个同事自称的,所以不要到处搜哈哈,搜不出来的
最近几天看了狐狸大叔写的C++游戏demo框架(未完善,仅一个demo而已),我觉得太好了,所以拿出来跟大家分享一出。
框架结构:vs2008 boost mysql
本人也是菜鸟一个,但有强烈的追求看到高效的代码。如有误导各位,请痛批~
今天讲的是Socket方面的创建、连接、管理。
【总体的设计思路】
已经知道这块地最多只能住下1000个人(玩家),那就先建好1000个房子(player & socket),同时也生产好了1000接待者(每一个接待者只接一个人accept),每来一名客人就简单记录下他的地址与信息就可入住,这就是高效。
单位 单位管理者关系
单位 单位管理者关系
单位中有list<单位*>、 vector<单位*> 有定义typedef 指针管理的关系
将这些放在该类的最后面
typedef list<netSocket*> SocketList;
typedef list<netSocket*>::iterator SocketListIter;
typedef vector<netSocket*> SocketVector;
typedef vector<netSocket*>::iterator SocketVectorIter;
单位管理者都有这样一个list 、vector,还有一个单位的指针数组缓存如 netSocket* mSocket[ MAX_SOCKET ];
每当要new一个单位管理者的时候,就会创建一定数量的单位,并将放入缓存的指针数组
单位管理者有几个容器:
netSocket* mSocket[MAX_SOCKET]; // 缓存
SocketVector mFreeSocket; // 空闲的房子
SocketVector mUsedSocket; // 已经有人住的房子
当一个玩家进入,则从mFreeSocket.begin()拿出来一个socket放入到mUserSocket.push_back(mFreeSocket.begin())中去,
mFreeSocket.earse(mFreeSocket.begin());
当一个玩家断开连接,则与上面的流程相反,当操作这些时,要适当的做锁
代码如下,怒不解讲
netSocket.h
//
// socket.h
// test
//
// Created by fox on 12-11-19.
// Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//
#ifndef netSocket_h
#define netSocket_h
#include "netIOBuffer.h"
namespace FOXSDK
{
class netServer;
enum SocketRecvType
{
FSRS_NULL = 0,
FSRS_HEAD = 1,
FSRS_BODY = 2,
FSRS_COUNT = 3
};
struct netSocketHead
{
fuint16 size;
fuint16 type;
};
class netSocket : public tcp::socket
{
public:
friend class netServer;
netSocket( io_service& io_service );
fvoid RecvMsg( const error_code& ec, size_t bytes_transferred );
fvoid SendMsg( const error_code& ec, size_t bytes_transferred );
fvoid ParkMsg( netSocketHead* head );
fvoid SendMsg();
fint32 ReadMsg( netSocketHead** head );
fvoid RemoveMsg( int len );
fint32 IsVaild()
{
return mVaild;
}
fvoid Close()
{
mClose = 1;
}
protected:
deadline_timer mTimer;
virtual ~netSocket();
fvoid Run();
virtual void close();
fvoid Clear();
//mutex mMutex;
fint32 mVaild;
fvoid ReadHead();
fvoid ReadBody();
void HandleWait( const error_code& error );
netServer* mServer;
netIOBuffer mIBuffer;
netIOBuffer mOBuffer;
mutable_buffers_1 mHeadBuffer;
mutable_buffers_1 mBodyBuffer;
mutable_buffers_1 mSendBuffer1;
fbyte mRecvBuffer[ MAX_SOCKET_BUFFER ];
fint32 mRecvStage;
fint32 mBodyLen;
fbyte mSendBuffer[ MAX_SOCKET_BUFFER ];
fint32 mSend;
fint32 mClose;
};
typedef list<netSocket*> SocketList;
typedef list<netSocket*>::iterator SocketListIter;
typedef vector<netSocket*> SocketVector;
typedef vector<netSocket*>::iterator SocketVectorIter;
}
#endif
netSocket.cpp
//
// socket.cpp
// test
//
// Created by fox on 12-11-19.
// Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//
#include <iostream>
#include "netsocket.h"
#include "netserver.h"
#include "logManager.h"
using namespace FOXSDK;
//mutex netSocket::mMutex;
netSocket::netSocket( io_service& io_service ):tcp::socket ( io_service ) ,
mHeadBuffer( buffer( mRecvBuffer , sizeof(netSocketHead) ) ),
mBodyBuffer( buffer( mRecvBuffer + sizeof(netSocketHead) , MAX_SOCKET_BUFFER - sizeof(netSocketHead) ) ) ,
mSendBuffer1( buffer( mSendBuffer , MAX_SOCKET_BUFFER ) ),
mBodyLen( 0 ) , mRecvStage( FSRS_NULL ) ,
mVaild( 0 ) ,
mSend( 0 ) ,
mClose( 0 ) ,
mTimer( io_service )
{
mServer = (netServer*)&io_service;
mIBuffer.InitBuffer( MAX_IBUFFER_CACHE );
mOBuffer.InitBuffer( MAX_OBUFFER_CACHE );
}
netSocket::~netSocket()
{
}
void netSocket::close()
{
tcp::socket::close();
}
fint32 netSocket::ReadMsg( netSocketHead** head )
{
if ( !mVaild )
{
return 0;
}
if ( mIBuffer.GetLen() )
{
*head = (netSocketHead*)mIBuffer.GetBuffer();
return 1;
}
return 1;
}
fvoid netSocket::RemoveMsg( fint32 len )
{
mIBuffer.RemoveBuffer( len );
}
fvoid netSocket::RecvMsg( const error_code& ec, size_t bytes_transferred )
{
mTimer.cancel();
if ( ec )
{
Clear();
mServer->SetAccept( this );
return;
}
else
{
if ( mClose )
{
Clear();
mServer->SetAccept( this );
return;
}
else
{
switch ( mRecvStage )
{
case (fint32)FSRS_HEAD:
{
netSocketHead* head = (netSocketHead*)mRecvBuffer;
mBodyLen = head->size;
if ( mBodyLen == sizeof( netSocketHead ) )
{
mIBuffer.Write( mRecvBuffer , mBodyLen );
ReadHead();
return;
}
else
{
mRecvStage = FSRS_BODY;
ReadBody();
return;
}
}
break;
case (fint32)FSRS_BODY:
{
mIBuffer.Write( mRecvBuffer , mBodyLen );
mRecvStage = FSRS_HEAD;
ReadHead();
return;
}
break;
default:
{
assert( 0 );
}
break;
}
}
}
}
fvoid netSocket::Clear()
{
if ( !mVaild )
{
return;
}
mVaild = 0;
mRecvStage = FSRS_NULL;
mBodyLen = 0;
mIBuffer.ClearBuffer();
mOBuffer.ClearBuffer();
mSend = 0;
//FLOG( LOGLEVEL0 , "OnNetMsgExit %s" , this->remote_endpoint().address().to_string().c_str() );
mTimer.cancel();
close();
}
fvoid netSocket::ParkMsg( netSocketHead* head )
{
mOBuffer.Write( (fbyte*)head , head->size );
}
fvoid netSocket::SendMsg()
{
if ( !mSend )
{
int len = mOBuffer.ReadRemove( &mSendBuffer );
if ( len )
{
async_write( *this , mSendBuffer1 , transfer_at_least( len ) , boost::bind( &netSocket::SendMsg , this , placeholders::error, placeholders::bytes_transferred ) ) ;
}
mSend = 1;
}
}
fvoid netSocket::SendMsg( const error_code& ec, size_t bytes_transferred )
{
if ( ec )
{
Clear();
mServer->SetAccept( this );
}
else
{
int len = mOBuffer.ReadRemove( &mSendBuffer );
if ( len )
{
async_write( *this , mSendBuffer1 , transfer_at_least( len ) , boost::bind( &netSocket::SendMsg , this , placeholders::error, placeholders::bytes_transferred ) ) ;
}
else
{
mSend = 0;
}
}
}
void netSocket::HandleWait( const error_code& error )
{
if ( error )
{
return;
}
this->Clear();
mServer->SetAccept( this );
}
fvoid netSocket::ReadHead()
{
async_read( *this , mHeadBuffer ,
transfer_at_least( sizeof(netSocketHead) ) ,
boost::bind( &netSocket::RecvMsg , this , placeholders::error, placeholders::bytes_transferred ) ) ;
mTimer.expires_from_now( boost::posix_time::seconds( 120 ) );
mTimer.async_wait( boost::bind( &netSocket::HandleWait , this , boost::asio::placeholders::error ) );
}
fvoid netSocket::ReadBody()
{
async_read( *this , mBodyBuffer ,
transfer_at_least( mBodyLen - sizeof(netSocketHead) ) ,
boost::bind( &netSocket::RecvMsg , this , placeholders::error, placeholders::bytes_transferred ) ) ;
mTimer.expires_from_now( boost::posix_time::seconds( 120 ) );
mTimer.async_wait(boost::bind( &netSocket::HandleWait , this , boost::asio::placeholders::error));
}
fvoid netSocket::Run()
{
mRecvStage = FSRS_HEAD;
mVaild = 1;
ReadHead();
}
netServer.h
//
// server.h
// test
//
// Created by fox on 12-11-20.
// Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//
#ifndef netServer_h
#define netServer_h
#include "netSocket.h"
namespace FOXSDK
{
typedef fvoid(*netServerHandler)(netSocket* s);
class netServer : public io_service
{
public:
friend class netSocket;
netServer();
virtual ~netServer();
fvoid SetAccept( netSocket* socket );
fvoid SetAddress( const fbyte* ip , fuint16 port );
fvoid SetHandler( netServerHandler enter , netServerHandler exit , netServerHandler msg );
fvoid ServerRun();
fvoid ServerUpdate();
fvoid Start();
fvoid Stop();
fvoid Update();
protected:
netServerHandler OnEnter;
netServerHandler OnExit;
netServerHandler OnMsg;
fvoid HandleStart();
fvoid HandleAccept( const boost::system::error_code& error , netSocket* socket );
netSocket* GetFreeSocket();
SocketVector mFreeSocket;
SocketVector mUsedSocket;
SocketVector mAcceptSocket;
netSocket* mSocket[ MAX_SOCKET ];
tcp::endpoint mServerAddr;
tcp::acceptor mAcceptor;
thread mServiceThread;
mutex mClientListMutex;
};
}
#endif
netServer.cpp
//
// server.cpp
// test
//
// Created by fox on 12-11-20.
// Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//
#include <iostream>
#include "netServer.h"
using namespace FOXSDK;
netServer::netServer()
: mAcceptor( *this )
{
// 经典从这里开始
for ( int i = 0 ; i < MAX_SOCKET ; i++ )
{
mSocket[ i ] = new netSocket( *this ); // 先创建好socket,并放入到缓存中去
mFreeSocket.push_back( mSocket[i] ); // 加入空闲的socket
}
}
netServer::~netServer()
{
}
netSocket* netServer::GetFreeSocket()
{
if ( mFreeSocket.empty() )
{
return NULL;
}
netSocket* socket = *mFreeSocket.begin();
mFreeSocket.erase( mFreeSocket.begin() );
return socket;
}
fvoid netServer::SetAccept( netSocket* socket )
{
mAcceptor.async_accept( *socket ,
boost::bind( &netServer::HandleAccept , this , boost::asio::placeholders::error ,
socket ) );
}
fvoid netServer::SetAddress( const char* ip , unsigned short port )
{
error_code ec;
mServerAddr = tcp::endpoint( address::from_string( ip , ec ) , port );
assert(!ec);
}
fvoid netServer::SetHandler( netServerHandler enter , netServerHandler exit , netServerHandler msg )
{
OnEnter = enter;
OnExit = exit;
OnMsg = msg;
}
fvoid netServer::ServerRun()
{
while (true)
{
error_code ec;
try
{
run(ec);
break;
}
catch ( std::exception& e )
{
}
}
}
fvoid netServer::ServerUpdate()
{
// while (true)
// {
// for ( fint32 i = 0 ; i < MAX_SOCKET ; ++i )
// {
// mSocket[ i ]->SendMsg();
// }
// }
}
fvoid netServer::Start()
{
thread t(boost::bind( &netServer::HandleStart , this ) );
this_thread::yield();
t.swap( mServiceThread );
}
fvoid netServer::HandleStart()
{
error_code ec;
mAcceptor.open( mServerAddr.protocol(), ec );
assert( !ec );
mAcceptor.set_option(tcp::acceptor::reuse_address(true) , ec );
assert( !ec );
mAcceptor.bind( mServerAddr , ec );
assert( !ec );
mAcceptor.listen( socket_base::max_connections , ec );
assert( !ec );
// 这里把所有的socket都拿去accept,很聪明吧
for ( int i = 0 ; i < MAX_SOCKET ; ++i )
{
SetAccept( mSocket[ i ] );
}
thread_group tg;
for ( int i = 0; i < MAX_THREAD ; ++i )
{
tg.create_thread(boost::bind( &netServer::ServerRun , this ) );
}
this_thread::yield();
tg.join_all();
//ServerUpdate();
}
fvoid netServer::Stop()
{
}
fvoid netServer::Update()
{
size_t size = mAcceptSocket.size();
if ( size )
{
mutex::scoped_lock lock( mClientListMutex );
size = mAcceptSocket.size();
for ( int i = 0 ; i < size ; ++i )
{
SocketVectorIter iter = std::find( mUsedSocket.begin() , mUsedSocket.end() , mAcceptSocket[i] );
if ( iter != mUsedSocket.end() )
{
(*OnExit)( *iter );
mUsedSocket.erase( iter );
//FLOG( LOGLEVEL0 , "OnNetMsgExit %d" , mUsedSocket.size() );
}
(*OnEnter)( mAcceptSocket[i] );
mUsedSocket.push_back( mAcceptSocket[i] );
//FLOG( LOGLEVEL0 , "OnNetMsgEnter %d" , mUsedSocket.size() );
}
mAcceptSocket.clear();
lock.unlock();
}
size = mUsedSocket.size();
for ( int i = 0 ; i < size ; ++i )
{
netSocketHead* head = NULL;
int b = mUsedSocket[ i ]->ReadMsg( &head );
if ( !b )
{
(*OnExit)( mUsedSocket[ i ] );
mUsedSocket.erase( mUsedSocket.begin() + i );
//FLOG( LOGLEVEL0 , "OnNetMsgExit %d" , mUsedSocket.size() );
i--;
size--;
}
else if ( head )
{
(*OnMsg)( mUsedSocket[ i ] );
mUsedSocket[ i ]->RemoveMsg( head->size );
}
}
}
fvoid netServer::HandleAccept( const boost::system::error_code& error , netSocket* socket )
{
if ( error )
{
socket->Clear();
SetAccept( socket );
}
else
{
// on enter
mutex::scoped_lock lock( mClientListMutex );
// SocketVectorIter iter = std::find( mUsedSocket.begin() , mUsedSocket.end() , socket );
// if ( iter != mUsedSocket.end() )
// {
// OnExit();
// mUsedSocket.erase( iter );
// }
mAcceptSocket.push_back( socket );
socket->mClose = 0;
socket->Run();
lock.unlock();
}
}
main.cpp
//
// main.cpp
// test
//
// Created by fox on 12-11-19.
// Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//
#include "netServer.h"
#include "dbDataBase.h"
#include "netMsgHandler.h"
#include "creatureManager.h"
using namespace FOXSDK;
fvoid RunServerExit();
#ifdef WIN32
void InitError()
{
}
#else
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
using namespace FOXSDK;
static void printReason(int sig, siginfo_t * info, void *secret)
{
RunServerExit();
void* array[10];
size_t size;
#ifdef F_USE_PRINT
char** strings;
size_t i;
size = backtrace( array , 10 );
strings = backtrace_symbols( array, (int)size );
printf( "obtained %zd stack frames.\n" , size );
for ( i = 0; i < size; i++ )
{
printf( "%s\n" , strings[ i ] );
}
free( strings );
#else
int fd = open( "error.log", O_CREAT | O_WRONLY );
size = backtrace( array , 10 );
backtrace_symbols_fd( array , (int)size , fd );
close( fd );
#endif
exit(0);
}
void InitError()
{
struct sigaction myAction;
myAction.sa_sigaction = printReason;
sigemptyset( &myAction.sa_mask );
myAction.sa_flags = SA_RESTART | SA_SIGINFO;
sigaction( SIGSEGV , &myAction , NULL );
sigaction( SIGUSR1 , &myAction , NULL );
sigaction( SIGFPE , &myAction , NULL );
sigaction( SIGILL , &myAction , NULL );
sigaction( SIGBUS , &myAction , NULL );
sigaction( SIGABRT , &myAction , NULL );
sigaction( SIGSYS , &myAction , NULL );
sigaction( SIGINT , &myAction , NULL );
}
#endif
netServer* gNetServer;
dbIDatabase* gDB;
fbool gIsRun = F_TRUE;
fbool gIsExit = F_TRUE;
fvoid dbexit( dbResult* result )
{
}
fvoid RunServerExit()
{
for ( int i = 0 ; i < 25550 ; i++ )
{
gDB->ExecuteAsyncSQL( "SELECT * FROM new_table;" , 0 , &dbexit );
}
while( gIsExit )
{
fuint32 size = gDB->GetRequestSize();
FLOG( LOGLEVEL4 , "db request %d." , size );
if ( !size )
{
gIsExit = F_FALSE;
}
}
}
fvoid ServerInit()
{
creatureManager::Instance();
gCreatureManager->Init();
gNetServer = new netServer(); // 这里的构造函数设计比较好
gNetServer->SetAddress( "192.168.0.103" , 8100 );
gNetServer->SetHandler( OnNetMsgEnter , OnNetMsgExit , OnNetMsg );
gNetServer->Start();
FLOG( LOGLEVEL4 , "server inited." );
InitError();
//((char *)NULL)[1] = 0;
}
fvoid ServerRun()
{
while ( gIsRun )
{
// dbResult* pRessult = F_NULL;
// while ( ( pRessult = gDB->GetAsyncResult() ) != F_NULL)
// {
// pRessult->callback( pRessult );
// pRessult->Release();
// }
gNetServer->Update();
}
}
fvoid c1( dbResult* result )
{
dbIRecordset* pRes = result->pResult;
dbIRecord* tempRecord = F_NULL;
fulong RecordCount = pRes->CountRecord();
for ( fuint32 i = 0 ; i < RecordCount ; i++ )
{
pRes->Move( i );
tempRecord = pRes->GetRecord();
fint32 petID = tempRecord->Field( (fuint32)0 );
petID = 1;
}
}
int main(int argc, const char * argv[])
{
ServerInit(); // 今天讲这里
ServerRun();
RunServerExit();
return 0;
}
本人也是刚学C++,很多设计模式都不懂,所以不会讲解了,看看代码了~~