当我们的应用服务运行时,通常没有界面监控运行情况,也不能动态配置一些运行参数。那么我们能不能象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
=
new
Network_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"
class
Network_Listener
: public
ACE_Event_Handler
{
public:
Network_Listener
(void);
// Default constructor
~
Network_Listener
(void);
// Default constructor
virtual
int
handle_input
(
ACE_HANDLE
handle
);
virtual
int
handle_close
(
ACE_HANDLE
handle
,
ACE_Reactor_Mask
close_mask
);
ACE_HANDLE
get_handle
(void) const;
ACE_INET_Addr
local_address_
;
ACE_SOCK_Acceptor
acceptor_
;
};
#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::
string
sValidChars
=
"abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()-_=+{[}]//|;:'/"<,>.?/"
;
#define
MAX_PARA
10
class
Command_Line
{
public:
Command_Line
(const
char
*
prog
):
argc
(0)
{
memset
(
buff
,0,
BUFSIZ
);
strcpy
(
buff
,
prog
);
int
i
;
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
:
char
buff
[
BUFSIZ
];
int
argc
;
char
*
argv
[
MAX_PARA
];
};
class
Network_Handler
: public
ACE_Event_Handler
{
public:
Network_Handler
(
ACE_SOCK_Stream
&
s
);
// Default constructor
virtual
int
handle_input
(
ACE_HANDLE
handle
);
virtual
int
handle_close
(
ACE_HANDLE
handle
,
ACE_Reactor_Mask
close_mask
);
virtual
ACE_HANDLE
get_handle
(void) const;
ACE_SOCK_Stream
stream_
;
private
:
void
handle_cmd_ls
(
int
argc
,
char
*
argv
[]);
int
list_services
(void);
void
process_request
(
ACE_TCHAR
*
request
);
inline
int
send
(const
char
*
strSend
);
char
cmd
[
BUFSIZ
];
bool
bIsPassword
;
};
Network_Handler
::
Network_Handler
(
ACE_SOCK_Stream
&
s
)
:
stream_
(
s
)
{
this->
reactor
(
ACE_Reactor
::
instance
());
int
result
= this->
reactor
()->
register_handler
(this, READ_MASK);
ACE_ASSERT
(
result
== 0);
memset
(
cmd
, 0 , sizeof(
cmd
));
unsigned
char
buffer
[]={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_HANDLE
handle
)
{
//ACE_DEBUG ((LM_DEBUG, "Network_Handler::handle_input handle = %d/n", handle));
//while (1) //
不用在这里循环,客户端每输入一个字符,都会激活这个输入事件
{
char
message
[
BUFSIZ
];
memset
(
message
, 0,
BUFSIZ
);
int
result
= 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_Line
cl
(
cmd
);
int
argc
=
cl
.GetArgc();
char
**
argv
=
cl
.GetArgv();
bool
bIsValidCmd
= 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)
{
int
i
= 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_HANDLE
handle
,
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
());
int
result
= 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_HANDLE
handle
)
{
ACE_DEBUG
((LM_DEBUG,
"%D Telnet::handle_input handle = %d/n"
,
handle
));
ACE_INET_Addr
remote_address
;
ACE_SOCK_Stream
stream
;
// 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.
int
reset_new_handle
= this->
reactor
()->
uses_event_associations
();
int
result
= 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_HANDLE
handle
,
ACE_Reactor_Mask
)
{
ACE_DEBUG
((LM_DEBUG,
"%d Telnet::listener_close handle = %d/n"
,
handle
));
this->
acceptor_
.
close
();
delete this;
return 0;
}
int
Network_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
服务配置器管理的服务列表
int
Network_Handler
::
list_services
()
{
ACE_TRACE
(
"list_services"
);
ACE_Service_Repository_Iterator
sri
(*
ACE_Service_Repository
::
instance
(), 0);
send
(
"Service running state:/r/n"
);
int
i
= 0;
for (const
ACE_Service_Type
*
sr
;
sri
.
next
(
sr
) != 0;
sri
.
advance
())
{
i
++;
char
buff
[
BUFSIZ
];
sprintf
(
buff
,
"#%2.2d %-20s %-10s"
,
i
,
sr
->
name
(),
(
sr
->
active
()) ?
"(active)"
:
"(paused)"
);
if (ACE_OS::
strlen
(
buff
) > 0)
{
ssize_t
n
=
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
(
int
argc
,
char
*
argv
[])
{
char
buff
[
BUFSIZ
];
//
用
ACE
工具类进行命令行参数分析
ACE_Get_Opt
get_opt
(
argc
,
argv
,
ACE_TEXT
(
"e:s:?"
));
int
c
;
if (
argc
> 1)
{
while ((
c
=
get_opt
()) != -1)
{
switch (
c
)
{
case
'e'
:
//
处理
-e
参数
{
//
列出某个设备的具体信息
int
iDevID
= ACE_OS::
atoi
(
get_opt
.
opt_arg
());
//......
列出该设备基本配置信息
}
break;
case
's'
:
//
处理
-s
参数
{
int
iDevID
= 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"
);
}