在家想遠端公司電腦?Python +微信一鍵連線

AI科技大本營發表於2019-02-24

640?wx_fmt=jpeg


作者 | 阿文

轉載自 CSDN(ID:CSDNnews)


有時候需要遠端家裡的桌上型電腦使用,因為我平時都是用 MAC 多,但是遠端喚醒只能針對區域網,比較麻煩,於是我想用微信實現遠端喚醒機器。


準備工作


本程式主要是實現遠端管理 Windows10作業系統的開機和關機:

  1. 在 Windows機器的相同內網中放一個 Linux 主機,我這裡用樹莓派代替,如果你是用 OpenWrt 之類的路由器也可以。

  2. Linux 主機需要能夠遠端訪問,我這裡是有 FRP 將樹莓派的埠對映到我的公網 Linux 主機上。所以可以隨時遠端 SSH 過去。

  3. Windows 機器的網路卡必須是有線連線,支援網路喚醒功能。


開機實現思路


首先通過微信傳送開機指令,這裡我使用的是 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 '執行開機訊息成功'


640?wx_fmt=jpeg


關機部分實現


當接收關機指令時,程式會去刪除網站根目錄的 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()


640?wx_fmt=jpeg


客戶端程式碼,寫完扔計劃任務,開機啟動:


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 連線:

640?wx_fmt=png


缺點


  1. 網頁端微信必須一直登入,不方便,這個就需要微信不能斷網了。

  2. WakeOnLan 是廣播 MAC 地址的,貌似不能返回是否成功沒,所以還是要 ping 主機看看通不通,判斷下。

  3. 需要一個樹莓派做跳板機,否則也不能喚醒內網裝置。

  4. 如果只允許自己控制最好是使用檔案助手來傳送訊息,因為預設情況下,任何人都可以給你傳送指令開機。

  5. Windows需要安裝TeamViewer並且設定為開機自動啟動以及繫結賬號設定無人值守模式。這樣方便遠端,如果是Linux 則不需要開啟 ssh 就可以了。

程式碼地址:https://github.com/monkey-wenjun/wchatwakeonlan

文章內的程式碼如果有 Bug,後續更新都在 GitHub 上,完整程式碼請參考 GitHub ,此文章程式碼不再更新。

原文:https://awen.me/post/3709919605.html


(本文為AI科技大本營轉載文章,轉載請微信聯絡原作者)


群招募


掃碼新增小助手微信,回覆:公司+研究方向(學校+研究方向),邀你加入技術交流群。技術群稽核較嚴,敬請諒解。

640?wx_fmt=jpeg


推薦閱讀:

                         640?wx_fmt=png

點選“閱讀原文”,檢視歷史精彩文章。

相關文章