用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 == 0 && 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; } |