用ACE寫網絡服務器

Java的NIO寫網絡服務是非常簡單的,而且性能和伸縮性都較高。如何用C++也達到這樣的效果呢?答案是肯定的。ACE就可以完成這個任務。ACE是一個重型的網絡服務實現方案,也具有跨平臺,但畢竟是C++實現,而且它的怪異的命名和高級的模式使它的學習曲線都較高,對於追求高性能的C++實現,這些又算不了什麼。

與Java的NIO相比,ACE更進一步,它使用了Reactor模式,將Socket的操作封裝在一個個的對象實例中,我們可以不關心Socket操作,而只關心自己的邏輯實現了。ACE也採用了“事件分離”的方法,將對Socket的讀和寫操作分離到對應的實例中處理。可以達到很高的性能。

下面是服務器端的代碼,讀取一個文件,並將文件的內容發給到客戶端上。和對應的Java例子一樣,也是以4K爲單位進行傳送。在open()函數中註冊了寫事件。

#include <stdio.h>
#include <tchar.h>
#include <ace/Acceptor.h>
#include <ace/SOCK_Acceptor.h>
#include <ace/SOCK_Stream.h>
#include <ace/Svc_Handler.h>

static const char * filename = "bigfile.dat";
// 服務器處理代碼,每一個鏈接都要隱式生成這樣一個的實例
class ACEServer : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>
{
  typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> super;
public:
  ACEServer() {
    ACE_DEBUG ((LM_DEBUG,"(%P|%t) Create New ACEServer/n"));
  };
public:
  virtual ~ACEServer() {
    ACE_DEBUG ((LM_DEBUG,"(%P|%t) Destory ACEServer/n"));
  };
  virtual int open(void *p){
    if((file = fopen(filename, "rb")) == NULL) {
      ACE_DEBUG ((LM_DEBUG,"(%P|%t) Failed to open file/n"));
      return -1;
    }
    total = 0;
    ACE_DEBUG ((LM_DEBUG,"(%P|%t) New Client Connection/n"));
    // 要註冊寫事件
    ACE_Reactor::instance()->register_handler(this, 
      ACE_Event_Handler::WRITE_MASK);
    return super::open(p);
  }
protected:
  virtual int handle_output(ACE_HANDLE) {
    size_t count = fread(block, sizeof(char), sizeof(block), file);
    // 沒有內容了,就註銷
    if(count <= 0) {
      ACE_DEBUG ((LM_DEBUG,"/n(%P|%t) Send Finished!/n"));
      fclose(file);
      file = NULL;
      return -1;
    }
    this->peer().send(block, count);
    total += count;
    ACE_DEBUG((LM_DEBUG, ".", total));
    return 1;
  }
private:
  char block[4096];
  FILE *file;
  size_t total;
};

int _tmain(int argc, _TCHAR* argv[])
{
  typedef  ACE_Acceptor<ACEServer, ACE_SOCK_ACCEPTOR> MyServer;
  ACE_INET_Addr address(12345);
  MyServer server;
  if(server.open(address== -1) {
    ACE_DEBUG((LM_DEBUG, "(%P|%t) Failed to Create Server/n"));
    return -1;
  }
  ACE_Reactor::instance()->run_reactor_event_loop();
  return 0;
}

下面是客戶端代碼,注意:由於open()函數默認註冊了讀事件,所以在open()中沒有做註冊事件的動作。

#include <stdio.h>
#include <tchar.h>
#include <ace/Connector.h>
#include <ace/SOCK_Connector.h>
#include <ace/SOCK_Stream.h>
#include <ace/Svc_Handler.h>
// 客戶端,獲取服務器的文件內容
class ACEClient : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>
{
  typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> super;
public:
  ACEClient() {
    ACE_DEBUG ((LM_DEBUG,"(%P|%t) Create New ACEClient/n"));
  };
public:
  virtual ~ACEClient() {
    ACE_DEBUG ((LM_DEBUG,"(%P|%t) Destory ACEClient/n"));
  };
  virtual int open(void *p){
    total = 0;
    ACE_DEBUG ((LM_DEBUG,"(%P|%t) Connected to Server/n"));
    return super::open(p);
  }
protected:
  // 處理服務器發過來的內容
  virtual int handle_input(ACE_HANDLE) {
    int count = this->peer().recv(block, sizeof(block));
    if(count > 0) {
      total += count;
      ACE_DEBUG ((LM_DEBUG,"(%P|%t) Received %d bytes/n", total));
      return 0;
    }
    // 結束了,停止反應器
    if(count == && ACE_OS::last_error() != EWOULDBLOCK) {
      ACE_Reactor::instance()->end_reactor_event_loop();
      return -1;
    }
    ACE_DEBUG ((LM_DEBUG,"(%P|%t) Received %d bytes/n", total));
    return 0;
  }
private:
  char block[4096];
  size_t total;
};


int _tmain(int argc, _TCHAR* argv[])
{
  typedef ACE_Connector<ACEClient, ACE_SOCK_CONNECTOR> MyClient;
  ACE_INET_Addr address(12345"localhost");
  MyClient client;
  ACEClient c;
  ACEClient *pc = &c;
  if(client.connect(pc, address== -1) {
    ACE_DEBUG((LM_DEBUG, "(%P|%t) Failed to connect server!/n"));
    return -1;
  }
  ACE_Reactor::instance()->run_reactor_event_loop();
  return 0;
}
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章