2011年2月27日 星期日

OpenGL 發射火焰的效果

光看靜止的圖片有時候還是看不出個所以然,所以這次我用CamStudio來抓取影像,
只是效果不是是很好.頓頓的,有空的話,再試試別種軟體來抓影片.
下面就是音速小子發射火焰槍的效果:

我把重點程式碼貼上,火焰槍就是這樣做出來的:

Flamethrower.h

struct Flame //火的資料(很多的火組成火焰)
{
Vector Position; //位置
Vector ViewPoint;//方向
int Life;//生命
    float Size; //大小
    float Rotate; //不斷的旋轉角度,使火焰看起來較活
};
const int MAX_FLAME_COUNT = 1000;

class Flamethrower
{
public:
    Flamethrower();
    ~Flamethrower();
    bool Initial(char *texture);
    void Fire(Vector Position,
              Vector ViewPoint,
              int Life);//發射火焰
    void UpData(Vector CameraPosition, int time);
    void Draw(Vector up, Vector right);
    CTexture Texture;
protected:
    Flame FlamePool[MAX_FLAME_COUNT];//一個火焰"坑"(就是存放很多的火)
    int SortIndex[MAX_FLAME_COUNT]; //排序的索引陣列
    int Count; //火的數量
    int time1,time2;//更新的變數
};

這個類別應該是很簡單,不用多做解釋,直接看Flamethrower.cpp
Flamethrower::Flamethrower()
{
   Count = 0;
   time1 = 0;
}

Flamethrower::~Flamethrower()
{

}

bool Flamethrower::Initial(char *texture)
{
    srand(GetTickCount());
    if( !Texture.LoadTGA(texture) )
       return false;
    return true;
}

void Flamethrower::Fire(Vector Position, Vector ViewPoint, int Life)
{
    int i,temp;
    if (Count < MAX_FLAME_COUNT) //若沒有超過限制數量
    {
        temp = Count;
        for(i=0;i<Count;i++)
        {
            if(FlamePool[i].Life == 0)
            {
                temp = i;
                break;
            }
        }
        if(temp==MAX_FLAME_COUNT)
            return;
        FlamePool[temp].Position = Position;
//配合音速小子攻擊時槍會"上下"震動
if(rand()%100 < 50)
        FlamePool[temp].Position[1] += rand()%3;
else
   FlamePool[temp].Position[1] -= rand()%3;
//
        FlamePool[temp].ViewPoint = ViewPoint;
        //往x軸擴展出去的效果
        if(rand()%100<50)
            FlamePool[temp].ViewPoint[0] += (float)((rand()%100)/500.00);
        else
            FlamePool[temp].ViewPoint[0] -= (float)((rand()%100)/500.00);

        //往y軸上升的效果
        FlamePool[temp].ViewPoint[1] += (float)((rand()%100)/90.00);

        //往z軸擴展出去的效果
        if(rand()%100<50)
            FlamePool[temp].ViewPoint[2] += (float)((rand()%100)/500.00);
        else
            FlamePool[temp].ViewPoint[2] -= (float)((rand()%100)/500.00);

        FlamePool[temp].Life = Life;
        FlamePool[temp].Size = 1;
        FlamePool[temp].Rotate = rand()%360;
        SortIndex[temp] = temp;

        if (temp == Count && Count < MAX_FLAME_COUNT-1) //若沒有超過限制數量
            Count++;
    }
}

void Flamethrower::UpData(Vector CameraPosition, int time)
{
    int i,j;

    if(time-time1 > 1)
    {
        time1 = time;
        for (i=0;i<Count;i++)
        {
            if (FlamePool[i].Life > 0)
            {
                Vector delta(FlamePool[i].ViewPoint[0]-FlamePool[i].Position[0],
                             0,
                             FlamePool[i].ViewPoint[2]-FlamePool[i].Position[2]);
                FlamePool[i].Position += delta * 6;
                FlamePool[i].ViewPoint += delta * 6;
                FlamePool[i].Life--;
                FlamePool[i].Size += .6;
                FlamePool[i].Rotate += 4+rand()%8;
                if( FlamePool[i].Rotate > 360 )
                    FlamePool[i].Rotate = 1;
            }
        }

        //做排序(離Camera越遠,就要越先畫出來)
        for (i=Count-1;i>0;i--)
        {
            for (j=1;j<=i;j++)
            {
                Vector v1 = FlamePool[SortIndex[j-1]].Position - CameraPosition;
                Vector v2 = FlamePool[SortIndex[j]].Position - CameraPosition;
                double length1 = (v1[0] * v1[0]) + (v1[1] * v1[1]) + (v1[2] * v1[2]);
                double length2 = (v2[0] * v2[0]) + (v2[1] * v2[1]) + (v2[2] * v2[2]);
                if ( length1 < length2 ) //v2距離較遠,與v1交換
                {
                    int temp = SortIndex[j-1];
                    SortIndex[j-1] = SortIndex[j];
                    SortIndex[j] = temp;
                }
            }
        }
    }
}

