用PHP+Python+C#实现手机语音远程控制电脑关机

背景

有时候出去办事情,电脑忘了关机;

或者差不多下班了,还差几分钟需要偷偷溜走,但是让别人感觉你还在上班的样子,哈哈,开玩笑啦。这时候你只要对着手机说:关机,远程电脑就可以实现关机了,是不是很酷。这个功能只是抛砖引玉作用,需要其他功能可以自行想象。

环境

1.微信公众号

2.php

3.python

4.c#

流程图

实现

PHP

WeiXinHelper.class.php

<?php
/**
 * Created by IntelliJ IDEA.
 * User: Administrator
 * Date: 2017/3/18
 * Time: 21:16
 * Desc: 微信帮助类
 */

class WeiXinHelper{
    /***
     * 配置文件
     * @var array
     */

    var $config = array();
    /***
     * 关注公众号
     */

    const EVENT_SUBSCRIBE = 'subscribe';
    /***
     * 取消关注公众号
     */

    const EVENT_UNSUBSCRIBE = 'unsubscribe';
    /***
     * 点击事件
     */

    const EVENT_CLICK = 'CLICK';
    /***
     * 跳转地址
     */

    const EVENT_VIEW = 'VIEW';
    /***
     * 扫码事件
     */

    const EVENT_SCAN = 'SCAN';
    /***
     * Wi-Fi连网成功事件
     */

    const EVENT_WIFI_CONNECTED = 'WifiConnected';
    /***
     * 接收文本消息
     */

    const MSGTYPE_TEXT = 'text';
    /***
     * 接收图片消息
     */

    const MSGTYPE_IMAGE = 'image';
    /***
     * 语音消息
     */

    const MSGTYPE_VOICE = 'voice';
    /***
     * 视频消息
     */

    const MSGTYPE_VIDEO = 'video';
    /***
     * 音乐消息
     */

    const  MSGTYPE_MUSIC = 'music';
    /***
     * 地理位置消息
     */

    const MSGTYPE_LOCATION = 'LOCATION';
    /***
     * 链接消息
     */

    const MSGTYPE_LINK = 'link';
    /***
     * 图文消息
     */

    const MSGTYPE_NEWS = 'news';
    /***
     * 事件消息类型
     */

    const MSGTYPE_EVENT = 'event';
    /***
     * @param array $config
     * $config = array(
        'app_id' => 111,
     *  'account' => 'xxxxx',//微信号
     *  'original_id' => 'xxxxx',//原始ID
     *  'token' => 'xxxxx',//Token
     * );
     */

    function __construct($config = array()){
        $this->config = array_merge($config, $this->config);
    }
    /***
     * 验证签名
     * @param bool $echo 是否首次验证接口,原样输出字符串
     * @return bool
     */

    public function checkSignature($echo = false){
        list($signature, $timestamp, $nonce, $echo_str) = array(
            isset($_GET['signature']) ? $_GET['signature'] : '',
            isset($_GET['timestamp']) ? $_GET['timestamp'] : '',
            isset($_GET['nonce']) ? $_GET['nonce'] : '',
            isset($_GET['echostr']) ? $_GET['echostr'] : '',
        );
        if($echo){
            exit($echo_str);
        }else{
            $tmpArr = array($this->config['token'], $timestamp, $nonce);
            sort($tmpArr, SORT_STRING);
            return $signature == sha1(implode('', $tmpArr));
        }
    }
    /**
     *@desc : 被动回复用户消息
     * @param:$toUserName string 接收方帐号(收到的OpenID)
     * @param:$msgType 消息类型 text | image | voice | video | music | news
     * @param:$data mix 根据不同$msgType 传送不同值
     * @param:$isExit bool 是否退出
     */

