|
一個日志服務器的實現(C 網絡編程第四章程序實現及解析)
一、服務端系統流程
1、首先運行Iterator_Logging_Server::run()函數,這個函數從父類Logging_Server繼承而來。Iterator_Logging_Server類中本身并沒有寫這個函數。
2、這里會調用Iterator_Logging_Server::open()函數,這個函數在Iterator_Logging_Server類中進行了重寫,所以覆蓋了父類中的open()函數。
3、運行Logging_Server::make_log_file(),我們并沒有輸入ACE_SOCK_Stream類型參數,那么不會執(zhí)行if中的語句,而執(zhí)行else中語句。并通過return中語句來產生一個新文件。
4、在return中調用Logging_Server::open()函數。這個函數用于實現建立一個通信端點。然后返回Logging_Server::run()中繼續(xù)運行。
5、執(zhí)行Iterative_Logging_Server::handle_connections()函數。在這里進行等待客戶端的連接。待有輸入后繼續(xù)運行。
6、執(zhí)行Iterative_Logging_Server:: handle_data()函數。用入等待用戶輸入,然后將輸入寫入日志文件,這里調用了Logging_Handler:: log_record()函數,這里是一個while()循環(huán),只有輸入錯誤才會停止。
7、在Logging_Handler:: log_record()中調用Logging_Handler::recv_log_record()接受用戶的輸入,并提取。
8、在Logging_Handler:: log_record()中調用Logging_Handler::write_log_record(),將用戶的輸入寫入到文件中。
程序的大致流程就是這樣。
二、服務器端源碼
1)Logging_Server.h
- #ifndef LOGGING_SERVER_H
- #define LOGGING_SERVER_H
-
- #include "ace/FILE_IO.h"
- #include "ace/SOCK_Acceptor.h"
-
- class ACE_SOCK_Streasm;
-
- class Logging_Server
- {
- public:
- virtual int run(int argc, char* argv[]);
-
- protected:
- virtual int open(u_short logger_port = 0);
- virtual int wait_for_multiple_events()
- {
- return 0;
- }
- virtual int handle_connections() = 0; //定義純虛函數
- virtual int handle_data(ACE_SOCK_Stream * = 0) = 0;
- int make_log_file(ACE_FILE_IO& , ACE_SOCK_Stream * = 0);
- virtual ~Logging_Server()
- {
- acceptor_.close();
- }
- ACE_SOCK_Acceptor& acceptor()
- {
- return acceptor_;
- }
-
- private:
- ACE_SOCK_Acceptor acceptor_;
- };
-
- #endif
2)Logging_Server.cpp
- #include "ace/FILE_Addr.h"
- #include "ace/FILE_Connector.h"
- #include "ace/FILE_IO.h"
- #include "ace/INET_Addr.h"
- #include "ace/SOCK_Stream.h"
- #include "Logging_Server.h"
-
- //attention: second.
- //僅僅是打開一個偵聽端口。
- int Logging_Server::run(int argc, char* argv[])
- {
- //atoi: Convert a string to integer.
- //這里建立了一個服務器被動通信端。
- if(open(argc > 1 ? atoi(argv[1]) : 888) == -1)
- return -1;
-
- for(;;)
- {
- //全部返回零。
- if(wait_for_multiple_events() == -1)
- return -1;
- if(handle_connections() == -1)
- return -1;
- if(handle_data() == -1)
- return -1;
- }
- return 0;
- }
- // attention: first.
- int Logging_Server::open(u_short logger_port)
- {
- ACE::set_handle_limit();
- ACE_INET_Addr server_addr;
- int result;
- if(logger_port != 0)
- result = server_addr.set(logger_port); //int ACE_INET_Addr::set(u_short port_number,ACE_UINT32 ip_addr = INADDR_ANY,int encode = 1,int map = 0)
- else
- result = server_addr.set("ace_logger", ACE_UINT32(INADDR_ANY)); //int ACE_INET_Addr::set ( const char port_name[],ACE_UINT32 ip_addr,const char protocol[] = "tcp")
- //"ace_logger" 在 /etc/service 中定義.
- if(result == -1)
- return -1;
- //If this is 1 then we'll use the <so_reuseaddr> to reuse this address
- return acceptor_.open(server_addr, 1); //被動建立一個通信端點。
- }
-
- int Logging_Server::make_log_file(ACE_FILE_IO& logging_file,
- ACE_SOCK_Stream* logging_peer)
- {
- //這里暫時不使用第二個參數
- char filename[MAXHOSTNAMELEN sizeof(".log")];
- //建立傳輸流。
- if(logging_peer != 0)
- {
- ACE_INET_Addr logging_peer_addr;
- logging_peer->get_remote_addr(logging_peer_addr);
- //獲取表示主機名字的字符,并存于filename中。
- logging_peer_addr.get_host_name(filename, MAXHOSTNAMELEN);
- strcat(filename,"*.log");
- }
- else
- strcpy(filename, "logging_server.log");
-
- ACE_FILE_Connector connector;
- //實際上是通過log_file_作為實參傳入的。這里面會對文件句柄(handler)進行設置。在后面的send_n()中,也會使用這里所設的文件句柄!
- return connector.connect(logging_file,
- ACE_FILE_Addr(filename),
- 0,
- ACE_Addr::sap_any,
- 0,
- O_RDWR|O_CREAT|O_APPEND,
- ACE_DEFAULT_FILE_PERMS);
- }
3)Logging_Handler.h
- #ifndef LOGGING_HANDLER_H
- #define LOGGING_HANDLER_H
-
- //原來就只包含了這么兩個頭文件,這尼瑪的,讓我情保以堪啊!
- #include "ace/FILE_IO.h"
- #include "ace/SOCK_Stream.h"
- //注意頭文件的包含
- #include "ace/Message_Block.h"
- #include "ace/CDR_Stream.h"
- #include "ace/CDR_Base.h"
- #include "ace/Log_Record.h"
- #include <iostream>
-
-
-
-
- class ACE_Message_Block;
-
- class Logging_Handler
- {
- protected:
- ACE_FILE_IO& log_file_;
- ACE_SOCK_Stream logging_peer_;
-
- public:
- Logging_Handler(ACE_FILE_IO& log_file)
- : log_file_(log_file)
- {}
-
- Logging_Handler(ACE_FILE_IO& log_file,
- ACE_HANDLE handle)
- : log_file_(log_file)
- {
- logging_peer_.set_handle(handle);
- }
-
- Logging_Handler(ACE_FILE_IO& log_file,
- const ACE_SOCK_Stream& logging_peer)
- : log_file_(log_file), logging_peer_(logging_peer)
- {}
-
- int close()
- {
- return logging_peer_.close();
- }
-
- //*&mblk 是一個對指針的引用,也可以使用**mblk 來代替,但現得相對麻煩。
- int recv_log_record(ACE_Message_Block *&mblk);
- int write_log_record(ACE_Message_Block* mblk);
- int log_record();
-
- ACE_SOCK_Stream& peer()
- {
- return logging_peer_;
- }
-
- // friend int operator<< (ACE_OutputCDR& cdr, const ACE_Log_Record& log_record);
- // friend int operator>> (ACE_InputCDR& cdr, ACE_Log_Record& log_record);
- };
-
- #endif
4)Logging_Handler.cpp
- #include "Logging_Handler.h"
- using std::cerr;
-
- int Logging_Handler::recv_log_record(ACE_Message_Block *&mblk)
- {
- ACE_INET_Addr peer_addr;
- logging_peer_.get_remote_addr(peer_addr);
- // ACE_Message_Block* head = new ACE_Message_Block(BUFSIZ);
- mblk = new ACE_Message_Block(MAXHOSTNAMELEN 1);
- peer_addr.get_host_name( mblk->wr_ptr(), MAXHOSTNAMELEN );
- mblk->wr_ptr(strlen(mblk->wr_ptr()) 1); //指向當前指針的下一位置
-
- ACE_Message_Block *payload = //payload是一個臨時的變量。
- new ACE_Message_Block(ACE_DEFAULT_CDR_BUFFSIZE);
- ACE_CDR::mb_align(payload);
- //首先我們要明白分幀,然后根據其來分析這個程序。其就是對幀進行了一個解編的過程。
- if(logging_peer_.recv_n(payload->wr_ptr(), 8) == 8) //這里的8位是消息頭。
- {
- payload->wr_ptr(8); //需要手動的移動到尾端,這時就是消息的內容,當然并不是data。這里面還包含有l(wèi)ength等東東。
- ACE_InputCDR cdr(payload);
-
- ACE_CDR::Boolean byte_order;
- cdr >> ACE_InputCDR::to_boolean(byte_order); //這是提取第一個字段4BYTE。在客戶端中用from_boolean(),所以這里要to_boolean()。
- cdr.reset_byte_order(byte_order);
- ACE_CDR::ULong length;
- cdr >> length; //提取了length字段,第二個4BYTE。
-
- //這里的size()是不是針對同一個節(jié)點進行的操作呢?需要考證一下!
- //在客戶端中使用了ACE_CDR::MAX_ALIGNMENT來分配空間,所以這里也一樣了。
- //加8是要保存前面我們存入的8BYTE的消息頭。
- if(logging_peer_.recv_n(payload->wr_ptr(), length) > 0)
- {
- //payload是動態(tài)分配的,所以這個函數運行完成后,這個變量無效,但是這個內存還在,不會刪除。
- payload->wr_ptr(length);
- mblk->cont(payload);
- return length; //若程序運行正常,將會以返回length結束。
- }
- }
-
- //當程序運行不正常進才會運行下面的程序。
- payload->release();
- mblk->release();
- payload = mblk = 0;
- return -1;
- }
-
- int Logging_Handler::write_log_record(ACE_Message_Block *mblk)
- {
- //通過這里實現的寫入文件:
- if(log_file_.send_n(mblk) == -1) //這里原來是用“->”,申明的是一個對象而不是一個指針。
- return -1;
- if(ACE::debug())
- {
- //mblk這個鏈的第一個作為頭使用。
- ACE_InputCDR cdr(mblk->cont());
- ACE_CDR::Boolean byte_order;
- ACE_CDR::ULong length;
-
- cdr >> ACE_InputCDR::to_boolean(byte_order);
- cdr.reset_byte_order(byte_order);//??
- cdr >> length;
- ACE_Log_Record log_record;
- cdr >> log_record;
- log_record.print(mblk->rd_ptr(), 1, cerr);
- }
- return mblk->total_length();
- }
-
- int Logging_Handler::log_record()
- {
- ACE_Message_Block *mblk = 0;
- if(recv_log_record(mblk) == -1)
- return -1;
- else
- {
- int result = write_log_record(mblk);
- mblk->release();
- return result == -1 ? -1 : 0 ;
- }
- }
5)Iterator_Logging_Server.h
- #ifndef ITERATIVE_LOGGING_SERVER_H
- #define ITERATIVE_LOGGING_SERVER_H
-
- #include "ace/FILE_IO.h"
- #include "ace/INET_Addr.h"
- #include "ace/Log_Msg.h"
-
- #include "Logging_Server.h"
- #include "Logging_Handler.h"
-
- class Iterative_Logging_Server : public Logging_Server
- {
- protected:
- ACE_FILE_IO log_file_;
- //logging_handler在這個類的構造函數中通過初始化列表進行了初始化。神奇啊。這樣也就使Iterative_Logging_Server::log_file_和Logging_Handler::logging_file_產生了聯系。所以后面可以使用send_n()。
- Logging_Handler logging_handler_;
-
- public:
- Iterative_Logging_Server();
-
- Logging_Handler& logging_handler();
-
- virtual ~Iterative_Logging_Server();
-
- protected:
- virtual int open(u_short logger_port);
-
- virtual int handle_connections()
- {
- ACE_INET_Addr logging_peer_addr;
- if(acceptor().accept(logging_handler_.peer(), &logging_peer_addr) == -1)
- ACE_ERROR_RETURN((LM_ERROR,"%p\n","accept()"), -1);
-
- ACE_DEBUG((LM_DEBUG,"Accepted connection from %s\n",
- logging_peer_addr.get_host_name()));
- return 0;
- }
-
- virtual int handle_data(ACE_SOCK_Stream*)
- {
- while(logging_handler_.log_record() != -1)
- continue;
- logging_handler_.close();
- return 0;
- }
- };
-
- #endif
6)Iterator_Logging_Server.cpp
- #include "Iterative_Logging_Server.h"
-
- Iterative_Logging_Server::Iterative_Logging_Server() : logging_handler_(log_file_)
- {
- }
-
- Logging_Handler& Iterative_Logging_Server::logging_handler() // Logging_Handler& logging_handler() const
- {
- return logging_handler_;
- }
-
-
- Iterative_Logging_Server::~Iterative_Logging_Server()
- {
- log_file_.close();
- }
-
-
- int Iterative_Logging_Server::open(u_short logger_port)
- {
- //原型是雙參,這里只傳入單參。第二個參數有一個默認值為0。
- if(make_log_file(log_file_) == -1)
- ACE_ERROR_RETURN((LM_ERROR,"%p\n","make_log_file()"), -1);
- return Logging_Server::open(logger_port);
- }
7)main.cpp
- #include "ace/Log_Msg.h"
- #include "Iterative_Logging_Server.h"
-
- int main(int argc, char* argv[])
- {
- Iterative_Logging_Server server;
-
- if(server.run(argc, argv) == -1)
- ACE_ERROR_RETURN((LM_ERROR,"%p\n","server.run()"), 1);
- return 0;
- }
三、關于文件句柄
很明顯,對文件的操作需要文件句柄,這里我們沒有看到任何句柄,但確實在對文件進行寫操作。除非句柄操作已封裝到了類中,所以不需要我們顯式的去操作。這樣我們可以去看一下源碼。
去看一下“Logging_Server.cpp”這個文件中的“make_log_file()”中的最后一個操作,也就是connector.connect()實現了創(chuàng)建文件。那么也應當在這里初始化了一個句柄。去“FILE_Connector.cpp”中查看這個成員函數。
在這里創(chuàng)建了一個臨時的句柄(句柄本身是在ACE_FILE_IO中):
ACE_HANDLE handle = ACE_INVALID_HANDLE;
//。。。。。。
handle = ACE_OS::mkstemp (filename); // mkstemp() replaces "XXXXXX"
//。。。。。。
new_io.set_handle (handle); //進行句柄的設置。
在“Iterative_Logging_Server.cpp”文件中:
Iterative_Logging_Server::Iterative_Logging_Server() : logging_handler_(log_file_){}
l og_file_(ACE_FILE_IO類)對logging_handler(Logging_Handler類)進行初始化,幸運的是這里采用的是引用方式:
Logging_Handler(ACE_FILE_IO& log_file)
: log_file_(log_file)
{}
那么在Logging_Itrator_Server中的Log_file_的變化都會反應在Logging_Handler類中。所以在我們的:
if(log_file_.send_n(mblk) == -1)
通過這個函數對文件進行了寫入,我們可以在“FILE_IO.inl”文件中可以看其源碼中有這樣的一行:
return ACE::write_n (this->get_handle (),
message_block,
bytes_transferred);
也就是通過write_n()進行寫入,這里便調用了get_handle()用于得到文件句柄(this是一個ACE_FILE_IO類型的指針,其繼承自ACE_FILE類)。
消息分幀圖:

四、客戶端
1)Logging_Client.h
- #include "ace/OS.h"
- //ACE_InputCDR,ACE_OutputCDR,ACE_CDR等類在這里頭文件里:
- #include "ace/CDR_Stream.h"
- #include "ace/INET_Addr.h"
- #include "ace/SOCK_Connector.h"
- #include "ace/SOCK_Stream.h"
- #include "ace/Log_Record.h"
- //ACE里自已封裝的一個流:
- #include "ace/streams.h"
- //原程序中少了這個頭文件:
- #include "ace/Log_Msg.h"
- #include <string>
-
- //這個未定義:
- //#define MAXLOGMSGLEN 40
-
- class Logging_Client
- {
- public:
- // send <log_record> to the server
- int send(const ACE_Log_Record& log_record);
- //獲得這個流
- ACE_SOCK_Stream& peer()
- {
- return logging_peer_;
- }
-
- ~Logging_Client()
- {
- logging_peer_.close();
- }
-
- private:
- // 流
- ACE_SOCK_Stream logging_peer_;
- };
2)Logging_Client.cpp
- #include "Logging_Client.h"
-
- int Logging_Client::send(const ACE_Log_Record &log_record)
- {
- const size_t max_payload_size =
- 4
- 8
- 4
- 4
- ACE_Log_Record::MAXLOGMSGLEN
- ACE_CDR::MAX_ALIGNMENT
- ACE_OutputCDR payload(max_payload_size);
-
- //插入到payload中
- payload << log_record;
- //total_length是計算begin()和end()之間的距離。也就是實際的長度,當然這里面還包含有時間等其安信息。
- ACE_CDR::ULong length = payload.total_length();
- //MAX_ALIGNMENT = 8
- ACE_OutputCDR header(ACE_CDR::MAX_ALIGNMENT 8);
- //ACE_CDR_BYTE_ORDER = 1,關于from_boolean()請追蹤。我在源碼中作了說明。
- header << ACE_OutputCDR::from_boolean(ACE_CDR_BYTE_ORDER);
- header << ACE_CDR::ULong(length);
-
- iovec iov[2];
- //這時是消息頭
- //只包含BYTE-ORDER和PAYLOAD LENGTH這兩個,共8bytes。
- iov[0].iov_base = header.begin()->rd_ptr();
- iov[0].iov_len = 8;
- //在消息分幀中,只有頭兩個屬性是作為了消息頭處理。其余是作為record處理。包含了在mani中寫入的時間,ID等信息。
- //這里是實際長度
- iov[1].iov_base = payload.begin()->rd_ptr();
- iov[1].iov_len = length;
-
- return logging_peer_.sendv_n(iov,2);
- }
3)main.cpp
- #include "Logging_Client.h"
-
- int main(int argc, char* argv[])
- {
- u_short logger_port =
- argc > 1 ? atoi(argv[1]) : 888;
- const char* logger_host =
- argc > 2 ? argv[2] : ACE_DEFAULT_SERVER_HOST;//這個其實就是"localhost"
-
- int result;
-
- ACE_INET_Addr server_addr;
-
- //端口綁定。
- if(logger_port != 0)
- result = server_addr.set(logger_port, logger_host);
- else
- result = server_addr.set("ace_logger", logger_host);
- if(result == -1)
- ACE_ERROR_RETURN((LM_ERROR,
- "lookup %s, %p \n",
- logger_port == 0 ? "ace_logger" : argv[1],
- logger_host), 1);
-
- ACE_SOCK_Connector connector;
- Logging_Client logging_client;
-
- if(connector.connect(logging_client.peer(),
- server_addr) < 0)
- ACE_ERROR_RETURN((LM_ERROR,
- "%p\n",
- "connect()"),
- 1 );
-
- cin.width(ACE_Log_Record::MAXLOGMSGLEN);
- for(;;)
- {
- //從stdin輸入一行數據:
- std::string user_input;
- cout << "watting for your input: ";
- getline(cin, user_input,'\n');
-
- if(!cin || cin.eof())
- break;
-
- //獲得時間:天
- //Informational messages (decimal 8): LM_INFO = 010,
- ACE_Time_Value now(ACE_OS::gettimeofday());
- ACE_Log_Record log_record(LM_INFO, now,
- ACE_OS::getpid() );
- log_record.msg_data(user_input.c_str());
-
- if(logging_client.send(log_record) == -1)
- ACE_ERROR_RETURN((LM_ERROR,
- "%p\n",
- "logging_client.send()"),
- 1 );
- else
- cout << "send the message succesfully!" << endl;
- }
- return 0;
- }
相對簡單容易看懂,故不作分析!
|