void Flamethrower::Draw(Vector up, Vector right)
{
    int i,j;
    Vector v;
    for (i=0;i<Count;i++)
    {
        if (FlamePool[SortIndex[i]].Life > 0
   && SphereTest(FlamePool[SortIndex[i]].Position[0], FlamePool[SortIndex[i]].Position[1], FlamePool[SortIndex[i]].Position[2], FlamePool[SortIndex[i]].Size))
        {
            glPushMatrix();
                glTranslatef(FlamePool[SortIndex[i]].Position[0], FlamePool[SortIndex[i]].Position[1], FlamePool[SortIndex[i]].Position[2]);
                glColor4f(1.0f,1.0f,1.0f,FlamePool[SortIndex[i]].Life/96.00);
                if( up[1] == 1 ) //正上方是頭頂,所以方塊對z軸旋轉
                    glRotatef(FlamePool[SortIndex[i]].Rotate,0,0,1);
                if( up[2] == -1 )//正上方是眼前,所以方塊對y軸旋轉
                    glRotatef(FlamePool[SortIndex[i]].Rotate,0,1,0);
   glBegin(GL_QUADS);
   v = up*(-FlamePool[SortIndex[i]].Size) + right*(-FlamePool[SortIndex[i]].Size);
       glTexCoord2f(0.0f, 0.0f);
   glVertex3fv(v);
   v = up*(-FlamePool[SortIndex[i]].Size) + right*FlamePool[SortIndex[i]].Size;
     glTexCoord2f(1.0f, 0.0f);
   glVertex3fv(v);
                    v = up*(FlamePool[SortIndex[i]].Size) + right*(FlamePool[SortIndex[i]].Size);
                    glTexCoord2f(1,1);
                    glVertex3fv(v);
   v = up*(FlamePool[SortIndex[i]].Size) + right*(-FlamePool[SortIndex[i]].Size);
     glTexCoord2f(0, 1);
   glVertex3fv(v);
   glEnd();
            glPopMatrix();
        }
    }
}

做出來的效果雖不是最佳,但也有幾分樣了,而且沒有加入讓火焰貼圖面向攝影機的功能,所以實用性質不大,(那....為什麼放上來啊?....)

OpenGL物體發光效果

以前有一段時間,練習過OpenGL,現在很少用了,最近整理資料時,把以前練習寫的程式翻出來了,執行了一下,畫面如下:

雖然只是個小程式,但其實也包含了不少東西,有地形,影子,水波,公告板,雨水,各式特效等...
其中比較特別的是人物身上會有向外發散的淡淡金光,怎麼做的呢?其實就是把人物畫到貼圖上去,因為是向外發散的光,所以在一個frame內要多畫幾次,還要調整大小,這樣做好的貼圖再繪製出來就有這種效果了,
下面貼上程式碼片斷:

//先準備好貼圖
void HeroLight::Initial( void )
{
unsigned int* data;

// Create Storage Space For Texture Data (128x128x4)
data = (unsigned int*)new GLuint[((128 * 128)* 4 * sizeof(unsigned int))];
ZeroMemory(data,((128 * 128)* 4 * sizeof(unsigned int)));// Clear Storage Memory

glGenTextures(1, &Texture); // Create 1 Texture
glBindTexture(GL_TEXTURE_2D, Texture); // Bind The Texture
glTexImage2D(GL_TEXTURE_2D, 0, 4, 128, 128, 0,
GL_RGBA, GL_UNSIGNED_BYTE, data);// Build Texture Using Information In data
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

delete [] data;
}


