有趣的python

在家想远程公司电脑?Python + 微信一键连接!

2019-06-02  本文已影响9人  c067527d47c2
img

作者 | 阿文

责编 | 伍杏玲

有时候需要远程家里的台式机使用,因为我平时都是用 MAC 多,但是远程唤醒只能针对局域网,比较麻烦,于是我想用微信实现远程唤醒机器。


image

当然在学习Python的道路上肯定会困难,没有好的学习资料,怎么去学习呢? 学习Python中有不明白推荐加入交流群号:984137898 群里有志同道合的小伙伴,互帮互助, 群里有不错的视频学习教程和PDF!

img

准备工作

本程序主要是实现远程管理 Windows10操作系统的开机和关机:

  1. 在 Windows机器的相同内网中放一个 Linux 主机,我这里用树莓派代替,如果你是用 OpenWrt 之类的路由器也可以。
  2. Linux 主机需要能够远程访问,我这里是有 FRP 将树莓派的端口映射到我的公网 Linux 主机上。所以可以随时远程 SSH 过去。
  3. Windows 机器的网卡必须是有线连接,支持网络唤醒功能。
img

开机实现思路

首先通过微信发送开机指令,这里我使用的是 itchat 程序会调用 Paramiko 库去 SSH 远程到内网的树莓派执行 WakeOnLan 命令去唤醒 Windows 主机。

pi@raspberrypi:~ $ wakeonlan  -i 192.168.1.0 14:dd:a9:ea:0b:96
Sending magic packet to 192.168.1.0:9 with 14:dd:a9:ea:0b:96

程序会通过 ICMP 协议, ping 下需要唤醒的目标主机然后进行过滤,一个正常的 ICMP 包是64字节,过滤打印出这个64。

例如 ping 百度:

➜  ~ ping www.baidu.com
PING www.a.shifen.com (180.97.33.108): 56 data bytes
64 bytes from 180.97.33.108: icmp_seq=0 ttl=53 time=8.865 ms
64 bytes from 180.97.33.108: icmp_seq=1 ttl=53 time=9.206 ms
64 bytes from 180.97.33.108: icmp_seq=2 ttl=53 time=8.246 ms

用一段 Linux 命令去过滤是否有64,这里为啥要用 head -n 1 呢?

因为有可能会出现2行,经过测试,我们只需要取64这个值就可以了:

ping 192.168.1.182 -c 1 | grep 64 | cut -d " " -f 1|head -n 1

如果有则表示开机成功已经联网了,返回开机成功,否则程序继续往下走,去唤醒,然后在 ping 一次确认是否开机,如果为是则返回开机成功,否则返回失败。程序执行成功后,在我的网站根目录创建一个 shutdown 文件,用于后面的关机操作:

#!/usr/bin/python
# -*- coding: utf-8 -*-
import itchat
import paramiko
import os
import time
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

hostname = ''
username = ''
port = 
key_file = '/home/fangwenjun/.ssh/id_rsa'
filename = '/home/fangwenjun/.ssh/known_hosts'

