P2P TELNET
一 缘起
几年前做一个嵌入式的Linux平台上的一个控制和监测的项目。因为施工后,还有很大的可能需要对现场设备进行管理维护、以及应用软件的升级修改操作,而实际设备在安装后的网络环境非常复杂,有可能是多重NAT,也有可能采用的2G,3G或者4G网络。并且设备的安装在地域上的跨度非常大,维护工程师在远程无法访问设备,也就造成无法完成上述任务。
当时意识到如果有一个能够像telnet,ssh一样的P2P的软件,这个问题就可以迎刃迎刃而解了。但是由于工作的问题,想到这个东西了却一直没有动手去考虑实现。最近抽出时间简单的实现一个telnet
二 原理
远程无法访问设备的最根本的在于通讯无法进行。如果能够解决通讯问题则可以实现对应的功能,这个就类似QQ的远程桌面。
在网上找到有两个协议非常适合这样的一个任务,这两个协议分别是XMPP和SIP。因此软件的实现就基于这两个协议来实现。
将软件分成服务端和客户端,服务端是被控制的一端,客户端是控制的一端。现在XMPP和SIP的客户端软件非常多,都可以拿来作为客户端使用。因此软件的开发是开发服务端软件
2.1 XMPP协议。
XMPP是一种基于标准通用标记语言的子集XML的协议,它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。经过扩展以后的XMPP可以通过发送扩展的信息来处理用户的需求,以及在XMPP的顶端建立如内容发布系统和基于地址的服务等应用程序。而且,XMPP包含了针对服务器端的软件协议,使之能与另一个进行通话,这使得开发者更容易建立客户应用程序或给一个配好系统添加功能。
XMPP协议严格来讲并不是一个P2P的协议,XMPP的数据通讯需要有一个中间服务器进行转发,所以这是一个中继通讯的协议。考虑到即使有人使用这样的一个方式进行处理,远程设备的管理,因为管理的操作本身并发性不集中。所以XMPP协议完全可以使用,不用考虑它的中继特性了。
XMPP有很多开源的项目,其中gloox是一个用C++实现的XMPP协议客户端库,接口良好,文档也全面,并且整个库编译后也很小,大概1MB吧。完全可以用于嵌入场合。并且这个库是一个跨平台的库,也就意味着可以用于windows,Linux等,笔者也成功的将其移植到arm a8上。
2.2 SIP协议。
SIP(Session Initiation Protocol,会话初始协议)是由IETF(Internet Engineering Task Force,因特网工程任务组)制定的多媒体通信协议。它是一个基于文本的应用层控制协议,用于创建、修改和释放一个或多个参与者的会话。广泛应用于CS(Circuit Switched,电路交换)、NGN(Next Generation Network,下一代网络)以及IMS(IP Multimedia Subsystem,IP多媒体子系统)的网络中,可以支持并应用于语音、视频、数据等多媒体业务,同时也可以应用于Presence(呈现)、Instant Message(即时消息)等特色业务。可以说,有IP网络的地方就有SIP协议的存在。
SIP协议在会话发起之后可以选择采用UDP 的点到点通讯。因此SIP协议在使用的时候可以是一个真正意义上的点到点通讯。
pjsip是一个非常优秀的SIP协议开源库,这是一个用C/C++实现的库。整个库非常小完全可以用于嵌入式系统。并且这个库可以用于windows,Linux多个平台。
三 设计
程序的主要流程是:
四 然后就是干货,主要代码。如果有需要的整体代码包的可以联系我
4.1 执行器
#include "exec_linux.h"
#include <stdio.h>
#if defined( __LINUX__ )
CExecLinux::CExecLinux( const std::string& usr , const std::string& str , CP2Pbackend& msger) :
CP2PExec( usr, str, msger )
{
p_cmd_stdin = proc.GetOutputStream();
p_cmd_stdout = proc.GetInputStream();
p_cmd_stderr = proc.GetErrorStream();
}
////////////////////////////////////////////////////////////////////////////////
CExecLinux::~CExecLinux()
{
{
boost::mutex::scoped_lock l( m_mutex );
m_is_run = false;
}
my_sleep( 500 );
}
////////////////////////////////////////////////////////////////////////////////
size_t CExecLinux::exec_read( void* buff , size_t len , int& err )
{
if( buff == NULL ) return -4;
if( len > CP2PExec::READ_BUFF_SIZE ) len = CP2PExec::READ_BUFF_SIZE;
size_t ret = 0;
//读标准错误输出通道
ret = read( p_cmd_stderr , buff , len );
if( ret != (size_t)-1 ) err = 0;
else err = -2;
//读标准输出通道
if( err != -2 ){
if( len + ret > CP2PExec::READ_BUFF_SIZE ) len = CP2PExec::READ_BUFF_SIZE - ret;
ret = read( p_cmd_stdout , (char*)(buff + ret ), len );
if( ret != (size_t)-1 ) err = 0;
else err = 0;
}
else{
ret = read( p_cmd_stdout , buff , len );
if( ret != (size_t)-1 ) err = 0;
else err = 0;
}
return ret;
}
////////////////////////////////////////////////////////////////////////////////
size_t CExecLinux::exec_write( const void* buff , size_t len , int &err )
{
size_t ret = write( p_cmd_stdin , buff , len );
if( ret == (size_t)-1) err = -1;
else err = 0;
return ret;
}
////////////////////////////////////////////////////////////////////////////////
void CExecLinux::Start()
{
m_is_run = true;
my_sleep( 1000 );
boost::thread thd( boost::bind( &CExecLinux::do_run , this ));
}
////////////////////////////////////////////////////////////////////////////////
size_t CExecLinux::exec_write( const std::string& data )
{
int err = 0;
size_t ret = 0;
std::string cmd = data;
#if defined __DEBUG__
std::cout << "last cmd = " << m_last_cmd << std::endl;
#endif
if( data == "stop"){
if(m_last_cmd.empty() == false ){
cmd = "killall -9 " + m_last_cmd;
system( cmd.c_str() );
m_last_cmd.clear();
}
ret = 0;
}
else{
cmd = cmd + "\n";
size_t pos = data.find_first_of(' ');
if( pos != std::string::npos )
m_last_cmd = data.substr( 0, pos );
else
m_last_cmd = data;
ret = exec_write( cmd.c_str() , strlen( cmd.c_str() ) , err);
}
return ret;
}
#endif
4.2 通讯控制器
#include "inc.h"
#if USE_GLOOX_LIB == 1
#if defined(__WXMSW__)
#include <wx/wx.h>
#include <wx/process.h>
#endif // defined
#include <gloox/disco.h>
#include "CBackendGloox.h"
#if defined( _WIN32 ) || defined(__GNUWIN32__ ) || defined( __WXMSW__ )
#include "exec_win32.h"
#elif defined( __LINUX__ )
#include "exec_linux.h"
#endif // defined
#include <stdio.h>
CBackendGloox *globe_p2p_telnetd = NULL;
CBackendGloox::CBackendGloox(const std::string& svr , const std::string& usr , const std::string& passwd)
:CP2Pbackend( svr , usr , passwd )
{
do_connect(svr , usr , passwd );
// array_exec.clear();
}
CBackendGloox::~CBackendGloox()
{
}
void CBackendGloox::Re( const std::string& msg )
{
m_session->send( "\n" + msg );
}
void CBackendGloox::do_start_shell( const std::string& user )
{
#if defined(__WXMSW__ ) || defined(_WIN32) || defined( __GNUWIN32__ )
CExecWin32 *p_exec = NULL;
p_exec = new CExecWin32( user , "cmd.exe" , *this );
*p_exec << "telnet 127.0.0.1\r\n";
#elif defined( __LINUX__ )
CExecLinux *p_exec = NULL;
p_exec = new CExecLinux( user , "/bin/bash" , *this );
#endif
if( p_exec == NULL ) return;
p_exec->SetNotify( boost::bind( &CBackendGloox :: Re , this , _1 ));
p_exec->Start();
make_new_connection( p_exec );
}
void CBackendGloox::do_connect( const std::string& svr , const std::string& usr , const std::string& passwd )
{
#if defined __LINUX__
std::string sys = "lINUX";
#elif defined _WIN32
std::string sys = "Windows";
#endif // defined
std::stringstream ss;
ss << usr << "@" << svr ;
std::string str_jid = ss.str();
JID jid( ss.str() );
j = new gloox::Client( jid , passwd );
if( j == NULL ) throw j;
j->registerConnectionListener( this );
j->registerMessageSessionHandler( this , 0 );
j->disco()->setVersion( "p2ptelnetd", GLOOX_VERSION, "test" );
j->disco()->setIdentity( "client", "bot" );
j->disco()->addFeature( XMLNS_CHAT_STATES );
StringList ca;
ca.push_back( "/data/cacert.crt" );
j->setCACerts( ca );
j->logInstance().registerLogHandler( LogLevelDebug, LogAreaAll, this );
if( j->connect( ) ){//使用阻塞方式启动客户端
ConnectionError ce = ConnNoError;
while( ce == ConnNoError ) {
ce = j->recv();
}
printf( "ce: %d\n", ce );
}
delete( j );
}
//////////////////////////////////////////////////////////////////////////////////////
void CBackendGloox::handleMessage( const Message& msg, MessageSession * /*session*/ )
{
std::string user = msg.from().full();
//将远程发过来的数据发送给执行器,让执行器执行
//完成后执行器会自动调用反馈处理
//if( msg.body().empty() == true ) return;
process_msg( user , msg.body() );
}
void CBackendGloox::handleMessageEvent( const JID& from, MessageEventType event )
{
// printf( "received event: %d from: %s\n", event, from.full().c_str() );
}
void CBackendGloox::onDisconnect( ConnectionError e )
{
printf( "message_test: disconnected: %d\n", e );
if( e == ConnAuthenticationFailed )
printf( "auth failed. reason: %d\n", j->authError() );
}
void CBackendGloox::onConnect()
{
#if defined( __WXDEBUG__ ) || defined( __DEBUG__ )
std::cout << "connected server " << std::endl;
#endif // defined
}
bool CBackendGloox::onTLSConnect( const CertInfo& info )
{
time_t from( info.date_from );
time_t to( info.date_to );
printf( "status: %d\nissuer: %s\npeer: %s\nprotocol: %s\nmac: %s\ncipher: %s\ncompression: %s\n"
"from: %s\nto: %s\n",
info.status, info.issuer.c_str(), info.server.c_str(),
info.protocol.c_str(), info.mac.c_str(), info.cipher.c_str(),
info.compression.c_str(), ctime( &from ), ctime( &to ) );
return true;
}
void CBackendGloox::handleChatState( const JID& from, ChatStateType state )
{
printf( "received state: %d from: %s\n", state, from.full().c_str() );
}
void CBackendGloox::handleLog( LogLevel level, LogArea area, const std::string& message )
{
//printf("log: level: %d, area: %d, %s\n", level, area, message.c_str() );
}
void CBackendGloox::handleMessageSession( MessageSession *session )
{
j->disposeMessageSession( m_session );
m_session = session;
m_session->registerMessageHandler( this );
m_messageEventFilter = new MessageEventFilter( m_session );
m_messageEventFilter->registerMessageEventHandler( this );
m_chatStateFilter = new ChatStateFilter( m_session );
m_chatStateFilter->registerChatStateHandler( this );
}
void CBackendGloox :: disconnect()
{
j->disconnect();
}
#endif
转载于:https://my.oschina.net/u/3071588/blog/1542017