RenderToTextureBegin與RenderToTextureEnd是拿來夾住要繪製的模型的
void HeroLight::RenderToTextureBegin( int w, int h )
{
    GLfloat glfMaterialColor[]={0.2f,0.2f,.1f,.4f}; // Set The Material Color
GLfloat specular[]={1.0f,1.0f,1.0f,1.0f};
    glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,glfMaterialColor);
glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specular);
    
    Witch = w;
    Height = h;
    glViewport(0,0,128,128);
}

void HeroLight::RenderToTextureEnd( void )
{
    glBindTexture(GL_TEXTURE_2D,Texture);  
    //Copy Our ViewPort To The Texture (From 0,0 To 128,128... No Border)
    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 0, 0, 128, 128, 0);
    //glClearColor(.0f, .0f, .5f, .5);// Set The Clear Color To Medium Blue
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Clear The Screen And Depth Buffer
    glViewport(0 , 0, Witch, Height);
}

要像這樣使用...

HeroLight.RenderToTextureBegin(Width,Height);
    glDepthFunc(GL_LESS);
    glCullFace(GL_BACK);

    glDisable(GL_BLEND);
    glDisable(GL_TEXTURE_GEN_S);
    glDisable(GL_TEXTURE_GEN_T);

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_FOG);
    glDepthFunc(GL_LEQUAL);
    glFrontFace(GL_CW);
    glPolygonMode(GL_FRONT, GL_FILL);
    //角色///////////////////////////////////////////////////////////////////
    glDepthFunc(GL_LEQUAL);
    glFrontFace(GL_CW);
    glPolygonMode(GL_FRONT, GL_FILL);
    Md2Model.GetAnimate(time);
    Md2Model.SetPosition(Vector(Md2Model.GetXPosition(),
                                26,//Terrain.GetHeight(Md2Model.GetPosition())+26,
                                Md2Model.GetZPosition()));
    glBindTexture(GL_TEXTURE_2D,Md2Model.texture);
    //glDisable(GL_TEXTURE_2D);
    Md2Model.DrawModel();
    glFrontFace(GL_CCW);
    ////////////////////////////////////////////////////////////////////////
HeroLight.RenderToTextureEnd();


    //畫出模型
    /////////////////////////////////////////////////////////////////////
    glDepthFunc(GL_LESS);
    glCullFace(GL_BACK);

    glDisable(GL_BLEND);
    glDisable(GL_TEXTURE_GEN_S);
    glDisable(GL_TEXTURE_GEN_T);

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_FOG);
    glDepthFunc(GL_LEQUAL);
    glFrontFace(GL_CW);
    glPolygonMode(GL_FRONT, GL_FILL);
    Md2Model.GetAnimate(time);
    Md2Model.SetPosition(Vector(Md2Model.GetXPosition(),
                                26,//Terrain.GetHeight(Md2Model.GetPosition())+26,
                                Md2Model.GetZPosition()));
    glBindTexture(GL_TEXTURE_2D,Md2Model.texture);
    //glDisable(GL_TEXTURE_2D);
    Md2Model.DrawModel();
    glFrontFace(GL_CCW);
    ////////////////////////////////////////////////////////////////////////


    HeroLight.Draw(10,.03);//最後將特效畫出

畫出的程式碼:

void HeroLight::Draw( int times, float inc ) //time約1 - 30 inc約 0.02 - 0.09
{
    float spost = 0.0f; // Starting Texture Coordinate Offset
float alphainc = 0.9f / times; // Fade Speed For Alpha Blending
float alpha = 0.2f; // Starting Alpha Value

// Disable AutoTexture Coordinates
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);

glEnable(GL_TEXTURE_2D);// Enable 2D Texture Mapping
glDisable(GL_DEPTH_TEST);// Disable Depth Testing
    glDisable(GL_LIGHTING);
glBlendFunc(GL_SRC_ALPHA,GL_ONE);// Set Blending Mode
glEnable(GL_BLEND); // Enable Blending
glBindTexture(GL_TEXTURE_2D,Texture);// Bind To The Blur Texture
ViewOrtho(); // Switch To An Ortho View

