Qt:利用telnet连接PMAC
POWER PMAC是一个新版的运动控制器,与原来的turbo PMAC相比缺少了一套基于C++且类似于PComm32的通讯库,但不管是哪种PMAC,本意都是利用TCP/IP的协议来与上位机进行连接。Power PMAC是利用Telnet协议来进行通讯的,本代码是利用Qt来实现Telnet系统,准确的来说是利用windows和linux下的套接字来实现。另外当然也可以使用Qt自带的网络套接字来实现连接。
Power PMAC是利用telnet的方式来进行通讯,我们可以打开windows下的功能与服务,将telnet服务开启。
账号:root
密码:deltatau
然后继续在终端中输入启动程序:gpascii
这样就可以启动PMAC了,但是问题来了:对于我们进行控制开发,想要使用一种自动的程序进行调试,控制PMAC,发送数据给PMAC,例如采集图像信息然后经过处理反馈给PMAC,使用C++设计API函数来自动的对PMAC进行连接呢?
步骤:
1.创建套接字,与PMAC建立连接,端口号为23(telnet端口为23);
2.与PMAC进行协商;只有完成了协商才能够实现相互间的通讯,协商时需要三个字节:首先需要读取IAC,读取到了才能进行下一步,第二个字节是命令码,即WILL 、 DO 、 WONT 、 DONT 四者之一,最后一位是选项码,对应的是选项功能;例如服务器消息: 255 251 24(IAC WILL 24—要求激活终端类型),这时你需要发送:255 253 24(IAC DO 24—执行激活终端类型),这样就可以建立协商了。
参考文献:
telnet命令:
telnet.h:
#ifndef TELNET_H
#define TELNET_H
#include <QObject>
#include <iostream>
#include <time.h>
#include <Windows.h>
#include <QString>
#include <QDebug>
//#pragma comment(lib,"ws2_32.lib")
class telnet:public QObject
{
Q_OBJECT
public:
telnet();
~telnet();
void connect();
SOCKET sock2;
inline bool telSendMess(QString str);
QString reciveMess();
bool ConnectStatus;
protected:
void telnetnvt();
private:
HANDLE stou1;
signals:
void connectedReady(bool);
};
#endif // TELNET_H
telnet.cpp:
#include <iostream>
#include "telnet.h"
#define WILL 251
#define WONT 252
#define DO 253
#define DONT 254
#define IAC 255
using namespace std;
telnet::telnet():ConnectStatus(false)
{
stou1 = GetStdHandle(STD_OUTPUT_HANDLE);
//socket版本
WORD wVersion;
wVersion = MAKEWORD(2, 1);
//初始化套接字库
WSADATA wsaData;
WSAStartup(wVersion, &wsaData);
SOCKADDR_IN server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family=AF_INET;
//监听23端口
//server_addr.sin_addr.S_un.S_addr=INADDR_ANY;
server_addr.sin_port = htons(IPPORT_TELNET);//IPPORT_TELNET
server_addr.sin_addr.s_addr = inet_addr("192.168.0.110");
memset(server_addr.sin_zero, 0x00, 8);
if ((sock2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
qDebug()<<QString::fromLocal8Bit("套接字创建失败!")<<endl;
if (::connect(sock2, (struct sockaddr *) &server_addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
qDebug()<<QString::fromLocal8Bit("PMAC连接失败!")<<endl;
else
{
qDebug()<<QString::fromLocal8Bit("PMAC创建成功")<<endl;
}
Sleep(3000);
//与服务器协商
telnetnvt();
}
telnet::~telnet()
{
//关闭套接字
closesocket(sock2);
WSACleanup();
}
void telnet::connect()
{
Sleep(1000);
telSendMess("root");//root
Sleep(1000);
qDebug()<<"root\n";
telSendMess("deltatau");//deltatau
Sleep(1000);
qDebug()<<"deltatau\n";
//get into the shell;
telSendMess("gpascii");//gpascii
Sleep(1000);
qDebug()<<"gpascii\n";
}
//发送数据
inline bool telnet::telSendMess(QString str)
{
char pBuff[256]={0};
int nRet = str.length();
QByteArray ba = str.toLatin1();
strncpy(pBuff, ba.data(), nRet);
if(send(sock2,ba.data(),nRet,0)<0)
{
qDebug()<<QString::fromLocal8Bit("发送失败:")<<ba.length()<<endl;
emit connectedReady(false);
return false;
}
else if(!ConnectStatus)
{
ConnectStatus = true;
qDebug()<<QString::fromLocal8Bit("发送成功")<<endl;
emit connectedReady(true);
}
//cout<<ba.data()<<endl;
send(sock2, "\r\n", 1, 0);//这个控制字符必须要---
return true;
}
//接收数据
QString telnet::reciveMess()
{
char msg[1024] = {0};
memset(msg, 0, 1024);
int res = recv(sock2, msg, 1024, 0);
if (res == SOCKET_ERROR)
{
qDebug()<<"Receive Failed\n";
return nullptr;
}
if (res == 0)
{
Sleep(10);
qDebug()<<"Can't get the message!\n";
}
return QString(msg);
}
void telnet::telnetnvt()
{
unsigned char strRecvBuf[3] = {0};
while(1)
{
//选项协商需要 3 个字节
//例如 IAC DONT ECHO
//第一个 IAC 一个字节 (0xff)
if(recv(sock2, (char*)(strRecvBuf), 1, 0)!=1)
return;
//如果不是IAC 选项
if(strRecvBuf[0] != IAC)
break;
//第二个字节是 WILL 、 DO 、 WONT 、 DON''T 四者之一
//最后一个 ID 字节指明激活或禁止选项。
if(recv(sock2, (char*)(strRecvBuf+1), 2, 0)!=2)//接收到两个字节,WILL和ID
return;
//查看第二个字节是什么,以选择相应的代码
switch(strRecvBuf[1])
{
case WILL: //WILL
strRecvBuf[1] = DO; //DO
break;
case WONT: //WONT
strRecvBuf[1] = DONT; //DONT
break;
case DO: //DO
case DONT: //DONT
strRecvBuf[1] = WONT; //WONT
break;
default:
return;
}
//发送信息反馈
send(sock2, (char*)strRecvBuf, 3, 0);
//qDebug()<<strRecvBuf[1];
}
}
Tips: 使用reciveMess
函数时一定要判断是否有信息传输回来,缓冲区是否有数据,否则就会出现线程阻塞,一直等待读取。
另外在windows下使用TCP/IP网络,一定要在CMakeLists.txt文件中添加这么一句 link_libraries(ws2_32 wsock32)
telnet.h
#ifndef TELNET_H
#define TELNET_H
#include <QObject>
#include <iostream>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QTcpServer>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <stdlib.h>
#include <QString>
//#include <winsock2.h>
class telnet:public QObject
{
Q_OBJECT
public:
telnet();
~telnet();
int sock2;
void connet();
void telSendMess(QString str,int hSocket);
QString reciveMess();
bool hasconnect;
protected:
void telnetnvt();
signals:
void connectedReady(bool);
private slots:
void connected()
{
qDebug()<<"connect successed!"<<endl;
}
void error(QAbstractSocket::SocketError)
{
qDebug()<<"can't connect,faild!\n";
}
void error()
{
qDebug()<<"can't connect,faild!\n";
}
void connectionError(QAbstractSocket::SocketError error)
{
qDebug()<<"connection Error\n";
}
void disconnected()
{
qDebug()<<"has disconnect\n";
}
};
#endif // TELNET_H
telnet.cpp
#include <iostream>
#include "sock.h"
#include <time.h>
//#include <Windows.h>
using namespace std;
telnet::telnet():hasconnect(false)
{
struct sockaddr_in server_addr;
memset(&server_addr,0,sizeof(server_addr));/*将serv_addr各个字段清零*/
server_addr.sin_family=AF_INET;
//监听23端口
//server_addr.sin_addr.S_un.S_addr=INADDR_ANY;
server_addr.sin_port = htons(IPPORT_TELNET);//IPPORT_TELNET
server_addr.sin_addr.s_addr = inet_addr("192.168.0.110");//192.168.0.110 192.168.10.129
if ((sock2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
qDebug()<<"the socket created failed!\n";
else{
qDebug()<<"the socket created successful\n";
}
if (::connect(sock2, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1)
qDebug()<<"pmac connect failed!\n";
else
{
qDebug()<<"PMAC creat successful!\n";
}
usleep(3000);
//与服务器协商
telnetnvt();
}
telnet::~telnet()
{
//关闭套接字
close(sock2);
}
void telnet::connet()
{
usleep(500);
//reciveMess();
telSendMess("root", sock2);//root
reciveMess();
sleep(1);
telSendMess("deltatau", sock2);//deltatau
sleep(1);
reciveMess();
//qDebug()<<"-------------------------------get into the shell--------------------------------\n";
telSendMess("gpascii", sock2);//gpascii
sleep(1);
reciveMess();
//reciveMess();
//qDebug()<<"---------------------------------发送变量数据---------------------------------------\n";
}
void telnet::telSendMess(QString str,int hSocket)
{
char pBuff[256]={0};
int nRet = str.length();
QByteArray ba = str.toLatin1();
strncpy(pBuff, ba.data(), nRet);
/*while (nRet--)
{
int resp = send(hSocket, &pBuff[i], 1, 0);
qDebug()<<pBuff[i];
i++;
//if (nRet == 0) break;
}
*/
if(send(hSocket,ba.data(),nRet,0)<0)
{
qDebug()<<"发送失败:"<<ba.length()<<endl;
emit connectedReady(false);
}
else if(!hasconnect)
{
hasconnect = true;
qDebug()<<"发送成功\n";
emit connectedReady(true);
}
//qDebug()<<ba.data()<<endl;
send(hSocket, "\r\n", 1, 0);//这个控制字符必须要---------------------------
}
QString telnet::reciveMess()
{
char msg[1024] = {0};
memset(msg, 0, 1024);
int res = recv(sock2, msg, 1024, 0);
if (res == -1)
{
qDebug()<<"接收失败\n";
return 0;
}
if (res == 0)
{
usleep(10);
qDebug()<<"can't get the message!\n";
}
//printf("%s\n",msg);
//qDebug()<<msg<<endl;
return QString(msg);
}
void telnet::telnetnvt()
{
unsigned char strRecvBuf[3] = {0};
while(1)
{
if(recv(sock2, (char*)(strRecvBuf), 1, 0)!=1)
return;
if(strRecvBuf[0] != 255)
break;
if(recv(sock2, (char*)(strRecvBuf+1), 2, 0)!=2)
return;
switch(strRecvBuf[1]){
case 251: //WILL
strRecvBuf[1] = 253; //DO
break;
case 252: //WONT
strRecvBuf[1] = 254; //DONT
break;
case 253: //DO
case 254: //DONT
strRecvBuf[1] = 252; //WONT
break;
default:
return;
}
send(sock2, (char*)strRecvBuf, 3, 0);
//qDebug()<<strRecvBuf[1];
}
}
如果是在windows下,需要添加link_libraries(ws2_32 wsock32)
,为了避免麻烦,我们可以在CMAKE中添加:
IF(CMAKE_SYSTEM_NAME MATCHES "Windows")
set(CMAKE_C_COMPILER "clang")
link_libraries(ws2_32 wsock32)
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
set(CMAKE_C_COMPILER "clang") #gcc
set(CMAKE_CXX_COMPILER "clang++") #g++
set(CMAKE_CXX_FLAGS "-std=c++11 -O3 -Wall")
ENDIF()
现在就可以快乐的使用C++连接power pmac的API函数了!
std::shared_ptr<telnet> pmacConnect;
pmacConnect = std::shared_ptr<telnet>(new telnet);
pmacConnect->connect();
...
QString message;
...
pmacConnect->telSendMess(message);
因篇幅问题不能全部显示,请点此查看更多更全内容