ace第二课-- 一个日志服务器的实现

一个日志服务器的实现(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类型参数,那么不会执行if中的语句,而执行else中语句。并通过return中语句来产生一个新文件。

4、在return中调用Logging_Server::open()函数。这个函数用于实现建立一个通信端点。然后返回Logging_Server::run()中继续运行。

5、执行Iterative_Logging_Server::handle_connections()函数。在这里进行等待客户端的连接。待有输入后继续运行。

6、执行Iterative_Logging_Server:: handle_data()函数。用入等待用户输入,然后将输入写入日志文件,这里调用了Logging_Handler:: log_record()函数,这里是一个while()循环,只有输入错误才会停止。

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。这里面还包含有length等东东。
		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()是不是针对同一个节点进行的操作呢?需要考证一下!
		//在客户端中使用了ACE_CDR::MAX_ALIGNMENT来分配空间,所以这里也一样了。
		//加8是要保存前面我们存入的8BYTE的消息头。
		if(logging_peer_.recv_n(payload->wr_ptr(), length) > 0)
		{
			//payload是动态分配的,所以这个函数运行完成后,这个变量无效,但是这个内存还在,不会删除。
			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()实现了创建文件。那么也应当在这里初始化了一个句柄。去“FILE_Connector.cpp”中查看这个成员函数。

在这里创建了一个临时的句柄(句柄本身是在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;
}

 

       相对简单容易看懂,故不作分析!

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值