alphainc = alpha / times;// alphainc=0.2f / Times To Render Blur

glBegin(GL_QUADS); // Begin Drawing Quads
for (int num = 0;num < times;num++)
{
glColor4f(1.0f, 1.0f, .0f, alpha);
glTexCoord2f(0+spost,1-spost);
glVertex2f(0,0);

glTexCoord2f(0+spost,0+spost);
glVertex2f(0,Height);

glTexCoord2f(1-spost,0+spost);
glVertex2f(Witch,Height);

glTexCoord2f(1-spost,1-spost);
glVertex2f(Witch,0);

spost += inc; // Gradually Increase spost (Zooming Closer To Texture Center)
alpha = alpha - alphainc;// Gradually Decrease alpha (Gradually Fading Image Out)
}
glEnd();// Done Drawing Quads

ViewPerspective();// Switch To A Perspective View
}


//調整為2D繪製
void HeroLight::ViewOrtho( void )
{
    glMatrixMode(GL_PROJECTION); // Select Projection
glPushMatrix(); // Push The Matrix
glLoadIdentity(); // Reset The Matrix
glOrtho( 0, Witch , Height , 0, -1, 1 );
glMatrixMode(GL_MODELVIEW); // Select Modelview Matrix
glPushMatrix(); // Push The Matrix
glLoadIdentity();

}

//恢復之前的繪製矩陣
void HeroLight::ViewPerspective( void )
{
glMatrixMode( GL_PROJECTION ); // Select Projection
glPopMatrix(); // Pop The Matrix
glMatrixMode( GL_MODELVIEW );// Select Modelview
glPopMatrix(); // Pop The Matrix

}

其實要做到物體發光的效果,應該是有其它更好的做法,沒記錯的話,這應該是8年前寫的東西了,還有其它雜七雜八的東西,也打算整理一下,丟上來做個記錄.

2011年2月26日 星期六

用ACE練習寫個簡單的server class(下)

剩下最後的關鍵了,HA_Proactive_Service主要做傳送接收的處理.


tatic ACE_Recursive_Thread_Mutex _lock;

int HA_Proactive_Service::initiate_read_stream(void)//開始一筆新的接收
{
// ACE_DEBUG( (LM_DEBUG, ACE_TEXT("client id : %d\n"),stAppid.id));
ACE_NEW_NORETURN (recv_data_, ACE_Message_Block (sizeof(PacketHeader), SBN_QUE_NONE));
ACE_HANDLE handle = this->handle ();
this->recv_data_->copy ((const char *)&handle, sizeof(ACE_HANDLE));
this->reader_.read (*recv_data_, recv_data_->space ());
return 0;
}
HA_Proactive_Service::HA_Proactive_Service()
{
recv_data_ = NULL;
stAppid.bOnline = false;
}
HA_Proactive_Service::~HA_Proactive_Service ()
{
if (this->handle () != ACE_INVALID_HANDLE)
ACE_OS::closesocket (this->handle ());
}

void HA_Proactive_Service::init( HA_Proactive_Acceptor *acceptor, int id )
{
  this->acceptor_ = acceptor;
  //this->acceptor_->ser[id].stAppid.id = id;
  stAppid.id = id;
  stAppid.num = 0;
}
void HA_Proactive_Service::SendData(char *buf)
{
#ifdef WIN32
size_t len = strlen(buf)+1;
#else
int len = strlen(buf)+1;
#endif

ACE_Message_Block *temp = new ACE_Message_Block(len);
temp->copy(buf);
temp->wr_ptr(len+1);
int nResult = writer_.write(*temp,len);

//呼叫了傳送後temp這個指標不可再用了

if( nResult != 0 )
ACE_DEBUG( (LM_DEBUG,"Write data failed:%s\n",buf) );
else
ACE_DEBUG( (LM_DEBUG,"send data(%d):%s\n",stAppid.id, buf) );//temp->rd_ptr()
}


void HA_Proactive_Service::handle_time_out(const ACE_Time_Value &tv, const void *p)
{
//ACE_DEBUG((LM_DEBUG,"time out: %d\n",(int*)p));
ACE_Proactor::instance()->cancel_timer(*this);
}

