醉月思 发布的文章


title: 如何从实验室唤醒在宿舍的关机状态下的电脑
tags:

  • 黑魔法

id: 9
categories:

  • 技术

thumbnail: 'https://blog.cdn.thinkmoon.cn/1.PNG'
abbrlink: dc85

date: 2017-10-23 13:29:43

如果你要常去实验室,机房。而又嫌带着笔记本麻烦。亦或者是台式电脑,不方便携带。你可能会想到远程桌面是吧?那么如果你的电脑处于关机状态呢?那么我想你可能会喜欢这篇文章。

如何从实验室唤醒在宿舍的关机状态下的电脑

本文适合有一定基础的同学阅读,如果你感觉看不懂或者有些名词不理解,可以先收藏起来,待到日后理解之时再来阅读。

首先我们来了解一下WOL

Wake-on-LAN (WoL) 网络唤醒(WoL)是以太网或令牌环 计算机网络标准,允许计算机通过网络消息打开或唤醒。

消息通常通过在连接到相同局域网(诸如智能电话)的设备上执行的程序发送到目标计算机。也可以通过使用子网定向广播或WOL网关服务来从另一个网络发起消息。等效术语包括在网络唤醒,远程唤醒,电源通过LAN,通过LAN上电,通过LAN恢复,恢复局域网和唤醒局域网。如果正在唤醒的电脑正在通过Wi-Fi进行通信,则称为补充标准必须使用无线LAN唤醒(WoWLAN)。

通俗点,就是利用这门技术,你再局域网内,可以通过一台设备来唤醒一台处于关机状态的设备。

硬件支持

在计算机的主板和网络接口(固件)上实现了局域网唤醒支持,因此不依赖于在硬件上运行的操作系统。某些操作系统可以通过NIC驱动程序来控制LAN唤醒行为。对于较旧的主板,如果网络接口是插卡而不是集成到主板上,则可能需要通过附加电缆将该卡连接到主板。支持LAN唤醒的嵌入式以太网控制器的主板不需要电缆。电源必须符合ATX 2.01规范。

额,说人话就是现在的大部分设备都是支持网络唤醒的,而且与你使用的是什么操作系统无关,不过与你的驱动程序却有关,这点很难解释。

工作原理

以太网连接,包括家庭和工作网络,无线数据网络和互联网本身,都是基于计算机之间发送的帧。网络唤醒(“WOL”)使用专门设计的称为魔术包的框架实现,该框架发送到网络中的所有计算机,其中包括要唤醒的计算机。魔术包包含目标计算机的MAC地址,每个网络接口卡中内置的识别号码(“NIC”)或计算机中的其他以太网设备,使其能够在网络上被唯一地识别和寻址。关闭或关闭能够启用LAN唤醒功能的计算机将包含能够在系统关闭电源时以低功耗模式“收听”传入数据包的网络设备。如果接收到指向设备的MAC地址的魔术包,则NIC会通知计算机的电源或主板以启动系统唤醒,其方式与按下电源按钮的方式相同。

魔术包在数据链路层(OSI模型中的第2层)上发送,并且在发送时,使用网络广播地址广播给给定网络上的所有连接的设备; IP地址(OSI模型中的第3层)不被使用。

这是一个常见的误解,因为Wake-on-LAN建立在广播技术上,它只能在当前的网络子网内使用。虽然这是一般情况,但有一些例外,网络唤醒可以在实际中运行在任何网络上,适当的配置和硬件,包括跨Internet的远程唤醒。

为了使局域网唤醒工作,网络接口的部分需要保持打开状态。这消耗了少量的待机功率,远低于正常工作电源。链路速度通常降低到尽可能低的速度,以免浪费电力(例如,千兆以太网网卡只保留10 Mbit / s链路)。在不需要时禁用LAN唤醒功能,可以在关闭但仍插入电源插座的计算机上轻松降低功耗。

说人话就是,你在打开了主机的远程唤醒功能后,对主机按下电源键关机的时候,他会让网卡设备仍处于供电状态,这会比不开网络唤醒要耗电些,实际上当你关机后再断电,如果你没有再开机,你的设备就不能被网络唤醒。而它唤醒的目标是根据mac地址来区分的,当这台设备收到唤醒数据包时,网卡设备会通知电源,主板开始工作,然后就等同与开机了。

筛选重点

  • 硬件:主板,电源,网卡设备。
  • 软件:网卡驱动
  • 刚需:供电,接入网络(并不需要具备有网的状态)。
  • 唤醒:唤醒数据包
好啦,我们筛选出重点之后,接下来就知道怎么入手啦,那就让我们开始吧!

1.对于主板

主板的操作是,我们需要设置主板能够被网卡设备所唤醒,这样才能正常地进入操作系统。要设置主板,那当然是要bios啦。
  • 进入BIOS设置,打开电脑主板的网络唤醒功能,一般情况是WAKE,LAN之类的,然后设置其值为enable;(需主板支持,现在大部分的主板都是支持的)

2.对于网卡和电源

  • 打开系统设置,设备管理器>网络适配器>找到你的网卡设备,双击>进入电源管理,