    public function passiveReplyUserMessage($toUserName, $msgType, $data, $isExit = true){
        //当前时间戳
        $unixTime = time();
        $specialContent = '';
        $fromUserName = $this->config['account'];

        switch($msgType){
            case 'text'://回复文本消息
                $specialContent = "<Content><![CDATA[{$data}]]></Content>";
                break;
            case 'image'://回复图片消息
                $specialContent = <<<EOT
                <Image>
                <MediaId><![CDATA[{$data}]]></MediaId>
                </Image>
EOT
;
                break;
            case 'voice'://回复语音消息
                $specialContent = <<<EOT
              <Voice>
              <MediaId><![CDATA[{$data}]]></MediaId>
              </Voice>
EOT
;
                break;
            case 'video'://回复视频消息
                $specialContent = <<<EOT
                <Video>
                <MediaId><![CDATA[{$data['mediaId']}]]></MediaId>
                <Title><![CDATA[{$data['title']}]]></Title>
                <Description><![CDATA[{$data['description']}]]></Description>
                </Video>
EOT
;
                break;
            case 'music'://回复音乐消息
                $specialContent = <<<EOT
                <Music>
                <Title><![CDATA[{$data['title']}]]></Title>
                <Description><![CDATA[{$data['description']}]]></Description>
                <MusicUrl><![CDATA[{$data['musicUrl']}]]></MusicUrl>
                <HQMusicUrl><![CDATA[{$data['hQMusicUrl']}]]></HQMusicUrl>
                <ThumbMediaId><![CDATA[{$data['mediaId']}]]></ThumbMediaId>
                </Music>
EOT
;
                break;
            case 'news'://回复图文消息
                $itemArr = array();
                $messageCount = count($data);

                foreach($data as $value){

                    array_push($itemArr ,<<<EOT
                <item>
                <Title><![CDATA[{$value['title']}]]></Title>
                <Description><![CDATA[{$value['description']}]]></Description>
                <PicUrl><![CDATA[{$value['picurl']}]]></PicUrl>
                <Url><![CDATA[{$value['url']}]]></Url>
                </item>
EOT

                    );
                }

                $itemList = implode('', $itemArr);

                $specialContent = <<<EOT
                <ArticleCount>{$messageCount}</ArticleCount>
                <Articles>
                {$itemList}
                </Articles>
EOT
;
                break;
        }

        //通用内容
        $totalContent = <<<EOT
            <xml>
            <ToUserName><![CDATA[{$toUserName}]]></ToUserName>
            <FromUserName><![CDATA[{$fromUserName}]]></FromUserName>
            <CreateTime>{$unixTime}</CreateTime>
            <MsgType><![CDATA[{$msgType}]]></MsgType>
            {$specialContent}
            </xml>
EOT
;
        echo $totalContent;
        if($isExit)exit();
    }
    /**
     *@desc: 接收消息
     */

    public function receiveMessage(){

        try{
            //获取未命名的POST数据 $GLOBALS['HTTP_RAW_POST_DATA']
            $postData = $GLOBALS['HTTP_RAW_POST_DATA'];

            if(empty($postData)){
                return null;
            }
            //LOAD XML数据
            $myXmlDocument = simplexml_load_string($postData);

            if(empty($myXmlDocument)){
                return null;
            }

            $dataArr = array();

            foreach($myXmlDocument->children() as $key=>$value){
                $dataArr[$key] =  $value .'';
            }

            return $dataArr;
        }catch (Exception $ex){
            return false;
        }
    }
}

WeiXinAction.php

<?php
/**
 * Created by IntelliJ IDEA.
 * User: Administrator
 * Date: 2017/3/18
 * Time: 21:03
 */

class WeiXinAction extends KgActionBase{
    private $myWenLu = null;
    /***
     * 构造函数
     */

    public function __construct(){
        $this->myWenLu = new WeiXinHelper(
            array(
                'account' => 'xxx',
                'original_id' => 'gh_xxx',
                'app_id' => 'xxx',
                'token' => 'xxx',
            ));
    }
    /***
     * 问路
     */