void HA_Proactive_Service::open (ACE_HANDLE h, ACE_Message_Block& message_block)//在這裡完成初始化的準備
{
this->handle(h);//新的socket句柄產生了

if (this->reader_.open (*this) != 0 || this->writer_.open (*this) != 0   )
{

this->acceptor_->free_handler( this );
return;
}

if (this->initiate_read_stream () == -1)
return;

   //取客戶端資料
ACE_INET_Addr addr;
ACE_SOCK_SEQPACK_Association ass=ACE_SOCK_SEQPACK_Association(h);
size_t addr_size=1;
//ass.get_local_addrs(&addr,addr_size);//取本地端
ass.get_remote_addrs(&addr,addr_size);
stAppid.iPort = addr.get_port_number();
stAppid.sIp = addr.get_host_addr();
stAppid.bOnline = true;
stAppid.num = this->acceptor_->cNum;
++this->acceptor_->cNum;
//ACE_Guard <ACE_Recursive_Thread_Mutex> locker (_lock);

acceptor_->mpInfo.insert(std::make_pair(stAppid.id, stAppid));

ACE_Proactor::instance()->schedule_timer(*this, (void *)stAppid.id, ACE_Time_Value(1), ACE_Time_Value(1));

//******************************************************************************************
ACE_Message_Block *putdata;
CmdPacker pack;
pack.id = stAppid.id;
pack.num = stAppid.num;
pack.bsize = 0;
pack.cmd = SBN_QUE_CONNECT;
ACE_NEW_NORETURN (putdata, ACE_Message_Block(sizeof(CmdPacker), SBN_QUE_CONNECT));
putdata->copy ((const char *)&pack, sizeof(CmdPacker));
acceptor_->RQData.putq(putdata);
//*****************************************************************************************

//ACE_DEBUG( (LM_DEBUG, ACE_TEXT("connect: %d %s\n"), stAppid.iPort ,stAppid.sIp.c_str() ) );

}
void HA_Proactive_Service::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result)
{
//result.message_block ().rd_ptr ()[result.bytes_transferred ()] = '\0';//數到的字串流結尾補結束字元

if (!result.success () || result.bytes_transferred () == 0)//傳輸失敗或傳輸0個字節,客戶斷開連接
{
//ACE_DEBUG( (LM_DEBUG, ACE_TEXT("client close : %d %d\n"), result.handle (),this->handle() ) );
result.message_block ().release ();
this->acceptor_->free_handler( this );
}
else
{
//result.message_block ().rd_ptr ()[result.bytes_transferred ()] = '\0';//數到的字串流結尾補結束字元
int len=0;
 
if (this->recv_data_->length() < sizeof(PacketHeader))// 數據包長度信息還未接收完
{
this->reader_.read (*recv_data_, recv_data_->space ());
return;
}
PacketHeader * hdr = reinterpret_cast<PacketHeader *> (this->recv_data_->rd_ptr());
ACE_Message_Block * data_mb = this->recv_data_->cont();
//將收到的4個字元轉成數值
len = (hdr->length[0]-'0')*1000 + (hdr->length[1]-'0')*100 + (hdr->length[2]-'0')*10 + (hdr->length[3]-'0');

if (!data_mb)// 剛剛接收完長度信息
{
//長度不對要做錯誤處理
if( len > 1024 )
len = 1024;
if( len < 1 )
len = 1;

ACE_NEW (data_mb, ACE_Message_Block(len));
this->recv_data_->cont (data_mb);
}
if (data_mb->length () == len)// 數據已接收完
{
//int cmd = (data_mb->rd_ptr()[0]-'0')*1000 + (data_mb->rd_ptr ()[1]-'0')*100 + (data_mb->rd_ptr ()[2]-'0')*10 + (data_mb->rd_ptr ()[3]-'0');//將4個字元轉成命令
ACE_Message_Block *putdata;
//CmdPacker pack;
//if( cmd < CMD_NONE )
// cmd = CMD_NONE;
recvpack.id = stAppid.id;
recvpack.num = stAppid.num;
recvpack.bsize = len;
recvpack.cmd = SBN_QUE_DATA;
#ifdef WIN32
memcpy(recvpack.data,data_mb->rd_ptr(),len);//strcpy_s(recvpack.data,256,data_mb->rd_ptr());
#else
strcpy(recvpack.data,data_mb->rd_ptr());
#endif
ACE_NEW_NORETURN (putdata, ACE_Message_Block(sizeof(CmdPacker), SBN_QUE_DATA));
putdata->copy((const char *)&recvpack, sizeof(CmdPacker));
//ACE_DEBUG( (LM_DEBUG,"read_stream:%d %d %d %s\n", pack.id, pack.num, pack.bsize, pack.data) );

acceptor_->RQData.putq(putdata);//,(ACE_Time_Value *) &ACE_Time_Value::zero);
recv_data_->release();
this->initiate_read_stream();// 再繼續接收下一個數據包
return;
}

this->reader_.read (*data_mb, data_mb->space ());// 否則繼續接收該數據包
}
}

