当我们的应用服务运行时,通常没有界面监控运行情况,也不能动态配置一些运行参数。那么我们能不能象Linux那样,提供一个类似shell那样的命令行界面进行交互呢? 当然可以,我们利用ACE的接受器和反应器框架,可以轻易为应用服务搭建一个类shell的远程Telnet环境。
主要有两个类实现:
Network_Listener 监听类,打开TCP监听端口,准备接受客户端连接
Network_Handler 为每个客户端连接创建的具体处理类,负责telnet命令交互
在进入反应器主事件循环之前,调用:
// 打开服务配置器
ACE_TCHAR *myargv[] = {0,"-f",”svc.conf”)};
if (ACE_Service_Config::open (3, //argc,
myargv,//argv,
ACE_DEFAULT_LOGGER_KEY,
1,
0,
1) < 0)
{
… …
}
// 创建telnet服务监听器
Network_Listener *listener = newNetwork_Listener;
默认打开的服务端口为ACE_DEFAULT_SERVER_PORT 即20002,你可以在Network_Listener的构造函数里改变它。
具体支持哪些命令,每个命令的含义,可以在Network_Handler::handle_input()函数里定义,该函数已实现了exit, svc, help等命令,其中svc命令可以动态配置ace服务配置器(前提是你先打开它)。另外,你也可以参考ls命令扩展你自己的命令集。
你还可以扩展现有功能,使它更象一个完整的shell,比如支持命令历史等。
// Network_Events.h
#ifndef NETWORK_LISTENER_H_
#define NETWORK_LISTENER_H_
#pragma warning (disable :4786)
#include "ace/Reactor.h"
#include "ace/Service_Config.h"
#include "ace/Service_Repository.h"
#include "ace/Service_Types.h"
#include "ace/WFMO_Reactor.h"
#include "ace/INET_Addr.h"
#include "ace/SOCK_Stream.h"
#include "ace/SOCK_Acceptor.h"
#include "ace/OS_main.h"
classNetwork_Listener : public ACE_Event_Handler
{
public:
Network_Listener (void);
// Default constructor
~Network_Listener (void);
// Default constructor
virtual inthandle_input (ACE_HANDLEhandle);
virtual inthandle_close (ACE_HANDLEhandle,
ACE_Reactor_Maskclose_mask);
ACE_HANDLEget_handle (void) const;
ACE_INET_Addrlocal_address_;
ACE_SOCK_Acceptoracceptor_;
};
#endif
// Network_Events.cpp,v 4.3 2003/11/05 09:36:08 jwillemsen Exp
//
// ============================================================================
//
// = LIBRARY
// examples
//
// = FILENAME
// Network_Events.cpp
//
// = DESCRIPTION
//
// This application tests Reactor to make sure that it responds
// correctly to different kinds of network events.
//
// The test starts off by creating a Network_Listener, that listens
// for connections at ACE_DEFAULT_SERVER_PORT. When a client
// connects, a Network_Handler is created. Network_Handler reads
// messages off the socket and prints them out. This is done until
// the remote side shuts down. Multiple clients can connect at the
// same time.
//
// Events tested in this example includes ACCEPT, READ, and CLOSE masks.
//
// To run this example, start an instance of this example and
// connect to it using telnet (to port
// ACE_DEFAULT_SERVER_PORT(20002)).
//
// = AUTHOR
// Irfan Pyarali
//
// ============================================================================
#include "Network_Events.h"
#include <string>
#include "ace/Get_Opt.h"
ACE_RCSID(WFMO_Reactor, Network_Events, "Network_Events.cpp,v 4.3 2007/7/18 Exp")
const char *strPassPromt = "Password:";
const char *strPass = "888";
const char *strINMS = "Network Management System/r/n";
const char *strVer = "Ver: 2.0/r/n/r/n";
const std::stringsValidChars = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()-_=+{[}]//|;:'/"<,>.?/";
#define MAX_PARA 10
classCommand_Line
{
public:
Command_Line (const char *prog): argc(0)
{
memset(buff,0,BUFSIZ);
strcpy(buff,prog);
inti;
for(i = 0;i < strlen(prog);i++)
{
if (*(buff + i) == ' ')
*(buff + i) = 0;
}
for(i = 0;i < strlen(prog);i++)
{
if ((0 == i && buff[i] != 0) || (buff[i - 1] == 0 && buff[i] != 0))
{
argc ++;
argv[argc - 1] = buff + i;
}
if (argc > MAX_PARA)
break;
}
}
virtual ~Command_Line ()
{
/*for(int i=0;i < argc;i++)
{
delete argv[i];
}*/
}
inline int GetArgc(){return argc;}
inline char **GetArgv(){return argv;}
private:
charbuff[BUFSIZ];
intargc;
char *argv[MAX_PARA];
};
classNetwork_Handler : public ACE_Event_Handler
{
public:
Network_Handler (ACE_SOCK_Stream &s);
// Default constructor
virtual inthandle_input (ACE_HANDLEhandle);
virtual inthandle_close (ACE_HANDLEhandle,
ACE_Reactor_Maskclose_mask);
virtual ACE_HANDLE get_handle (void) const;
ACE_SOCK_Streamstream_;
private:
void handle_cmd_ls(intargc,char *argv[]);
intlist_services (void);
void process_request (ACE_TCHAR *request);
inline intsend(const char *strSend);
charcmd[BUFSIZ];
boolbIsPassword;
};
Network_Handler::Network_Handler (ACE_SOCK_Stream &s)
: stream_ (s)
{
this->reactor (ACE_Reactor::instance ());
intresult = this->reactor ()->register_handler (this, READ_MASK);
ACE_ASSERT (result == 0);
memset(cmd, 0 , sizeof(cmd));
unsigned charbuffer[]={255,251,1,0};//打开客户端回显,主要针对Windows XP的telnet客户端
send((char*)buffer);
send(strINMS);
send(strVer);
send(strPassPromt);
bIsPassword = true;
}
ACE_HANDLE
Network_Handler::get_handle (void) const
{
return this->stream_.get_handle ();
}
int
Network_Handler::handle_input (ACE_HANDLEhandle)
{
//ACE_DEBUG ((LM_DEBUG, "Network_Handler::handle_input handle = %d/n", handle));
//while (1) //不用在这里循环,客户端每输入一个字符,都会激活这个输入事件
{
charmessage[BUFSIZ];
memset(message, 0, BUFSIZ);
intresult = this->stream_.recv_n (message, 1);
if (result > 0)
{
message[result] = 0;
//ACE_DEBUG ((LM_DEBUG, "Remote message: %s/n", message));
if (message[0] == 8) // 退格键
{
message[0]=8;
message[1]=32;
message[2]=8;
if (strlen(cmd) > 0)
{
this->stream_.send (message, 3);
cmd[strlen(cmd) - 1] = 0;
}
}
else if (message[0] == 13) // 回车符,进行命令处理
{
send("/r/n");
if (!bIsPassword && strlen(cmd) > 0)
{
Command_Linecl(cmd);
intargc = cl.GetArgc();
char **argv = cl.GetArgv();
boolbIsValidCmd = true;
if (strcmp(argv[0], "help") == 0 || strcmp(argv[0], "?") == 0)//显示帮助
{
send("/r/nCommands currently supported:/r/n");
send("help list commands currently supported/r/n");
send("ls [-e/s DevID] display information of devices/r/n");
send("svc xxx service configure command/r/n");
send("exit exit/r/n");
send("/r/n");
}
else if (strcmp(argv[0], "svc") == 0)//动态配置ace服务
{
if (argc > 1)
{
inti = 0;
while(cmd[i] == ' ') i++;
process_request(cmd + i + (argv[1] - argv[0]));
}
else
{
send("/r/nservice configure command:/r/n");
send("help list states of all managed service/r/n");
send("reconfigure reload the service configuration/r/n");
send("suspend xxx suspend service named xxx/r/n");
send("resume xxx resume service named xxx/r/n");
send("/r/n");
}
}
else if (strcmp(argv[0], "ls") == 0)//列出设备信息的命令
{
handle_cmd_ls(argc, argv);//ls命令的处理函数
}
else if (strcmp(argv[0], "exit") == 0) //退出命令
{
ACE_DEBUG ((LM_DEBUG, "%D Telnet client exited %d/n", handle));
//linux下面必须注销reactor上的注册,不然阻塞主事件循环
this->reactor ()->remove_handler (this, READ_MASK);
this->stream_.close ();
return 0;
}
else
{
bIsValidCmd = false;
send("unkown command./r/n");
send("/r/n");
}
if (bIsValidCmd)
ACE_DEBUG ((LM_DEBUG, "%D Telnet Handle %d cmd : %s/n", handle, cmd));
}
if (bIsPassword && strcmp(cmd, strPass) == 0)
{
send("/r/n");
bIsPassword = false;
}
memset(cmd, 0, sizeof(cmd));
if (bIsPassword)
{
send(strPassPromt);
}
else
send(">");
}
else //ordinary char
{
if (sValidChars.find(message,0,1) != -1)
{
strcat(cmd, message);
if (bIsPassword)
{
send("*");//密码回显成*
}
else
this->stream_.send (message, sizeof(message));
}
}
}
else if (result == 0)
{
ACE_DEBUG ((LM_DEBUG, "%D Telnet client connection closed %d/n", handle));
return -1;
}
else if (errno == EWOULDBLOCK)
{
return 0;
}
else
{
//release版里面后总是跑到这里来,不知何故
/*ACE_DEBUG ((LM_DEBUG, "%D Telnet receiving problems, result = %d/n", result));
return -1;*/
}
}
return 0;
}
int
Network_Handler::handle_close (ACE_HANDLEhandle,
ACE_Reactor_Mask)
{
ACE_DEBUG ((LM_DEBUG, "%D Telnet::handle_close handle = %d/n", handle));
#ifdef WIN32
this->stream_.close_writer();
#endif
this->stream_.close ();
delete this;
// ACE_Reactor::end_event_loop ();
return 0;
}
Network_Listener::Network_Listener (void)
: local_address_ (ACE_DEFAULT_SERVER_PORT),
acceptor_ (local_address_, 1)
{
this->reactor (ACE_Reactor::instance ());
intresult = this->reactor ()->register_handler (this,
ACE_Event_Handler::ACCEPT_MASK);
//ACE_ASSERT (result == 0);
}
Network_Listener::~Network_Listener (void)
{
}
ACE_HANDLE
Network_Listener::get_handle (void) const
{
return this->acceptor_.get_handle ();
}
int
Network_Listener::handle_input (ACE_HANDLEhandle)
{
ACE_DEBUG ((LM_DEBUG, "%D Telnet::handle_input handle = %d/n", handle));
ACE_INET_Addrremote_address;
ACE_SOCK_Streamstream;
// Try to find out if the implementation of the reactor that we are
// using requires us to reset the event association for the newly
// created handle. This is because the newly created handle will
// inherit the properties of the listen handle, including its event
// associations.
intreset_new_handle = this->reactor ()->uses_event_associations ();
intresult = this->acceptor_.accept (stream, // stream
&remote_address, // remote address
0, // timeout
1, // restart
reset_new_handle); // reset new handler
ACE_ASSERT (result == 0);
remote_address.dump ();
Network_Handler *handler;
ACE_NEW_RETURN (handler, Network_Handler (stream), -1);
ACE_DEBUG ((LM_DEBUG, "%D Telnet connection from: [%s:%d] %d/n",
remote_address.get_host_addr(),
remote_address.get_port_number(),
handler->get_handle()));
return 0;
}
int
Network_Listener::handle_close (ACE_HANDLEhandle,
ACE_Reactor_Mask)
{
ACE_DEBUG ((LM_DEBUG, "%d Telnet::listener_close handle = %d/n", handle));
this->acceptor_.close ();
delete this;
return 0;
}
intNetwork_Handler::send(const char *strSend)
{
return this->stream_.send (strSend, strlen(strSend));
}
void Network_Handler::process_request(ACE_TCHAR *request)
{
ACE_TRACE("process_request");
ACE_TCHAR *p;
// Kill trailing newlines.
for (p = request;
(*p != '/0') && (*p != '/r') && (*p != '/n');
p++)
continue;
*p = '/0';
if (ACE_OS::strcmp (request, ACE_LIB_TEXT ("help")) == 0)
// Return a list of the configured services.
list_services ();
else if (ACE_OS::strcmp (request, ACE_LIB_TEXT ("reconfigure") )== 0)
{
// Trigger a reconfiguration by re-reading the local <svc.conf> file.
ACE_Service_Config::reconfig_occurred ((sig_atomic_t) 1);
send("Reconfiguration done./r/n/r/n");
}
else
// Just process a single request passed in via the socket
// remotely.
ACE_Service_Config::process_directive (request);
// Additional management services may be handled here...
}
//列出ace服务配置器管理的服务列表
intNetwork_Handler::list_services()
{
ACE_TRACE ("list_services");
ACE_Service_Repository_Iteratorsri (*ACE_Service_Repository::instance (), 0);
send("Service running state:/r/n");
inti = 0;
for (const ACE_Service_Type *sr;
sri.next (sr) != 0;
sri.advance ())
{
i++;
charbuff[BUFSIZ];
sprintf(buff,"#%2.2d %-20s %-10s",
i,
sr->name (),
(sr->active ()) ? "(active)" : "(paused)" );
if (ACE_OS::strlen(buff) > 0)
{
ssize_tn = send (buff);
send("/r/n");
if (n <= 0 && errno != EPIPE)
ACE_ERROR ((LM_ERROR,
ACE_LIB_TEXT ("%p/n"),
ACE_LIB_TEXT ("send_n")));
}
}
send("/r/n");
return 0;
}
//该函数用来处理命令ls,这是一个命令举例,实际应用时应根据自身需要改写或添加命令处理函数
void Network_Handler::handle_cmd_ls(intargc, char *argv[])
{
charbuff[BUFSIZ];
//用ACE工具类进行命令行参数分析
ACE_Get_Optget_opt (argc, argv, ACE_TEXT ("e:s:?"));
intc;
if (argc > 1)
{
while ((c = get_opt ()) != -1)
{
switch (c)
{
case 'e': //处理-e 参数
{
//列出某个设备的具体信息
intiDevID = ACE_OS::atoi (get_opt.opt_arg ());
//......列出该设备基本配置信息
}
break;
case 's': //处理 -s 参数
{
intiDevID = ACE_OS::atoi (get_opt.opt_arg ());
//......列出该设备SNMP配置信息
}
break;
case '?':
default:
//如果是?或者其他非法参数,则打出usage
send("Usage: ls [-e devid] [-s devid]/r/n");
break;
}
}
}
else //打印全部设备的信息
{
sprintf(buff,"%5s %3s %6s %4s %8s %-15s"
"%5s %6s/r/n","DevID","Num","Parent","Type","Protocol",
"IP_ADDR","Port","Online");
send(buff);
//......列出全部设备信息
}
send("/r/n");
}