    public function doWenLu(){
        $message = $this->myWenLu->receiveMessage();
        $openId = (empty($message) || !isset($message['FromUserName'])) ? '' : $message['FromUserName'];

        if(!$this->myWenLu->checkSignature()){
            $error = '签名错误';
            if(empty($openId)){
                exit($error);
            }else{
                $this->myWenLu->passiveReplyUserMessage($openId, WeiXinHelper::MSGTYPE_TEXT, $error);
            }
        }
        if(empty($openId)){
            $this->myWenLu->passiveReplyUserMessage($openId, WeiXinHelper::MSGTYPE_TEXT, '非法请求');
        }
        //消息类型
        $type = strtolower($message['MsgType']);
        switch($type){
            case WeiXinHelper::MSGTYPE_TEXT:
                break;
            case WeiXinHelper::MSGTYPE_IMAGE:
                break;
            case WeiXinHelper::MSGTYPE_VIDEO:
                break;
            case WeiXinHelper::MSGTYPE_LOCATION:
                break;
            case WeiXinHelper::MSGTYPE_LINK:
                break;
            case WeiXinHelper::MSGTYPE_EVENT:
                break;
            case WeiXinHelper::MSGTYPE_VOICE:
                $this->voiceMsg($message);
                break;
            default:
                break;
        }
        $this->myWenLu->passiveReplyUserMessage($openId, WeiXinHelper::MSGTYPE_TEXT, '欢迎光临。');
    }
    /***
     * 语音消息
     */

    public function voiceMsg($message){
        $testOpenIds = array(
            'o4ygms2-liSrOt69_YZnShytcrXw',//alvin
        );

        $openId = $message['FromUserName'];
        $voiceText = isset($message['Recognition']) ? $message['Recognition'] : '';
        if(empty($voiceText)){
            $this->myWenLu->passiveReplyUserMessage($openId, WeiXinHelper::MSGTYPE_TEXT, '没有返回Recognition字段');
        }
        //非控制人员简单输出
        if(!in_array($openId, $testOpenIds)){
            $this->myWenLu->passiveReplyUserMessage($openId, WeiXinHelper::MSGTYPE_TEXT, $voiceText);
        }else{
            $this->socketCmd($voiceText, $openId);
        }
    }
    /***
     * 处理命令
     * @param $cmdStr
     */

    private function socketCmd($cmdStr, $openId){
        $port = 8080;
        $host = 'localhost';

        /************************************/
        $host = 'tcp://'. $host .':' . $port;
        $fp = stream_socket_client ($host, $errno, $error, 20);
        if (!$fp){
            $error = "$error ($errno)";
        }else{
            $error = '链接成功';
            fwrite ($fp, $cmdStr);
            /*while (!feof($fp)){
                $error =fgets($fp); #获取服务器返回的内容
            }*/

            fclose($fp);
        }
        /************************************/

        /*
        $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if(!empty($socket)){
            $result = socket_connect($socket, $host, $port);
            if(!empty($result)){
                $error = '链接成功.';
                $str = 'hello,world';
                socket_write($socket, $str, strlen($str));
                //读返回
                //socket_read()
            }else{
                $error = '链接失败.';
            }
            socket_close($socket);
        }else{
            $error = '创建失败.';
        }*/

        $this->myWenLu->passiveReplyUserMessage($openId, WeiXinHelper::MSGTYPE_TEXT, $error);
    }
}

 

Python(socket服务端)

socket_server.py