void HA_Proactive_Service::handle_write_stream (const ACE_Asynch_Write_Stream::Result &result)
{
//ACE_DEBUG( (LM_DEBUG,"handle_write_stream:%s\n",result.message_block().rd_ptr ()) );
result.message_block ().release ();
}

而連線,傳送,接收的運用就由 HA_Proactive_Acceptor驅動


HA_Proactive_Acceptor::HA_Proactive_Acceptor() : ACE_Asynch_Acceptor<HA_Proactive_Service>()
{
cNum = 1;
ser = NULL;
}

HA_Proactive_Acceptor::~HA_Proactive_Acceptor(void)
{
Destroy();
}
void HA_Proactive_Acceptor::Destroy(void)
{
if( ser )
delete[] ser;
}
void HA_Proactive_Acceptor::free_handler( HA_Proactive_Service * service )
{
ACE_Guard <ACE_Recursive_Thread_Mutex> locker (_lock);
ACE_OS::closesocket(service->handle ());
std::map<int, CData>::iterator mpIter;
for (mpIter = mpInfo.begin(); mpIter != mpInfo.end(); ++mpIter)  
{
//if( service->stAppid.iPort != mpIter->second.iPort ) continue;
//if( service->stAppid.sIp != mpIter->second.sIp ) continue;
if( service->stAppid.id != mpIter->second.id ) continue;
mpInfo.erase(mpIter);
break;
}


//******************************************************************************************
ACE_Message_Block *putdata;
CmdPacker pack;
pack.id = service->stAppid.id;
pack.num = service->stAppid.num;
pack.bsize = 0;
pack.cmd = SBN_QUE_DISCONNECT;
ACE_NEW_NORETURN (putdata, ACE_Message_Block(sizeof(CmdPacker), SBN_QUE_DISCONNECT));
putdata->copy((const char *)&pack, sizeof(CmdPacker));
RQData.putq(putdata);
//*****************************************************************************************

service->stAppid.bOnline = false;
service->stAppid.num = 0;
this->handler_list_.push_back( service );

}

int HA_Proactive_Acceptor::validate_connection(const ACE_Asynch_Accept::Result& result,  const ACE_INET_Addr &remote, const ACE_INET_Addr& local)
{
return 0;
}

HA_Proactive_Service * HA_Proactive_Acceptor::make_handler (void)
{
if( this->handler_list_.empty() )
{
ACE_DEBUG( (LM_DEBUG,"Connect full\n") );
return 0;
}
HA_Proactive_Service *service = this->handler_list_.front();
this->handler_list_.pop_front();
return service; //得到處理事件的句柄後,前攝器會調用處理事件的open方法   HA_Proactive_Service::open
}

void HA_Proactive_Acceptor::init_handlers(int scount)
{
ServiceCount = scount;
ser = new HA_Proactive_Service[ServiceCount];
for( int i = 0; i < ServiceCount; ++i )
{
//HA_Proactive_Service * service;
//ACE_NEW( service, HA_Proactive_Service );
//service->init( this );
ser[i].init(this,i);
this->handler_list_.push_back( &ser[i] );//service );
}
}

其實主要的用意就只是把ACE再包成一個容易使用的類別,大家可以參考看看

2011年2月25日 星期五

用ACE練習寫個簡單的server class(中)

補上.cpp的程式碼

SBNet.cpp