@itchat.msg_register(itchat.content.TEXT)
def text_reply(msg):
    if msg['ToUserName'] != 'filehelper': return
    if msg['Text'] ==  u'开机':
        paramiko.util.log_to_file('ssh_key-login.log')
        privatekey = os.path.expanduser(key_file) 
        try:
            key = paramiko.RSAKey.from_private_key_file(privatekey)
        except paramiko.PasswordRequiredException:
            key = paramiko.RSAKey.from_private_key_file(privatekey,key_file_pwd)

        ssh = paramiko.SSHClient()
        ssh.load_system_host_keys(filename=filename)
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(hostname=hostname,username=username,pkey=key,port=port)
        #执行唤醒命令
        stdin,stdout,stderr=ssh.exec_command('ping 192.168.1.182 -c 1 | grep 64 | cut -d " " -f 1|head -n 1')
        sshCheckOpen = stdout.read()
        sshCheckOpen =sshCheckOpen.strip('
')
        print type(sshCheckOpen)
        print sshCheckOpen
        #进行判断,如果为64,则说明 ping 成功,说明设备已经在开机状态,程序结束,否则执行唤醒
        if sshCheckOpen == '64':
            connect_ok_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 
            itchat.send(connect_ok_time+u'设备已经开机', toUserName='filehelper')
        else:
            ssh_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 
            itchat.send(ssh_time+u'开始连接远程主机', toUserName='filehelper')
            stdin,stdout,stderr=ssh.exec_command('wakeonlan -i 192.168.1.0 14:dd:a9:ea:0b:96')
            wakeonlan_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 
            itchat.send(wakeonlan_time+u'执行唤醒,等待设备开机联网', toUserName='filehelper')
            #由于开机需要一些时间去启动网络,所以这里等等60s    
            time.sleep(60)
            #执行 ping 命令,-c 1 表示只 ping 一下,然后过滤有没有64,如果有则获取64传给sshConStatus
            stdin,stdout,stderr=ssh.exec_command('ping 192.168.1.182 -c 1 | grep 64 | cut -d " " -f 1|head -n 1')
            sshConStatus = stdout.read()
            sshConStatus =sshConStatus.strip('
')
            print type(sshConStatus)
            print sshConStatus
            #进行判断,如果为64,则说明 ping 成功,设备已经联网,可以进行远程连接了,否则发送失败消息
            if sshConStatus == '64':
                connect_ok_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 
                itchat.send(connect_ok_time+u'设备唤醒成功,您可以远程连接了', toUserName='filehelper')
            else:
                connect_err_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
                itchat.send(connect_err_time+u'设备唤醒失败,请检查设备是否连接电源', toUserName='filehelper')
            ssh.close()
            #在网站根目录创建一个空文件,命名为 shutdown
            os.system('touch /www/shutdown')
            print '执行开机消息成功'
img img

关机部分实现

当接收关机指令时,程序会去删除网站根目录的 shutdown 文件,客户端我写了几行代码,去通过 Requests 库每隔30s 发送 HTTP head 请求去判断文件是否是404,如果是404 这说明文件不存在,调用系统关机操作,执行关机。

然后 SSH 到树莓派去 ping 目标主机,如果返回为空,则说明关机成功,否则关机失败。这只是针对 Windows 的关机,如果目标主机是 Linux 则简单多了:

if msg['Text'] ==  u'关机':
        #删除网站根目录的shutdown 文件
        rmfile = os.system('rm -rf /www/shutdown')
        if rmfile == 0:
            print '执行关机消息成功'
        shutdown_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 
        itchat.send(shutdown_time+u'正在关机....', toUserName='filehelper')
        paramiko.util.log_to_file('ssh_key-login.log')
        privatekey = os.path.expanduser(key_file) 
        try:
            key = paramiko.RSAKey.from_private_key_file(privatekey)
        except paramiko.PasswordRequiredException:
            key = paramiko.RSAKey.from_private_key_file(privatekey,key_file_pwd)

        ssh = paramiko.SSHClient()
        ssh.load_system_host_keys(filename=filename)
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(hostname=hostname,username=username,pkey=key,port=port)
        itchat.send(shutdown_time+u'正在确认设备是否完成关机操作,大约需要等待60s.', toUserName='filehelper')
        #等等60秒后确认,因为关机需要一段时间,如果设置太短,可能网络还没断开
        time.sleep(60)
        stdin,stdout,stderr=ssh.exec_command('ping 192.168.1.182 -c 1 | grep 64 | cut -d " " -f 1|head -n 1')
        sshConStatus = stdout.read()
        sshConStatus =sshConStatus.strip('
')
        print type(sshConStatus)
        print sshConStatus
        #如果获取的值为空,则说明已经关机,否则关机失败
        if sshConStatus != '64':
            shutdown_success_err_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
            itchat.send(shutdown_success_err_time+u'关机成功', toUserName='filehelper')
        else:
            shutdown_err_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 
            itchat.send(shutdown_err_time+u'关机失败,请连接桌面检查客户端程序是否正常执行', toUserName='filehelper')
        ssh.close()
itchat.auto_login(hotReload=True,enableCmdQR=2)
itchat.run()
img

客户端代码,写完扔计划任务,开机启动:

import requests
import os
import time
while 1:
    time.sleep(30)
    r = requests.head("https://awen.me/shutdown")
    print r.status_code
    if r.status_code == 404:
        os.system("shutdown -s -t 5")

使用 TeamViewer 连接:

img img

缺点

  1. 网页端微信必须一直登录,不方便,这个就需要微信不能断网了。
  2. WakeOnLan 是广播 MAC 地址的,貌似不能返回是否成功没,所以还是要 ping 主机看看通不通,判断下。
  3. 需要一个树莓派做跳板机,否则也不能唤醒内网设备。
  4. 如果只允许自己控制最好是使用文件助手来发送消息,因为默认情况下,任何人都可以给你发送指令开机。
  5. Windows需要安装TeamViewer并且设置为开机自动启动以及绑定账号设置无人值守模式。这样方便远程,如果是Linux 则不需要开启 ssh 就可以了。
上一篇下一篇

猜你喜欢

热点阅读