# coding:utf-8
'''
Socket 服务器端
    常见的协议及端口(这些端口是由操作系统管理的)
    ftp-Data:20,
    ftp-Control:21
    SSH:22,
    Telnet:23
    SMTP:25,
    HTTP:80
    POP3:110
    IMAP:143
    HTTPS:443
'
''
import socket, threading
import time, Queue
import re


def main():
    # Socket
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 创建tcp socket
    s.bind(('', 8080))  # 绑定到端口 //localhost
    s.listen(5)  # 监听,但只能挂起5以下链接

    # 创建队列
    queue = Queue.Queue()

    while True:
        client, addr = s.accept()  # 连接
        addr = str(addr)
        print("从 %s 获取一个连接" % addr)  # 直接输出到控制台
        timestr = time.ctime(time.time()) + "\r\n"  # 时间羽化输出
        strs = '现在是:' + timestr
        # client.send(strs)  # 发送输数据
        task = str(client.recv(1024))
        cs = '%s 客户端返回的数据为:%s' % (addr, task)  # 接收客户端数据

        if task == 'C#':
            if not queue.empty():
                client.send(queue.get())  # 发送输数据
        else:
            pattern = re.compile(r'关机')
            if pattern.match(task):
                queue.put('shutdown')

        print(cs)
        client.close()


if __name__ == '__main__':
    main()

然后执行以下命令,启动socket服务端:

python socket_server.py

 

C#(socket客户端+winform)

Form1.Designer.cs

namespace ReceiveRemoteCmd
{
    partial class Form1
    {
        /// <summary>
        /// 必需的设计器变量。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 清理所有正在使用的资源。
        /// </summary>
        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows 窗体设计器生成的代码

        /// <summary>
        /// 设计器支持所需的方法 - 不要
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            this.richTextBox1 = new System.Windows.Forms.RichTextBox();
            this.SuspendLayout();
            //
            // richTextBox1
            //
            this.richTextBox1.Location = new System.Drawing.Point(12, 12);
            this.richTextBox1.Name = "richTextBox1";
            this.richTextBox1.Size = new System.Drawing.Size(520, 328);
            this.richTextBox1.TabIndex = 0;
            this.richTextBox1.Text = "";
            //
            // Form1
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(544, 352);
            this.Controls.Add(this.richTextBox1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.Load += new System.EventHandler(this.Form1_Load);
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.RichTextBox richTextBox1;
    }
}

 

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Diagnostics;
using System.Threading;

namespace ReceiveRemoteCmd
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Thread MyThread = new Thread(new ThreadStart(startThread));
            MyThread.Start();
            //startThread();
        }
        /// <summary>
        /// 开始线程
        /// </summary>
        private void startThread()
        {
            do
            {
                addMessage(richTextBox1, "开始执行...");
                string ReceiveContent = string.Empty;
                Socket MySocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPEndPoint ie = new IPEndPoint(IPAddress.Parse("xxxx.xxxx.xxxx.xxxx"), 8080);
                try
                {
                    MySocket.Connect(ie);
                    MySocket.Send(Encoding.UTF8.GetBytes("C#"));
                    //Process.Start("shutdown.exe", "-s");
                    //接收数据
                    byte[] data = new byte[1024];
                    int recv = MySocket.Receive(data);
                    ReceiveContent = Encoding.UTF8.GetString(data, 0, recv);
                }
                catch (Exception ex)
                {
                    addMessage(richTextBox1, "执行报错:" + ex.Message);
                }
                finally
                {
                    try
                    {
                        MySocket.Shutdown(SocketShutdown.Both);
                        MySocket.Close();
                    }
                    catch { }
                }


                if (ReceiveContent == "shutdown")
                {
                    addMessage(richTextBox1, "执行关机...");
                    Process.Start("shutdown.exe", "-s");
                }
               
                addMessage(richTextBox1, "执行结束...");

                Thread.Sleep(5000);

            } while (true);
        }
        /// <summary>
        /// 消息委托
        /// </summary>
        /// <param name="myControl">控件</param>
        /// <param name="message">消息</param>
        private delegate void delegateMessage(Control myControl, string message);
        /// <summary>
        /// 添加消息
        /// </summary>
        /// <param name="myControl">控件</param>
        /// <param name="message">消息</param>
        private void addMessage(Control myControl, string message)
        {
            myControl.Invoke(new delegateMessage(showMessage), new object[] { myControl, message });
        }
        /// <summary>
        /// 显示消息
        /// </summary>
        /// <param name="myControl">控件</param>
        /// <param name="message">消息</param>
        private void showMessage(Control myControl, string message)
        {
            ((RichTextBox)myControl).AppendText(message + "\r\n" );
        }
    }
}

 

启动C#客户端,启动python socket服务端,然后在公众号内用语音说出:关机,如果出现以下界面,恭喜你,可以实现远程关机啦。

Leave a Comment