CSBNet::CSBNet()
{
ACE::init();
Net = new SBaseNet();
}
CSBNet::~CSBNet()
{
ACE::fini();
}
bool CSBNet::Start(int scount)
{
return Net->Start(scount);
}
bool CSBNet::Start(int port,int scount)
{
return Net->Start(port,scount);
}
bool CSBNet::End(void)
{
return Net->End();
}
void CSBNet::Send(int id, char *data )
{
Net->Send(id, data);
}
char *CSBNet::Receive(void)
{
return Net->Receive();
}
上面就是將SBaseNet包裝起來

SBaseNet類別

SBaseNet::SBaseNet():Port(ACE_DEFAULT_SERVER_PORT)
{
}

SBaseNet::~SBaseNet()
{
}

bool SBaseNet::Start(int scount)
{
aio_acceptor.init_handlers(scount);
return this->activate();
}
bool SBaseNet::Start(int port, int scount)
{
Port = port;
aio_acceptor.init_handlers(scount);
return this->activate();
}

int SBaseNet::svc()
{
ACE_INET_Addr addr( Port );
if (0 != aio_acceptor.open (addr,0, 0, ACE_DEFAULT_ASYNCH_BACKLOG, 1, 0, 0))
ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("SBaseNet acceptor open")), 1);

ACE_DEBUG ((LM_DEBUG, ACE_TEXT("SBaseNet start\n")));
//ACE_Time_Value oHdlTime(1);
//while(run)
{
// oHdlTime = 1;
ACE_Proactor::instance()->proactor_run_event_loop();//oHdlTime);
}
ACE_DEBUG ((LM_DEBUG, ACE_TEXT("SBaseNet ended\n")));
return 0;

}

bool SBaseNet::End(void)
{
ACE_Proactor::instance()->proactor_end_event_loop();
this->wait();
aio_acceptor.Destroy();
return 0;
}

void SBaseNet::Send(int id, char *data )
{
aio_acceptor.ser[id].SendData(data);
}
char *SBaseNet::Receive(void)
{
return aio_acceptor.RQData.Receive();
}


接收資料的類別

RecvQueue::RecvQueue()
{
//this->msg_queue_->high_water_mark(655350);
}
RecvQueue::~RecvQueue()
{

}
int RecvQueue::open(void)
{
return this->activate();
}
int RecvQueue::close(void)
{
/*
ACE_Message_Block * hangup;
ACE_NEW_RETURN (hangup, ACE_Message_Block(0, ACE_Message_Block::MB_HANGUP), -1);
this->putq (hangup);
this->wait ();
*/
return 0;
}
char *RecvQueue::Receive( void )
{
ACE_Message_Block *message = NULL;
int result;
result = this->getq(message,(ACE_Time_Value *) &ACE_Time_Value::zero);

if( result > -1 && message )
{
CmdPacker *hdr = reinterpret_cast<CmdPacker *> (message->rd_ptr ());
//ACE_DEBUG((LM_DEBUG, ACE_TEXT("recv:%d %d %d %s\n",hdr->id,hdr->num,hdr->bsize, hdr->data)));
//ACE_DEBUG((LM_DEBUG, ACE_TEXT("recv:%d \n",hdr->id)));
ReadBuf[0] = (char)message->msg_type();
if( SBN_QUE_DATA == ReadBuf[0] )
memcpy(ReadBuf,(char*)hdr,16+hdr->bsize);
else
memcpy(ReadBuf,(char*)hdr,16);
message->release();
}
else
ReadBuf[0] = SBN_QUE_NONE;
return ReadBuf;
}

接下來剩HA_Proactive_Acceptor和HA_Proactive_Service,貼到下篇好了,不然這一堆程式碼,看得頭都昏了...

2011年2月24日 星期四

用ACE練習寫個簡單的server class(上)

曾經想用ACE來寫server,只是學了一下,就放著了...,3年前的事了吧,現在來回味一下....



class CSBNet {
public:
CSBNet(void);
~CSBNet();
bool Start(int port,int scount);
bool Start(int scount);
bool End(void);
void Send(int id, char *data );
char *Receive(void);
private:
SBaseNet *Net;
};

這樣使用起來較簡便,Start後,server就算啟動了,傳送時帶上要傳給client的索引和資料就可以了.
至於接收的Receive則是要把收到資料轉型為CmdPacker的結構


