阅读目录
1. 远程开机原理
1.1 简述
电脑关机之后,BIOS继续给网卡供电,网卡等待接收一个数据包(MagicPacket),收到数据包之后唤醒,进入开机流程。
1.2 WakeOnLAN
WakeOnLAN简称WOL,是一种电源管理功能。WakeOnLAN的实现,主要是向目标主机发送特定格式的数据包,而这个数据包被称作魔术包(MagicPacket),MagicPacket格式是由AMD公司开发推广的技术,受到网卡厂商的支持,现在的网卡基本都支持这个功能。
1.3 MagicPacket 数据包格式
6字节0xFF + 16次MAC地址
例如:Mac地址是:D8 CB 8A 32 4A 2B 3C,数据包如下
FF FF FF FF FF FF D8 CB 8A 32 4A 2B 3C (此处省略14次...) D8 CB 8A 32 4A 2B 3C
1.4 BIOS设置
开机进入BIOS
1.Power Management setup ->WakeUpOnLAN 设置为Enable
2.WakeUp By PCI Card 设置为Enable
F10 保存退出 重启之后再关机会发现关机之后网卡灯依然闪烁
1.5 如何发送过去
获取Mac地址
Win + r 运行程序
cmd 打开命令行
ipconfig -all 查看网卡信息
一般使用UDP协议发送(理论上任意协议都可以),IP设置为网段广播地址,192.168.1.255,(有的用255.255.255.255好像也可以),端口是9,然后发送MagicPacket,完成网络唤醒
2. 远程关机原理
2.1 简述
Windows支持远程关机命令,需要设置好远程关机权限。
例如:让IP为192.168.1.25的计算机10s以后执行关机命令
shutdown -s -t 10 -m \\192.168.1.25
2.2 设置计算机允许远程控制关机
修改远程PC的本地安全策略,为指定用户开放权限。
Windows 默认的安全策略,只有Administrators组的用户才有权限远程关闭计算机,如果要给其他用户远程关机的权限,可修改“本地安全策略” 实现
1. Win + r 运行程序
2. gpedit.msc 打开组策略管理
3. 计算机配置->Windows设置->安全设置->本地策略->用户权限分配
4. 从远程系统强制关机->添加用户或组 输入Guest->检查名称->确定
5. 拒绝次从网络访问这台计算机->选中Guest->删除
6. 打开控制面板->用户账户->管理其他账户->启用Guest(Win7需要)来宾账户
3. 代码实现
3.1 实现远程开关机类
TRemoteCtrlPC.cpp
/******
* WakeOnLAN
* 注意:需要设置广播地址,例如:192.168.1.255
******/
#include <QHostAddress>
#include <QUdpSocket>
#include <QProcess>
#include "TRemoteCtrlPC.h"
TRemoteCtrlPC::TRemoteCtrlPC(QObject *parent)
{
Q_UNUSED(parent)
}
/* Note:
* 被控计算机需要支持网络唤醒功能(Wake on LAN)
* 1.组织魔法包(MgicPacket) 由6字节0xFF和重复16次的MAC地址 102字节
* 2.通过UDP的方式广播 IP地址为路由的广播地址 如:网段为192.168.1.x,IP设为192.168.1.255
* @strMac: 格式示例 "20:af:0a:47:aa:bb"
*/
void TRemoteCtrlPC::wakeOnLAN(QString strMac)
{
/*将字符串MAC转换成十六进制 存入数组*/
char cstrMacAddr[6];
bool isOK;
for(int j = 0;j < 6; j++){
if(j < 5){
cstrMacAddr[j] = strMac.mid(strMac.indexOf(":",j*3) - 2,2).toInt(&isOK,16);
if(false == isOK)
return ;
}else if(j == 5){
cstrMacAddr[j] = strMac.mid(strMac.indexOf(":",j*3 - 1) + 1,2).toInt(&isOK,16);
if(false == isOK)
return ;
}
}
/*构建一个魔术唤醒包 6字节0xFF 和 16次的 Mac地址*/
QByteArray magicPacket;
/*6个 0xFF*/
for(int i = 0;i < 6; i++){
magicPacket[i] = 0xFF;
}
/*16个 MAC*/
for(int k = 0; k < 16; k++)
{
for(int l = 0; l < 6; l++)
magicPacket[(k+1)*6 + l] = cstrMacAddr[l];
}
QHostAddress FakeAddress;
FakeAddress.setAddress ("192.168.1.255");
QUdpSocket udpSocket;
udpSocket.writeDatagram(magicPacket, 102, FakeAddress, 9);
}
/* https://blog.csdn.net/smstong/article/details/16879347 */
/* https://www.cnblogs.com/shenjieblog/p/5455691.html
* Note:实现远程关机原理
* 1.用户管理:被控计算机启用来宾账户(Guest)
* 2.运行gpedit.msc 允许Guest强制关机 拒绝从网络访问计算机里删除 Guest
*/
void TRemoteCtrlPC::remoteShutDown(QString strIp)
{
m_strIp = strIp;
/*命令的执行过程会阻塞 放入线程执行*/
this->start();
}
void TRemoteCtrlPC::run()
{
QProcess cmd;
QString strResult;
QByteArray arrayOut;
QString strCmd = "shutdown -s -t 5 -m \\\\"; //四个斜杠 转义
strCmd += m_strIp;
qDebug()<<"关闭电脑:"<<strCmd;
cmd.start(strCmd);
cmd.waitForFinished(); // !!!阻塞操作 放在主线程会导致窗口卡死 直至执行完成
arrayOut = cmd.readAllStandardOutput();
strResult = QString::fromLocal8Bit(arrayOut);
emit signalCmdFinished(strResult); //命令执行完成 返回命令执行结果
}
TRemoteCtrlPC.h
#ifndef TREMOTECTRLPC_H
#define TREMOTECTRLPC_H
#include <QObject>
#include <QThread>
class TRemoteCtrlPC : public QThread
{
Q_OBJECT
public:
explicit TRemoteCtrlPC(QObject *parent = 0);
void wakeOnLAN(QString strMac);
void remoteShutDown(QString strIp);
protected:
void run();
signals:
void signalCmdFinished(QString result);
public slots:
private:
QString m_strIp;
};
#endif // TREMOTECTRLPC_H
3.1 测试类
TTestForRemoteCtrlPC.cpp
#include "TTestForRemoteCtrlPC.h"
TTestForRemoteCtrlPC::TTestForRemoteCtrlPC(QWidget *parent)
: QWidget(parent)
{
initUI();
}
void TTestForRemoteCtrlPC::initUI()
{
resize(200,200);
m_pRemoteCtrlPC = new TRemoteCtrlPC(this);
m_pComboxWake = new QComboBox(this);
m_pComboxWake->addItem("90:FB:A6:7E:E5:DF");
m_pComboxWake->setEditable(true);
m_pComboxWake->setFixedHeight(30);
QPushButton *buttonWake = new QPushButton("Wake",this);
buttonWake->setFixedHeight(30);
connect(buttonWake,SIGNAL(clicked()),this,SLOT(slotWake()));
m_pComboxShutdown = new QComboBox(this);
m_pComboxShutdown->addItem("10.1.81.69");
m_pComboxShutdown->setEditable(true);
m_pComboxShutdown->setFixedHeight(30);
QPushButton *buttonShutdown = new QPushButton("ShutDown",this);
buttonShutdown->setFixedHeight(30);
connect(buttonShutdown,SIGNAL(clicked()),this,SLOT(slotShutdown()));
QVBoxLayout *layout = new QVBoxLayout;
layout->addSpacing(20);
layout->addWidget(m_pComboxWake);
layout->addSpacing(20);
layout->addWidget(buttonWake);
layout->addSpacing(20);
layout->addWidget(m_pComboxShutdown);
layout->addSpacing(20);
layout->addWidget(buttonShutdown);
this->setLayout(layout);
}
void TTestForRemoteCtrlPC::slotWake()
{
qDebug("唤醒电脑 ");
m_pRemoteCtrlPC->wakeOnLAN(m_pComboxWake->currentText());
}
void TTestForRemoteCtrlPC::slotShutdown()
{
qDebug("关闭电脑 ");
m_pRemoteCtrlPC->remoteShutDown(m_pComboxShutdown->currentText());
}
TTestForRemoteCtrlPC.h
#ifndef TTESTFORREMOTECTRLPC_H
#define TTESTFORREMOTECTRLPC_H
#include <QWidget>
#include <QComboBox>
#include <QPushButton>
#include <QVBoxLayout>
#include "TRemoteCtrlPC.h"
class TTestForRemoteCtrlPC : public QWidget
{
Q_OBJECT
public:
explicit TTestForRemoteCtrlPC(QWidget *parent = 0);
signals:
public slots:
void slotWake();
void slotShutdown();
private:
void initUI(void);
QComboBox *m_pComboxWake;
QComboBox *m_pComboxShutdown;
TRemoteCtrlPC *m_pRemoteCtrlPC;
};
#endif // TTESTFORREMOTECTRLPC_H