![设备管理器](https://blog.cdn.thinkmoon.cn/1.PNG "设备管理器")
设备管理器
  • 取消勾选<允许计算机关闭此设备以节约电源>的作用就是,让你的电脑关机之后网卡设备仍处于供电状态。
  • 勾选<允许此设备唤醒计算机>的目的就是,让你的网卡收到数据包后可以激活电源供电。
![电源管理](https://blog.cdn.thinkmoon.cn/2.PNG "电源管理")
电源管理

3.对于供电和接入网络

这个。。。。。插好电源,插好网线即可

4.对于网络唤醒数据包

其实网络唤醒用啥方式并不重要,因为实际上都是对特定mac设备发送网络唤醒包
  1. 下载一个网络唤醒的软件,
  2. 使用路由器发网络唤醒包(我就是用这个)

5.对于网卡驱动

win7,win8都是直接默认网卡驱动就可以了的,但是win10却是需要自己下载一个驱动安装的。
  • 对于win7,win8,网上好多人说要关闭快速启动,但是在我看来,这两点貌似毫无干系啊。不过我没试过win7,也不好发表意见,既然他们都说要关闭快速启动,那就说一下如何关闭快速启动吧。

控制面板>硬件和声音>电源选项>选择电源按钮功能

![enter description here](https://blog.cdn.thinkmoon.cn/1508405841217.jpg "选择电源按钮")
选择电源按钮

更改当前不可用的设置>取消勾选快速启动

![enter description here](https://blog.cdn.thinkmoon.cn/1508406031142.jpg "取消勾选快速启动")
取消勾选快速启动
  • 对于win10,我的台式机就是win10的,但是搞这个网络唤醒折腾了好久。不过好在发现了这个帖子。原文链接https://www.reddit.com/r/Windows10/comments/3f73sz/psaif_windows_10_killed_your_wol_functionality_or/

    原文是英文的,如果不想看英文,我大致翻译一下。楼主说,他遇到win10无法网络唤醒的问题,然后他研究了一下驱动和网卡底层方面的逻辑,然后得出来,是驱动的问题,然后他把驱动用技巧装回了win7的,成功唤醒。不过大部分人采用的是一楼的回复解决方案。

    原文截图:

![enter description here](https://blog.cdn.thinkmoon.cn/1508406748550.jpg "解决方案")
解决方案

他说的是realtek官方已经更新了驱动,如果你在这个链接,下载win10的网卡驱动,就可以网络唤醒了http://www.realtek.com.tw/Downloads/downloadsView.aspx?Langid=1&PNid=13&PFid=5&Level=5&Conn=4&DownTypeID=3&GetDown=false#1

end:我的唤醒流程

被唤醒主机:宿舍win10 台式

唤醒设备:海蜘蛛路由器

操作主机:实验室win7 台式

1.在实验室,先连上宿舍我的路由器局域网下的路由器(额,我是用路由器搭了一个vpn服务器的,其实可以跳过这步)

2.登入我的路由器管理员网站,然后打开网络工具>网络唤醒

![enter description here](https://blog.cdn.thinkmoon.cn/1508409324490.jpg "网络唤醒")
网络唤醒

3.唤醒之后,刷新列表就会发现,多了一台设备在连接局域网了,然后使用mstsc远程桌面,就可以控制宿舍的电脑了。有没有感觉高大上?

没有截图
路由器,和vpn都是非必需的,你也可以直接在局域网内用客户端唤醒的,还有就是如果你有域名,然后能成功访问也是可以广域网唤醒的。

一说到搭建个人博客,大家一定是第一时间想到WordPress了吧?
其实使用WordPress非常的方便,实用性也非常高,如果不知如何搭建wordpress博客,
可以去隔壁Benny的学习日记(公众号)看教程。但是如果你只是想要一个简洁的博客文章页,那么你可以试着用一下gitblog

什么是markdown(md)文件

markdown是一种轻量级的标记语言,它允许人们使用易读易写的纯文本格式编写文档。唔,通俗点就跟HTML一样。约定一些特定的符号来决定它的显示形式。它相对于HTML的优势就是,简单,使作者将关注重点放在内容上。相信大家也经历过调CSS的绝望吧?

它的语法极其简单,语法内容:
我的简书
像我这篇文章就是md文件复制的。

了解gitblog 网址http://www.gitblog.cn

gitblog官网

github地址https://github.com/jockchou/gitblog

Gitblog是一个简单易用的Markdown博客系统,它不需要数据库,没有管理后台功能,更新博客只需要添加你写好的Markdown文件即可。 它摆脱了在线编辑器排版困难,无法实时预览的缺点,一切都交给Markdown来完成,一篇博客就是一个Markdown文件。 同时也支持评论,代码高亮,数学公式,页面PV统计等常用功能。 Gitblog提供了不同的主题样式,你可以根据自己的喜好配置,如果你想自己制作博客主题,也是非常容易的。 Gitblog还支持整站静态导出,你完全可以导出整站静态网页部署到Github Pages。

gitblog与WordPress的区别

  1. 数据库,gitblog只有文件,没有数据库。也就是说它不需数据库,这样做的优点是简洁,适合与那些网站内容修改频率不高博客。系统更轻量级,移植性高,还可以到全站导出为静态文件。然后把它们部署到类似于githubpage平台。
  2. 框架,gitblog是使用codeigniterhttp://codeigniter.org.cn

框架(一个小型的轻量级php框架)。WordPress是完全使用自己的设计思想,不依赖于主流框架,如果你了解框架的思想,如果你想好好学习,和修改开发博客源码,你可以选择使用前者。当然对自己足够自信,你可以去研究WordPress源码。

  1. 完善性,如果你只是想搭个稳定完善的个人博客,那么不建议你使用gitblog。如果你自己的diy,探索精神比较强,或者你是个简洁控,你可以试试gitblog。

如何配置安装gitblog

  1. 去github下载源码
  2. 安装Apache+php环境
  3. 解压到网站根目录
  4. 修改配置文件conf.yaml,文件说明[https://github.com/jockchou/gitblogdoc/blob/master/posts/gitblog/config.md]

(https://github.com/jockchou/gitblogdoc/blob/master/posts/gitblog/config.md)

url: / #域名地址
title: thinkmoon #站点名称
    subtitle: #副标题
    theme: quest #主题名
    enableCache: false #是否开启缓存
    highlight: true #是否开启代码高亮
    mathjax: false #数学公式支持
    katex: false #?
    youyan: #有言ID
    baiduAnalytics: #百度统计ID
    keywords: thinkmoon #网站关键字
    description: &gt;
        thinkmoon #网站描述
    version: 2.2
    author:
        name: 醉月思 #作者
        email: tim@thinkmoon.cn #邮箱
        github: thinkmoon #github地址
        weibo: your-weibo #微博地址
        avatar:
    blog:
        recentSize: 5
        pageSize: 6
        pageBarSize: 5
        allBlogsForPage: false
    text:
        title: 介绍
        intro: &gt;
            本博客使用Gitblog搭建,
            wordpress博客请访问https://thinkmoonmagic.wordpress.com
你可能需要修改的配置参数:
 url: 修改成你的域名,http://yourdomain.com,注意最后没有杠。
 title: 修改成你的博客标题
 subtitle: 修改成你的副标题
 duoshuo: GitBlog采用多说评论框,你需要申请多说账号,并在这里填写你的多说ID
 baiduAnalytics: GitBlog采用百度统计,你需要申请百度统计账号,在这里填写你的统计Key
 author:修改为你个人的信息即可
 如果你不需要评论和统计功能,删除duoshuo和baiduAnalytics这两荐即可。其他信息,可根据浏览博客页面的效果进行修改调整。

5.访问,OK了

编写一篇博客

在gitblog里面,一篇文章就是markdown文件。所有的文章都在/blog目录下,你把你写好的md文件放到这个文件夹就可以了

博客文章的属性,包括作者,题目,标签,分类目录等,由md文件里面的注释决定。注释规范

例如use_gitblog_setup_blog.md

gitblog博客文章编

上传到blog文件夹之后,主页界面刷新效果

gitblog效果展示

文章页效果

gitblog文章页效果展示

这儿需注意,gitblog默认是开启网站缓存的,如果你的网站更新不够频繁,你可以不用改配置,在配置conf.yaml里面修改。

同步wp博客到gitblog

  1. 首先从wordpress后台中导出xml数据
  2. 重命名为wordpress.xml
  3. 使用PHP 网站根目录/index.php wp2gb 命令同步

    例如: php /var/www/html/index.php wp2gb

    运行成功会提示finished!运行后的主页俯视图

wordpress迁移到gitblog

完成gitblog的搭建

好啦,大功告成啦,一个gitblog博客网站就已经搭建成功了www.thinkmoon.cn

原项目代码类图

各类文件解析(按难度排序)

  1. Point (坐标类,使用经度,维度用来表示一个点)

Point.h

  1. #  
  2. pragma once class Point {  
  3.     public: Point(double longitude, double latitdue);  
  4.     bool operator == (Point & point);  
  5.     privatedouble longitude; //经度  
  6.     double latitude; //纬度  
  7. };  
    1. Road(道路类,使用两个点来表示一条道路)

    Road.h

  8. <li>
    #  
    <li>
    pragma once# include "stdafx.h"#  
    <li>
    include "Point.h"#  
    <li>
    include "Vehicle.h"#  
    <li>
    include "TrafficLight.h"#  
    <li>
    include "BitMatrix.h"#  
    <li>
    define R 6378 //地球的平均半径  
    <li>
    # define PI 3.14# define LANNE_AMOUNT 3 //每条道路的车道数量   
    <li>
    # define MIN_VEHICLE 10 //初始状态每条道路的最少车辆  
    <li>
    # define VEHICLE_SIZE 70 class Vehicle;  
    <li>
    class TrafficLight;  
    <li>
    class Road {  
    <li>
        public: Road(double longitude1, double latitude1, double longitude2, double latitude2): point1(longitude1, latitude1), point2(longitude2, latitude2) {  
    <li>
            length = R * 2 * asin(sqrt(pow(sin((latitude1 / 180 * PI - latitude2 / 180 * PI) / 2), 2) + cos(latitude1 / 180 * PI) * cos(latitude2 / 180 * PI) * pow(sin((longitude1 / 180 * PI - longitude2 / 180 * PI) / 2), 2))); //计算道路长度  
    <li>
        }  
    <li>
        bool operator == (Road & road); //用于判断两条道路是否相连  
    <li>
        double countVehicleDensity(void); //计算行车密度, 行车密度 = 车辆数 / (道路长度(km) * 车道数)  
    <li>
        double countCrowdExtent(void); //计算道路拥挤度  
    <li>
        double returnLength(void); //返回道路长度  
    <li>
        bool enterVehicle(void); //当前道路是否能进车  
    <li>
        void updateVehicle(vector < Road > & road); //更新当前道路的车辆  
    <li>
        void creatVehicle(int roadSize, int totalRoadSize, BitMatrix & roadBitMatrix, vector < Road > & road); //为该道路生成车辆  
    <li>
        void enterVehicle(Vehicle & vehicle); //进入车辆  
    <li>
        void updateTrafficLight(void); //更新当前道路的交通灯  
    <li>
        /*  计算拥挤度的公式:   crowdExtent = 0.15 * exp(-0.0322 * vehicleDensity) - 0.15 (vehicleDensity <= 45.5)   crowdExtent = 0.111 * vehicleDensity - 0.0152 (vehicleDensity > 45.5)    */  
    <li>
        vector < int > adjoin; //存储相连的道路编号   是否改成private?  
    <li>
        private: Point point1, point2; //道路的两个端点的经纬度  
    <li>
        double length, vehicleDensity, crowdExtent; //length为道路的长度,vehicleDensity为行车密度,crowdExtent为道路拥挤度  
    <li>
        list < Vehicle > vehicle; //当前道路的车辆表  
    <li>
        TrafficLight trafficLight; //交通灯用于控制车辆进入该道路  
    <li>
        int vehicleSize; //当前道路车辆数  
    <li>
    };  
    分析与思考:存储相连采用向量方式?这样真的好吗?一条条道路判断相等再连接?


说到交通网路的模拟化表示,那就不得不用到数据结构中的图。想必这应该是最方便形象的表示方法了把。

图的概念

图是由顶点集合及顶点之间关系的集合组成的一种数据结构,Graph = (V,E)。
其中顶点集合V = { x | x ∈ 某个数据对象集}是个有穷非空集合。E = { <x, y> | x , y ∈ V && Path( x , y )} ,即边集。

我所知的图的存储结构

邻接矩阵表示

邻接矩阵的表示,首先将所有的顶点信息组成一个表。然后利用一个矩阵来表示各顶点之间的相邻关系,称之为邻接矩阵。

邻接表表示

在第i行的单链表中,各节点(或称边节点)分别存放与同一个顶点Vi关联的各条边。各个节点配有其标识(及对应的顶点)和权值(若为有权图)以及指向另一个边节点的指针。

*邻接多重表表示

邻接多重表的表示,主要一处理图的边为主(为什么会有这个需求?在什么情况会用到?),要求每条边处理一次的实际应用中特别有用(比如?)。它的主要思想是把多重表结构引入到图的邻接表中,就有点像把边作为研究的基本单位,用一个多重表节点来表示一条边。

*十字链表表示

此为百度词条:十字链表(Orthogonal List)是有向图的另一种链式存储结构。该结构可以看成是将有向图的邻接表(和逆邻接表结合起来得到的。用十字链表来存储有向图,可以达到高效的存取效果。同时,代码的可读性也会得到提升。

我该选什么存储结构

首先,交通道路网络是双向的,所以我们可以将其视为无向图; 其次在一座城市的交通网络下,道路E 与路口 n的关系是 E << n^2,而且道路是会出现两点之间多条路的情况(即多重图)所以我舍弃第一种方法; 后面两种表示方式其实我也是一知半解,我有种感觉,如果在交通道路的分层模型下,可能第三种方式要更具优势,但是目前还想不了那么远。所以我暂时选用第二种方式,用邻接表表示。

我的实现代码

(代码年久失修,已失去完整内容,仅供参考)

Graph_lnk.h // V1.0.1

pragma once
# include 
using namespace std; 
int DefaultMaxVertices = 500; //最大顶点数
    auto memory_error = [](char * function, string aim) {    
    cerr << function << "申请" << aim.c_str() << "内存分配错误" << endl;
    exit(1);
}; //内存申请错误的提示lamba表达式

struct Edge {  
int dest; //标记关系节点
double weight;//权值
Edge * link;//指向边的指针
Edge(int num, double weight): dest(num), weight(weight), link(nullptr) {} 
};

struct Vertex {
string data; //道路口的信息,暂时用string
Edge * adj; //指向边的指针
Vertex(string data = "点"): data(data), adj(nullptr) {} 
};  

class Graph_lnk {  
friend ostream & operator << (ostream & in, Graph_lnk & G); //运算符重载,图的输出

public: 
Graph_lnk(int sv = DefaultMaxVertices);
~Graph_lnk();  
int NumberOfVertices() {  return numVertices; } //返回当前顶点数
int NumberOfEdges() {  return numEdges; } //返回当前边数
Vertex getVertex(return NodeTable[v]; } //返回该节点  
                                
string getValue(int v) {return NodeTable - > data;} //返回道路信息  
                                
bool insertEdge(int v1, int v2, double weight); //插入一条边  
                                
bool insertVertex(string data); //插入一个路口  
                                
protected: int numVertices; //当前顶点数  
                                
int numEdges; //当前边数  
                                
private: Vertex * NodeTable;  
                                
};  
                            
Graph_lnk::Graph_lnk(int sv) {  
                                
    numVertices = sv;  
                        
    numEdges = 0;  
                        
    NodeTable = new Vertex[DefaultMaxVertices];  
                                
    if (NodeTable == nullptr) {  
                                
        memory_error(__func__, "NodeTable");  
                                
    }  
                        
    for (int i = 0; i };  
                        
bool Graph_lnk::insertEdge(int v1, int v2, double weight) {  
                                                
if (v1 >= 0 && v1 = 0 && v2 
                                
                        Edge * q = nullptr, * p = nullptr;  
                        
                        if (NodeTable[v1].adj != nullptr) {  
                                
                            p = NodeTable[v1].adj;  
                        
                            q = p - > link;  
                        
                            while (q != nullptr) {  
                                
                                p = q;  
                        
                                q = p - > link;  
                        
                            }  
                        
                            q = new Edge(v2, weight);  
                                
                            p - > link = q;  
                        
                        } else {  
                                
                            NodeTable[v1].adj = new Edge(v2, weight);  
                                
                        }  
                        
                        if (NodeTable[v2].adj != nullptr) {  
                                
                            p = NodeTable[v2].adj;  
                        
                            q = p - > link;  
                        
                            while (q != nullptr) {  
                                
                                p = q;  
                        
                                q = p - > link;  
                        
                            }  
                        
                            q = new Edge(v1, weight);  
                                
                            p - > link = q;  
                        
                        } else {  
                                
                            NodeTable[v2].adj = new Edge(v1, weight);  
                                
                        }  
                        
                        numEdges++;  
                        
                    }  
                        
                    return 0;  
                                
                }  
                        
                bool Graph_lnk::insertVertex(string data) {  
                        
                    if (numVertices == DefaultMaxVertices) return false;  
                                                
                    else {  
                                
                        NodeTable[numVertices].data = data;  
                        
                        NodeTable[numVertices].adj = nullptr;  
                        
                        numVertices++;  
                        
                    }  
                        
                    return true;  
                                        
                }  
                        
                    
 Graph_lnk::~Graph_lnk() {  
                        
    delete[] NodeTable;  
                                
};

分析与理由

在交通道路网络图的构建中,一定需要的两个函数insertEdge();和insertVertex(); 我使用两个主要的数据结构,Edge(表示边),Vertex(表示点)。用它们的集合来表示整个图,这样做可以有效的利用空间?(但是还是申请了VerTex(500))
 

缺陷与不足

不管你构建含多少个点的图,都需要申请固定的空间,只有当点小于而且越接近于500时空间利用率才最高。
插入边时,需要在两个点做增加,但是好像对于实际情况这样做并没有好处?
。。。。
 

问题与思考

作为交通网络图,是否还需要拓展一些别的功能?
在储存的过程中,是否用bit矩阵来存储数据?
能不能在插入的过程中只新增一个点上的边?
或者直接以边为基本研究单位,来构建图类?

心得与感悟

本来以为写一个图类,会是一件比较容易的事,没想到却也花了这么久,是考虑的太多?还是基础不牢?
刚开始想用模板类来表示,这样在后期数据类型拓展时比较方便,没想到却是发现一堆错误,还解决不了,最后要重新来过。
基础还是要牢固才可以,现在写的东西自己都感觉境界不够,没有别人那种精妙绝伦的感觉。
平常有时间多沉下心来学习,切记好高骛远,绕了一圈最后发现自己什么都不行...

C++11引入了auto和decltype关键字实现类型推导,通过这两个关键字不仅能方便地获得复杂的类型,还能简化书写,提高编码效率。下面说一下C++中的auto

旧标准

auto其实并不是一个新的关键字,在旧的标准C++98/03中,它代表着“具有自动存储周期的局部变量”。啥意思呢?就是我们平常所说的变量,他与static相对。就是说所有非static类型的都是“具有自动存储期的”。也就是说在旧的标准下。

autoint i =3;//等价于int i=3;

新标准

在C++11中,auto作为一个新的类型指示符(如int,double)来指示编译器的,但是auto申明的变量的类型必须由编译器在编译时期推导出来,也称类型推导。这种类型推导不是C++所独有的,还有很多具备这种能力的语言(如Python,Javascript)。我们先来看一段Python代码

name ="thinkmoon"print"hello,"+ name 

在这里的name是不需要定义类型的,因为这个类型很容易被推导为字符串性,如过要想在C++中实现这种效果,我们可以这样。

#include<iostream>
int main(){
    auto name ="thinkmoon"; 
    std::cout <<"hello,"<< name << std::endl;
    return0;
}

效果是一样的,是不是觉得写起来特别的方便呢?

但是需要注意的是,在C++中这种静态类型推导是发生在编译期间的。而像Python这种动态类型推导却是发生在运行期间的。

auto的基本用法

#include<iostream>
usingnamespace std;
int main(){
auto x =5; 
cout << x << endl;//x被推导为intauto p =newauto(1);    
cout <<"*"<< p <<"="<<*p << endl;//p被推导为
int *constauto*v =&x, u =6; 
cout <<"*"<< v <<"="<<*v <<"\n u="<< u << endl;//v被推导为const int *,u被推导const int
}

对于最后一个类型推导有几个需要注意

  1. v被推导为const int *而这里auto代替int,但是u等于6还是要写的,否则编译器会报错。
  2. u的等号后面只能写整型的变量,否则会报错,因为不能让编译器产生具有二义性的推断。
其实我们学习的时候可以把auto理解为占位符,它只是占着一个位置并不做其它的事情,由编译器将其类型推导出来再用对应的类型去运行,所以这个时候auto的类型推导是不能让编译器产生二义性的。

auto的推导规则

int x =0;auto* a =&x;//auto推导为int,
auto b =&x;//auto推导为int *,即使不申明为指针也能推断为指针
auto& c = x;//auto推导为int,等价于int 
auto d = c;//auto推导为int,auto会抛弃右值的引用类型
const auto e = x;//e是const int类型,
auto f = e;//f是int型constauto&g = x;//g是const 
int & auto & h = g;  //h是const int & 
总结:
  1. 当不申明为引用或者指针时,auto的推导规则会抛弃对应右值的cv限定符(cv-qualifier,const,volatile限定符的总称)。
  2. 当申明为引用或者指针时,auto推导规则会保留右值的cv属性。

auto的限制

  1. auto s
错误原因:s没有明确的类型,auto无法推断。
  1. void fun(auto a=1){….}
错误原因:auto类型推导不能用作函数参数。
  1. auto不能用于非静态成员函数
struct Foo
{
    auto var_1=0;//错误,auto不能用于非静态成员函数
    static const auto var_2=0;//OK,var_2为static const int
}

4. auto无法定义数组

int main()
{
    int arr[10]={0};
    auto aa    = arr; //OK,aa为int *
    auto bb[10]=arr;//error,auto无法定义数组
}

5. auto无法推导出模板参数

Bar<int> bar;
Bar<auto> bb = bar;//error,auto无法推导出模板参数

auto的优势

既然auto的功能特性这么方便,那么它的优势在哪?或者说,我们什么时候使用它能达到神效呢?

  1. 遍历vector
这是一个简单的遍历。
vector<int> vs;for(auto i = vs.begin(); i < vs.end(); i++){......}
其实还可以更简单
for(auto var : vs){.......}
  1. 待补充。。。。。。。嘿嘿!

一听说QQ机器人是不是感觉有点高大上?感觉离自己有点远?不过事实就是如果你想创建,或者说拥有一个QQ机器人确实一件非常容易的事情。

创建方法

1.腾讯QQ自带的QQ小冰

2.利用第三方框架在自己电脑或者windows云主机上挂一个机器人(mypcqq,酷Q等)

这个方法的好处就是可拓展性强,可二次开发,所以我在以后主要介绍这种方法

框架一(mypcQQ)

从功能上看这个框架功能略显强大(拥有撤回检测,气泡设置,卡片消息),但是缺点也很明显那就是不稳定,页面不太友好。

框架二(酷Q机器人)

因为mypcQQ我认为实在是太不稳定了,所以我在以后的QQ机器人二次开发中着重介绍酷Q机器人的二次开发。

酷Q机器人C++SDK解析

酷Q机器人工作原理

  1. 先是QQ服务器端发送数据给酷Q客户端,酷Q根据数据判断是否触发了事件,以及触发了哪类事件,不同类事件都有一个默认的type。
  2. 然后酷Q会根据这个type在JSON中查找所有具有该type的event项目,并将这些项目按照priority从小到大的顺序排列,然后按照顺序依次向你的DLL发送指令。
  3. 指令的内容就是每个event项目所规定的function值,以及该事件的type所决定的必要参数。在你的DLL内部,会先查找指令中指定的function,找到[函数名]正确的函数,然后按[参数列表]规定的顺序代入参数,执行你写的代码。
  4. [参数列表]只规定了参数的数目、顺序和类型,而参数变量名称酷Q是不管的,也就是说只要数目顺序类型正确,参数变量的名称你可以自己修改。比如fromQQ你可以把它改成TheQNumberItFrom,或者abc,都可以,只要你自己方便就行。
  5. 最后,如何命令酷Q执行你需要的动作呢?你需要调用头文件cqp.h中声明的函数。这些函数只进行了声明,其定义在酷Q加载后会在其它DLL中定义,所以VS可能会提示这些函数未定义,但不影响编译生成,请忽视这些提示。

SDK视图

SDK文件解析

appmain.h

插件APPID的定义,内容不多,在编译前,将这里面的CQAPPID也定义为你之前的文件名AppID。规则见 http://d.cqp.me/Pro/开发/基础信息

cqp.h

cqp.h分为常量定义和函数声明两部分。常量定义部分看不懂也没关系,关键是后面的函数声明部分。函数用途按英语意思去用

每一条声明都具有如下格式:

CQAPI([返回值类型]) 函数名;
[函数名]描述了该函数的功能,如果你看不懂英文(你还是放弃吧),JSON中的auth部分的注释有每一个函数英文名的翻译。
CQ_get开头的函数用于向QQ服务器获取一些状态信息(比如获取某个群员的信息),其它函数用于向服务器发送操作指令(比如发送消息)。get类函数返回你想要获取的信息,其它函数返回操作结果。不同的返回值对应的操作结果,在cqp.h的常量定义部分有说明。
你必须按照[参数列表]的规定调用这些函数。

appmain.cpp

这个cpp是最重要的部分,我们平常编写的功能都是在这个cpp里面,根据对应的规则调用,官方SDK里面有详细的注释。

JSON文件

在修改之前,你要明确JSON的作用:这个文件是你的DLL和酷Q之间的交流协议,它规定了酷Q该如何使用你写的DLL。

  • json规则:http://d.cqp.me/Pro/开发
  • event部分:// 事件列表,同一事件类型可重复定义(发布前请删除无用事件)
  • id:这就是个索引,只要保证你所有事件的id不重复即可
  • type:规定酷Q应该在何时触发这个事件。每种事件对应的type值是固定的,只要你设置的type值正确,酷Q就会在正确的时机触发你的事件。事件的名称无关紧要。
  • name:这个就是显示给用户看的,和你的程序没有关系。
  • function:自由定义,但不能重复,而且有格式要求:

1、由英文字母、数字、下划线构成
2、第一个字符不能是数字
3、大小写敏感,大写和小写是不同的
4、不能和这些关键字重名:https://msdn.microsoft.com/zh-cn/library/2e6a4at9.aspx

如果你需要删除一个事件,需要把一对{}内的内容以及之后的逗号完全删除,否则会报错。请注意不要删除[],而且[]内的最后一对{}后不能跟逗号,其它的必须跟逗号。
如果你需要增加新的事件,建议复制粘贴再修改,以免疏忽。

参考链接:https://cqp.cc/t/27173

这篇主要让大家对酷Q有个基本的了解,下一篇做个小例子。

2019.12.6补充

不打算搞小例子了

废话不说,进入正题,为了简便请调整项目属性为使用多字节字符集

tinyxml文件下载地址,(操作xml文件的)

http://sourceforge.net/projects/tinyxml/

一些函数功能

ValueStr     //返回元素名称
SetValue     //设置元素名称
Parent     //返回父节点对象

FirstChild    //返回第一个子节点
LastChild     //返回最后一个子节点
IterateChildren   //返回下一个子节点

InsertEndChild   //在最后一个子节点后插入子节点
InsertBeforeChild   //在指定的子节点前插入子节点
InsertAfterChild   //在指定的子节点后插入子节点
ReplaceChild    //替换指定的子节点
RemoveChild    //删除指定的子节点
Clear     //删除所有的子节点

PreviousSibling   //返回同级中前一个节点
NextSibling    //返回同级中后一个节点

NextSiblingElement   //返回同级中后一个元素
FirstChildElement   //返回第一个子元素节点
Attribute     //返回元素中的属性值
QueryValueAttribute //返回元素中的属性值
SetAttribute    //设置元素中的属性值
FirstAttribute   //返回元素中第一个属性对象
LastAttribute    //返回元素中最后一个属性对象
RemoveAttribute   //删除元素中指定的属性对象

tinyxml的添加

首先,将下载的文件解压复制到原项目目录,
然后,在解决方案中头文件添加现有项,添加进去。

接下来,在stdafx.h中包含两个头文件。

mfc中xml文件的使用

  • 第一步:建立对话框,添加CString变量(与编辑框关联起来).
  • 第二步,(信息录入)添加按钮处理消息(双击按钮即可),利用UpdataeData()更新数据,将数据录入xml文件。
TiXmlDocument *pDoc = new TiXmlDocument();//文件指针
    const char *FileName = "res\\Number.xml";//文件名
    if(!pDoc->LoadFile(FileName))
    {
        UpdateData(TRUE);
        TiXmlElement *RootElement = NULL;//根节点
        TiXmlElement *PersonElement = NULL;//子节点
 
        TiXmlDeclaration *pTd = new TiXmlDeclaration("1.0", "gb2312", "yes" );
        pDoc->LinkEndChild(pTd);
 
        RootElement = new TiXmlElement("账号数据");
        pDoc->LinkEndChild(RootElement);
         }
         else{
                RootElement = pDoc->RootElement();
              }
 
        PersonElement = new TiXmlElement("用户");
        RootElement->LinkEndChild(PersonElement);
 
        TiXmlElement *NameElement = new TiXmlElement("账号");
        PersonElement->LinkEndChild(NameElement);
        TiXmlText *Number = new TiXmlText([要存的数据]);//存的数据可为CString类型
        NameElement->LinkEndChild(Number);
 
        TiXmlElement *PassWordElement = new TiXmlElement("密码");
        PersonElement->LinkEndChild(PassWordElement);
        TiXmlText *PassWord = new TiXmlText([要存的数据]);//存的数据可为CString类型
        PassWordElement->LinkEndChild(PassWord);
                
                //你有几项就弄几段上面的代码,本例为两项数据
                pDoc->SaveFile(FileName);
  • 第三步,(信息显示)先添加list control 控件,选择report(报表)属性,同时添加变量m_ctllist1选择control类型(默认)。

然后在OnInitDialog(void)函数中添加如下代码

DWORD dwStyle = m_ctllist1.GetExtendedStyle();                    //添加列表框的网格线!!!
 
    dwStyle |= LVS_EX_FULLROWSELECT;            
    dwStyle |= LVS_EX_GRIDLINES;                
    m_ctllist1.SetExtendedStyle(dwStyle);
    //list control 的初始化
    m_ctllist1.InsertColumn(0,"车牌",LVCFMT_CENTER,80); 
    m_ctllist1.InsertColumn(1,"进入时间",LVCFMT_CENTER,120);
    m_ctllist1.InsertColumn(2,"停车时间",LVCFMT_CENTER,120); 
    m_ctllist1.InsertColumn(3,"应缴费用",LVCFMT_CENTER,100);
    m_ctllist1.InsertColumn(4,"备注",LVCFMT_CENTER,220);

数值为长度,如未发现此函数,请在类向导中虚函数添加该函数。运行效果如下

然后添加刷新按钮事件处理程序

TiXmlDocument * myDocument = new TiXmlDocument(); 
    if (!myDocument->LoadFile("res\\Number.xml"))
    {
        return ;
    }
    TiXmlElement *RootElement = myDocument->RootElement(); 
 
    TiXmlElement *FirstElement = RootElement->FirstChildElement();
 
    //循环遍历
    for (int i=0;FirstElement;i++)
    {
        TiXmlElement *NameElement = FirstElement->FirstChildElement();
        m_ctllist1.InsertItem(i,NameElement->GetText());//设置列表框第0行第0列数据
 
        //显示进车时间
        TiXmlElement *DateElement = NameElement->NextSiblingElement();
        const char *temp = DateElement-><pre name="code" class="html">GetText()
;m_ctllist1.SetItemText(i,1,temp);//设置列表框第0行第1列数据FirstElement = FirstElement->NextSiblingElement();}
  • 第四步,(信息查找及修改)添加查找对话框,编辑框添加变量,本例为m_Number 。查找代码如下
const char *FileNmae = "res\\Number.xml";
    TiXmlDocument *pDoc = new TiXmlDocument();
    if (pDoc->LoadFile(FileNmae))
    {
        TiXmlElement *RootElement = pDoc->RootElement();
        TiXmlElement *SonElement = RootElement->FirstChildElement();
        TiXmlElement *NumberElement = NULL;
        TiXmlElement *passWordElement = NULL;
        while (SonElement)
        {
            NumberElement = SonElement->FirstChildElement();
            if (NumberElement->GetText() == m_Number)//查找成功判断之后
            {
 
            }
            SonElement=SonElement->NextSiblingElement();
        }
信息查找修改代码如下,本例为查找用户名为m_Number修改密码为m_PassWord.两个变量都为编辑框CString类型
const char *FileNmae = "res\\Number.xml";
    TiXmlDocument *pDoc = new TiXmlDocument();
    if (pDoc->LoadFile(FileNmae))
    {
        TiXmlElement *RootElement = pDoc->RootElement();
        TiXmlElement *SonElement = RootElement->FirstChildElement();
        TiXmlElement *NumberElement = NULL;
        TiXmlElement *passWordElement = NULL;
        while (SonElement)
        {
            NumberElement = SonElement->FirstChildElement();
            if (NumberElement->GetText() == m_Number)
            {
                if (IDYES == MessageBox("该账号已存在,继续操作将覆盖","是否继续?",MB_YESNO|MB_ICONWARNING))
                {
                    passWordElement = NumberElement->NextSiblingElement();
                    TiXmlNode *PassWord = passWordElement->FirstChild();
                    PassWord->SetValue(m_PassWord);
                    pDoc->SaveFile(FileNmae);
                    MessageBox("密码修改成功,下次登录生效");
                }
                    goto end;
            }
            SonElement=SonElement->NextSiblingElement();
        }
           }
  • 第五步,信息删除,本例为查找账号名m_Number对该用户进行删除
const char *FileName = "res\\Number.xml";
     TiXmlDocument *pDoc = new TiXmlDocument();
     if (!pDoc->LoadFile(FileName))
     {
         MessageBox("Number.xml文件损坏","加载失败",MB_OK | MB_ICONWARNING);
         return ;
     }
     else
     {
         TiXmlElement *RootElement = pDoc->RootElement();
         TiXmlElement *PersonElement = RootElement->FirstChildElement();
         TiXmlElement *NumberElement = PersonElement->FirstChildElement();
         TiXmlElement *PassWordElement = NumberElement->NextSiblingElement();
         while (PersonElement)
         {
             NumberElement = PersonElement->FirstChildElement();
             if (NumberElement->GetText() ==  m_Number)
             {
                 RootElement->RemoveChild(PersonElement);
                 MessageBox("删除成功");
                 break;
             }
             else
             {
                  PersonElement = PersonElement->NextSiblingElement();//节点后移
             }
            
         }
         pDoc->SaveFile(FileName);
          }

结语

至于统计什么的,就是查找与显示的变异。