struct CmdPacker//解開的令命包資料
{
int cmd;//從queqe接收的命令
int id;//某個client的索引
int num;//分配的編號
int bsize;//位元組數
char data[1024];
};

雖然是亂七八糟的設計,但使用起來是很輕便的...,其實CSBNet是將SBaseNet再做一次包裝,主要的重點是在SBaseNet類別...


class SBaseNet : public ACE_Task_Base
{
public:
SBaseNet();
~SBaseNet();
bool Start(int scount);
bool Start(int port,int scount);
bool End(void);
void Send(int id, char *data );
char *Receive(void);
virtual int svc();
private:
HA_Proactive_Acceptor aio_acceptor;
int Port;
};
看起來和CSBNet 很像,但前面說過了,CSBNet只是再包一層而已,為什麼要這樣?記得當初是想隔開ACE相關的檔案的,使用CSBNet類時,載入dll就好了,完全不用再管ACE相關配置,因為一些設定還滿煩人的.

接下來的重點是這個

class HA_Proactive_Acceptor : public ACE_Asynch_Acceptor<HA_Proactive_Service>  //異步連接接收器,監聽客戶的請求
{
public:
HA_Proactive_Acceptor();
  HA_Proactive_Acceptor(int scount);
  ~HA_Proactive_Acceptor();
  void free_handler( HA_Proactive_Service * service );
  void Destroy(void);
  void init_handlers(int scount);//在先產生好處理對像,在make_handler內時,就不用new出來了
  RecvQueue RQData;//接收的資料存放處
private:
  //make_handler 為 ACE_Asynch_Acceptor類的虛函式,原本的功能為,有連線連入時,會產生一個服務處理對像
  virtual HA_Proactive_Service *make_handler (void);//客戶連接請求到達時,會自動週用此方法
  virtual int validate_connection(const ACE_Asynch_Accept::Result& result,  const ACE_INET_Addr &remote, const ACE_INET_Addr& local);//可以做ip黑名單
public:
std::map<int, CData> mpInfo;            // 注冊客戶端信息表
typedef std::list<HA_Proactive_Service *> T_Handler_List;
T_Handler_List handler_list_;//事件處理器的指標陣列,一個客戶端分配一個,用完後回收
HA_Proactive_Service *ser;//handler_list_ 指向的實體
int cNum;//連線登入後,分配的號碼
int ServiceCount;
};

這邊開始使用ACE了,再附上其它的class


class RecvQueue : public ACE_Task<ACE_MT_SYNCH>
{
public:
RecvQueue();
virtual ~RecvQueue();
int open(void);
int close(void);
char* Receive(void);
private:
char ReadBuf[65535];
};


class HA_Proactive_Service : public ACE_Service_Handler  //事件處理器,處理所有異步操作的結果
{
public:
HA_Proactive_Service();
virtual ~HA_Proactive_Service ();
void init( HA_Proactive_Acceptor * acceptor, int id );
virtual void open (ACE_HANDLE h, ACE_Message_Block& message_block);//客戶端連接成功後調用
virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &result);
virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result &result);
virtual void handle_time_out(const ACE_Time_Value &tv, const void *p);

void SendData(char *buf);

private:
ACE_Asynch_Read_Stream reader_;//異步讀取器
ACE_Asynch_Write_Stream writer_;//異步寫入器
ACE_Message_Block *recv_data_;//接收的資料
int initiate_read_stream(void);
CmdPacker recvpack;
public:
HA_Proactive_Acceptor *acceptor_;
CData   stAppid;//客戶端資料
};

一些自定的結構


ypedef struct s_CData//客戶端資料
{
std::string  sIp;               // 客戶端IP
int    iPort;               // 客戶端連接到服務端的端口
int id;
bool   bOnline;              // 在線狀態
int num;//使用時分配的編號 0:未使用
}CData;

struct PacketHeader//數據包的包頭
{
ACE_HANDLE handler;
char length[4];
};

struct send_data//傳送的字串資料包
{
   char len[4];
    char cmd[4];
   char data[256];
};

以上主要都是在.h內所做的一些宣告,下次再說明.cpp內的程式碼