<![CDATA[心灵有约-使用手册]]>https://ariser.cn/index.php/archives/78/

应用简介

心灵有约-心理咨询在线预约系统,是用于湖南科技大学心理健康教育中心进行心理咨询的在线受理平台。本应用依托于湖南科技大学企业号,面向于所有在校师生。

预约流程

提交预约 - 等待受理 - 受理通过 - 现场咨询

使用简介

进入系统

打开湖南科技大学企业号,点击心灵有约即可进入。
系统会自动识别身份,并与教务系统对接,在本应用内生成您的个人信息和联系方式,因此请确认信息并修改以便我们能联系到您。
2193971889.png

在线预约

根据自己的情况合理选择咨询师(预约咨询的时间默认为下周次),并在备注栏简要说明问题,点击提交申请
4231888369.png

我的预约

提交预约后可以在我的预约界面查看预约申请。对正在受理状态的预约可以进行取消预约
等待后台受理之后,会在企业号为您推送一条消息,点击详情,按照提示信息前往心理健康教育中心进行心理咨询。
2844283580.png

个人中心

用于修改个人联系方式,默认您的联系方式来自教务系统,若有偏差请及时修改,确认是有效的联系方式,以便我们能及时联系到您!
1964597894.png

在线咨询

在线咨询仅回复一般心理问题,需进行深入心理咨询,建议进行在线预约。
4290845518.png
在线咨询会唤起QQ,请出现提示后选择

使用指南和关于我们

介绍在线预约流程和常见问题,以及湖南科技大学心理健康教育中心的简介,欢迎扫码关注湖科大心理在线
136442089.png

常见问题

  • 问题1:我遇到了较为紧急的心理障碍需要排解。答:请直接致电湖南科技大学心理教育健康中心:0731-58291687。
  • 问题2: 申请的预约是在线进行心理咨询吗?答:不是,本应用是用于"现场咨询"的在线预约。
  • 问题3:如果我临时有事情,可以取消预约吗?答:在"正在受理"的状态下可以进行"取消预约"。
  • 问题4: 我提交的预约信息会被其他人看到吗?答:不会,您提交的预约只有心理健康教育中心的咨询师可以看到,隐私信息也由系统全程进行加密保护。
  • 问题5: 我提交了预约,可以再申请以提高成功率吗?答:不行,咨询师资源有限,“正在受理”和“受理通过”情况下不可再提交新的预约。
  • 问题6: 我转专业了,学院信息不一致答:如有学院信息不一致情况,请在"在线咨询"回复:张三-更改学院-XXX学院。
  • 问题7: 我提交的申请为什么会被退回呢?答:提交的申请不规范或咨询师临时有事情,可以重新提交申请。
  • 问题8: 为什么我把这个分享给同学不能直接使用呢?答:本应用唯一入口是"湖南科技大学企业号 ➔ 心灵有约"。

问题反馈

使用过程如果有任何问题语言不规范,请发送邮件到i@ariser.cn1136206244联系,董。

]]>
默认分类,Web开发,PHP,JavaScript,Linux,数据结构,Web安全,微信开发
<![CDATA[Webp优化多图网站加载]]>https://ariser.cn/index.php/archives/44/

事由:近期把博客从WordPress转到了Typecho,使用了material主题。该主题文章显示可以选择随机图片的样式,样式如下:

屏幕快照 2019-04-13 下午6.04.53.png

将自己准备好的图片命名为material-x.png(x为数字),替换Themes/material/im/random下的图片。如果图片数目很多,请将控制台-外观-设置外观中的随机缩略图数量设置为你文件下图片的数目。

这时候问题来了

  1. 图片数量过多,重命名过于麻烦。
  2. 准备的图片文件过大网页加载奇慢无比,压缩(降低分辨率)后又会变模糊。

网页完全加载完用了30秒,且大部分时间是在等待png格式的图片加载。

QQ20190413-182755.png

于是又想到了webp格式,766k的jpg格式图片转化成webp只有377k,大大节省了加载时间,且清晰度下降可以忽略不计。且支持动图。
QQ20190413-182516.png

写了Python脚本来实现批量图片格式转换以及自动命名为material-x.webp样式。

  • 创建jpgwebp文件夹,将自己选好的图片放入到jpg,运行脚本即可自动转换。(其他格式自己该脚本里面的代码)
import PIL.Image
import os
i=1
path = "jpg/"
savepath = "webp/"
filelist = os.listdir(path)
for file in filelist:
    im = PIL.Image.open(path+filelist[i])
    filename = 'material-'+str(i)
    print(filename)
    im.save(savepath+filename+'.webp')
    i=i+1

另外还需将主题默认png格式更改为webp,编辑usr/themes/Meterial/functions.php210240行中的png改为webp

更改之后,图片质量无损的情况下,加载速度大为提升。

主题地址:Material

]]>
PythonWeb开发,Python
<![CDATA[从防护角度浅谈服务器安全和网站安全]]>https://ariser.cn/index.php/archives/24/

弱密码、常用密码、防止社工

  1. 任何时候、任何场景,请不要使用弱口令。
  2. 社工库是真真实实存在的,当你QQ、微博、服务器、邮箱、各种站点的会员,长期使用的过程中,不管密码有多复杂,有可能已经被泄露,而被存入黑客所谓的社工库(亲身经历,一个有社工库的朋友拿我的邮箱直接查出了我的最常用的一个密码)。这个泄露即使在用户安全上网的情况下也会存在,所注册的站点一旦被脱裤子,这些密码就直接泄露出去(优酷、B站等大互联网公司都有被脱库的事件)。
  3. 检测是否存在泄露的密码:传送门
  4. 还有屡见不鲜的钓鱼网站,伪造输入QQ账号密码登录界面,群邮件是重灾区(考研群发送"来参加复试"的邮件蠕虫式传播);在手机QQ或电脑QQ登录的情况下,凡事官方登录页面都会有一键点击登录或者扫码登录。这样被盗号的,总结一个字"傻!"。

服务器安全

  • 环境:CentOS 7
  • 工具:XShell(Windows)FinalShell(macOS)

我们知道,SSH远程登录端口默认为22,任何人都可以通过对应端口尝试登录你的服务器,针对服务器主机的攻击,一般都是机器扫描批量扫描爆破的
(宝塔面板提示)
1

因此,解决措施有如下:

  • 关闭root账户远程登录(CentOS)

    1. 创建新的账户,设置ssh登录:传送门
    2. sudo vim /etc/ssh/sshd_config
    3. /PermitRootLogin->Enter,查找
    4. 去掉#PermitRootLogin yes注释,或改为PermitRootLogin no
    5. 重启ssh服务/etc/rc.d/init.d/sshd restart
  • 禁用Ping、不用时关闭SSH登录
    宝塔面板设置:
    2
  • 更改默认SSH端口号(暂时缓解,大部分端口扫描工具可以直接扫出来)
  • 安装悬镜、云锁、安全狗等安全软件(只安装一个)
  • 使用复杂密码
  • 开启防火墙,只放行要用到的端口

网站安全

  1. 隐藏后台管理入口
  2. 除了update/cache等少数目录,其它所有目录都给只读权限
  3. 在nginx/apache站点配置中限制除入口目录及资源目录以外的所有目录的访问权限
  4. 若网站程序支持,尽量使用php5.4以上的版本
  5. 如非必要,不要给站点创建FTP,使用完就删除或停用FTP帐户
  6. 如非必要,不要对外开放3306端口,并隐藏好phpmyadmin位置,最好设个访问密码
  7. 开启SSL(HTTPS)
  8. 保护好网站源码及数据库备份,请不要将数据库备份及网站源码包等敏感数据放在站点根目录
  9. 使用Nginx时,可在宝塔面板中开启WAF防火墙,可有效防止绝大多数web攻击

代码托管平台提交项目前删除配置信息

  1. GitHub上提交开源项目,一定注意要将运维WIKI和数据库配置信息等敏感数据删除掉再上传。
  2. 如果已经上传,删除后重新提交Push是没用的!!!,因为每次Push都会有文件更改记录:

因此建议直接删除仓库里的项目重新提交。

]]>
默认分类,Web开发,Linux,Web安全
<![CDATA[Hackintosh_Envy13_10.13.6-10.14.5]]>https://ariser.cn/index.php/archives/4/

Hackintosh_Envy13_10.13.6-10.14.5

前言:社区共同开发成果,希望用于个人DIY和技术交流,不得用于商业用途,淘宝贩子还请绕道!

仓库为缺省及非最新版本,删除了部分核心文件,并不能直接食用,防止TB店家直接盗用。需要完整版请加页面最下方的交流群免费获取、一起交流、参与贡献!

目前适配到10.14.5,如果因升级系统造成的模块出问题,请重建缓存后重启(使用Kext Utility)。

Github
码云

进群切勿当伸手党,请先自己逛 黑果小兵远景论坛 ,学习基本安装流程之后自己动手,在群里讨论关键性问题。

完美适用于Hp Envy13 2017 ad1xxx

LZ的Envy13 2017配置为i5 8250u + uhd620 + 8G + 256G + Bcm94352z(网卡),惠普其它型号,可以进群寻找EFI、讨论及共同开发完美版本(群号在最下面)。

亮度快捷键

  1. 外接一个键盘
  2. 偏好设置 - 键盘 - 快捷键 - 显示器 - 用机械键盘设置亮度升降 F2 和 F3
  3. 笔记本调节亮度(Fn 配合 F2 F3)

开启HIDPI(系统原生缩放和显示更细腻)

$ sh -c "$(curl -fsSL https://raw.githubusercontent.com/xzhih/one-key-hidpi/master/hidpi.sh)"

优秀Mac资源网站:

软件推荐

  1. 功能增强工具,自己看图看名称
    image
  2. 神器:鼠标悬浮预览 + 拖拽全屏/窗口
    image
  3. Memory Cleaner:定时清理运行内存

    • General: 1.开机自启 2.隐藏窗口;其余全部关闭
    • Advanced: 勾选Auto Clean,设置6秒清理
    • 关闭 Notifications 里所有选项
  4. Markdown编辑器:MWeb
  5. Others:
    image

以上软件在资源网站里面都能找到。

声卡

声卡ID用的3,可以驱动下面两个喇叭,声音大一些,四个喇叭同时驱动还在寻找方法
有了Hotpatch的话,改config里面的ID是没用的,要改 /Volumes/EFI/EFI/CLOVER/ACPI/patched 下的 SSDT-Config.aml`
SSDT-HDEF.aml`对应位置的ID,可用的还有13,28
image

目前还存在的问题

  1. Windows非正常关机,再引导到MacOS后会长时间读条,强制关机再开机即可
  2. 不同BIOS版本出的问题不一样,有的睡眠后不出声音,有的蓝牙和Wi-Fi失效

交流群

]]>
HackintoshHackintosh
<![CDATA[改良NoCSRF实现对PHP后端接口的安全验证]]>https://ariser.cn/index.php/archives/3/

改良NoCSRF实现对PHP后端接口的安全验证

自己造的轮子,用于对前后端分离中后端接口的安全加固,如果有缺陷,还请指出,共同讨论改良!

改良和改造NoCSRF,实现对PhalAPI接口框架等前后端分离架构接口的安全加密认证。

不想看分析思路的可以直接跳到“实现过程”及上传的源码,参照进行部署。

目录:

  • NoCSRF的介绍
  • 配置到框架(以单次请求为示例)
  • 多次请求的处理
  • 解决方案
  • 实现过程
  • 结语

NoCSRF

国外大神开发的一个包,用于防范Web页面中的CSRF攻击。

代码一共有120行,思路很清晰,有兴趣可以进行拜读NoCSRF.php。

思路类似于常见的接口签名的实现:

  1. 请求头IP进行SHA1后,与20位随机码及时间戳连接,最后进行Base64处理。
  2. 每次请求接口前,生成上述$token存储到Session
  3. 携带$token请求接口。
  4. 后台验证时候逐步进行:

    • Session$token存在性检查
    • $_POST数组中$token存在性检查
    • 请求来源检查(请求头IP进行SHA1,与$token中的值进行对比)
    • 验证Session$_POST中的$token是否相同
    • 验证该$token是否过期(比对时间戳)
  5. 验证通过后,执行接口操作,否则抛出异常。
  6. 销毁$token

只要$token生成并存储的位置选择合理(每次页面加载前,PHP网页头部),基本不存在伪造的可能。因为$token生成时就放入了Session数组当中,存储在服务器硬盘Redis等缓冲区中,同时$token作为表单请求,后台将二者进行多重验证。

后面会介绍到,这个包只适用于一个页面对后端只有一次接口调用,多次请求需要进行改良。

配置到框架(以单次请求为示例)

配置前参见官网简单请求的示例:(PHP) NoCSRF

  1. 在框架命名空间中注册:
// nocsrf.php放入到/src/App/Common/
<?php
    namespace App\Common;
    ///。。。
    class NOCSRF{
    
    }
?>
  1. 页面头部生成Token:
<?php
    require_once("../vendor/autoload.php");//自动加载类
    use App\Common\NoCSRF;
    
    session_start();
    $token = NoCSRF::generate('csrf_token');
?>
  1. 表单携带Token:
<form name="csrf_form" action="#" method="post">
   <input type="hidden" name="csrf_token" value="<?php echo $token; ?>">
...Other form inputs...
    <input type="submit" value="Send form">
</form>

如果是Ajax,直接放入到变量当中,记得加上引号: var token = '<?php echo $token; ?>';

  • 后端验证:(对于每一个需要验证的接口,在构造函数内执行,./src/App/Api/xxx.php)
<?php

namespace App\Api;
use PhalApi\Api;

use App\Common\NoCSRF;
use PhalApi\Exception;

class Login extends Api{
    public function __construct(){
        session_start();

        try {
            // Run CSRF check, on POST data, in exception mode, with a validity of 10 minutes, in one-time mode.
            NoCSRF::check( $MainKey, $_POST, true, 60*10, false );
            // form parsing, DB inserts, etc.
        }
        catch ( Exception $e ) {
            exit('Need token!');
            // CSRF attack detected
        }
    } 

    public function getRules(){
    //.....
    }
    //...Other functions...
}
?>

以上内容针对:页面加载一次只请求后台一个接口的情形,比如登录。


多次请求的处理

对于一个页面同时请求多个接口,上述显然不适合。因为页面每次加载只会生成一个$token,而这个$token用于验证后,就会被后台销毁掉,同时请求的其他接口就会失效,而抛出Need Toekn!

思路1:(不可行)

Ajax请求接口的时候再<?php echo NoCSRF::generate('csrf_token');?>

比如下方例子,理论上可行,有请求就生成token,但实际上只有最后一次生成的token有效。因为PHP网页也算作脚本,页面每次刷新,页面内所有的PHP代码都会自动执行,所以前方的token在后方的token生成后被销毁。

function f1(){
    $.ajax({
            url: "xxxxx",
            type: "POST",
            data: {
                    'csrf_token': '<?php echo NoCSRF::generate('csrf_token');?>'
            },
            success: function(res, status, xhr) {
                    console.log(res);
            },
    })
}

function f2(){
    $.ajax({
            url: "xxxxx",
            type: "POST",
            data: {
                    'csrf_token': '<?php echo NoCSRF::generate('csrf_token');?>'
            },        
            success: function(res, status, xhr) {
                    console.log(res);
            },
    })
}
思路2:(可行但漏洞显而易见)

生成token单独作为接口发布,每次需要就先请求再获取。

于是有了以下方案:

  • 生成token的接口./src/App/Api/Token.php
<?php

namespace App\Api;

use PhalApi\Api;
use App\Common\NoCSRF;
use PhalApi\Exception;

class Token extends Api{
    public function getRules(){
        return array(
            'index' => array(),
        );
    }

    public function index(){
        session_start();
        return NoCSRF::generate('csrf_token');
    }
}
  • Ajax调取接口,封装成函数
function getCSRF(){
    let csrf_token = "";
    $.ajax({
            type: "GET",
            cache: false,
            async: false,
            url: "/xxx/xxx/?s=Token/Index",//Tokenj接口的URL
            success: function(res) {
                    csrf_token = res.data;
            }, error: function(XMLHttpRequest, textStatus, errorThrown) {
                    console.log(XMLHttpRequest.status);
                    console.log(XMLHttpRequest.readyState);
                    console.log(textStatus);
                    console.log(errorThrown);
                    csrf_token = "";
            }
    });
    return csrf_token;
}
  • 每次需要就执行:
function refresh(){
    $.ajax({
            url: "xxxxx",
            type: "POST",
            data: {
                    'csrf_token': getCSRF(),
                    'otherData' : 'xxx',
            },        
            success: function(res, status, xhr) {
                    console.log(res);
            },
    })
}

后面发现,直接用Postman请求/xxx/xxx/?s=Token/Index,获取到token,再携带这个token请求其他接口,依然能访问成功。

思考发现,这时的token仍然是在服务器端生成,无状态的HTTP请求直接拿过去,再反回来请求,依然是可行,只起到了验证时效性验证的功能,token与客户端没有唯一性联系,这种方案脱离了NoCSRF包本身的设计思路。


解决方案:

每次生成token的过程'NoCSRF::generate('csrf_token');',其中的'csrf_token'是自定义的,那么不妨把这个key利用起来,使之成为唯一且动态变化的值。

普通请求

在每个页面首部生成Token1,作为后面接口生成的tokentoken_key,请求是下面的样子:

改良后的请求

由于Token1是在页面首部(自身脚本,相当于与客户端绑定)生成的,不存在被伪造的可能 (原因见文章第一部分的介绍) ,故身份具有唯一性,拥有token的网页才可以访问接口。


实现过程:

注册接口: 每次页面加载会生成Token1,并请求此接口验证身份,当作token_key
token接口: 请求此接口会得到token_key: token2(上图)样式的Token用于业务接口的验证。
常规接口: 业务接口,比如“获取列表”。

  • 每个页面生成Token1并前往注册,注册成功Token1采纳,否则为空:(存储在session中)
<?php
    require_once("../vendor/autoload.php");
    use App\Common\NoCSRF;

    session_start();
    $token = NoCSRF::generate('csrf_token');
    $_SESSION['token_key'] = $token;//token_key或者Token1
?>

<html>
<body>
<script>
    var token_key = "";
    $.ajax({
            url: "/xxx/public/?s=Token/Login",//身份注册接口
            type: "POST",
            cache: false,
            async: false,
            data:{
                    "csrf_token": "<?php echo $token?>",
            },
            success: function(res) {
                    token_key = "<?php echo $token?>";
            }, error: function(error) {
                    console.log(error);
                    token_key = "";
            }
    });
</script>
</body>
</body>
  • 注册接口:./src/App/Api/Token.php(这里的key仍然是‘csrf_token’)
public function Login(){//页面头部的注册
    session_start();
    try {
        NoCSRF::check( 'csrf_token', $_POST, true, 60*10, false );
    }
    catch ( Exception $e ) {
        unset($_SESSION['token_key']);//验证不通过就销毁
        exit('Need token!');
    }
}
  • 拥有token_key后获取组合token: (此处开始,tokentoken_key作为键值)
        // ./src/App/Api/Token.php
    public function index(){
        session_start();
        $token_key = $_SESSION['token_key'];
        //generate函数的参数不再是'csrf_token'而是$token_key
        return NoCSRF::generate($token_key);
    }

    //Ajax请求Token,这一步无变化
    function getCSRF(){
            let csrf_token = "";
            $.ajax({
                    type: "GET",
                    cache: false,
                    async: false,
                    url: "/xxx/xxx/?s=Token/Index",             
                    success: function(res) {
                            csrf_token = res.data;
                    }, error: function(error) {
                            console.log(error);
                            csrf_token = "";
                    }
            });
            return csrf_token;
    }

    //请求业务接口,这里需要将Token1/token_key作为key,其中token_key就是页面首部生成,通过身份注册的
    <script>
        let json_data = {
                'data1' : 'xxx',
        };
        //注意,变量作为key传输必须用下方写法,不能用上面json格式写法,否则key直接为'token_key'.
        json_data[token_key] = getCSRF();

        $.ajax({
                url: "/xxx/xxx/?s=Order/GetList",
                type: "POST",
                data: json_data,        
                success: function(res, status, xhr) {
                        console.log(res);
                        //
                },
        })
     </script>
  • 业务接口验证(对于每一个需要验证的接口,在构造函数内执行,./src/App/Api/Order.php):
public function __construct(){
    session_start();

    //验证$_SESSION中是否存在'token_key'
    if(!isset($_SESSION['token_key'])){
        exit('Need token!');
    }
    $token_key = $_SESSION['token_key'];

    //注意下方check函数的第一个参数不再是'csrf_token'而是$token_key
    try {
        NoCSRF::check( $token_key, $_POST, true, 60*10, false );
    }
    catch ( Exception $e ) {
        exit('Need token!');
    }    
}

public function getRules(){
     //...
}

/// ...Other functions...

完结

至此,整个从前端请求和后端接口验证过程结束。至于如何部署到Phalapi框架或其他框架里面,相信看完整个过程就可以上手,也可以直接查看上传的示例。

本方案只针对采用前后端分离框架开发的微服务项目中,接口安全验证的防护。Web开发中涉及到方方面面的安全性问题:明文传输、数据库明文存储、XSS、渗透、社工等,要想让项目固若金汤,开发过程中都勇于去面对这些问题,寻找方案进行加固。

《鸟哥的Linux私房菜-服务器架设篇》和《大型网站技术架构》推荐阅读
]]>
Web开发,PHP,JavaScript,Web安全
<![CDATA[2.3-线性表的链式表示]]>https://ariser.cn/index.php/archives/106/

单链表的实现和基本操作

// 单链表的操作

#include <iostream>
#include <stdio.h>
#include <stdlib.h>

typedef int ElemType;

typedef struct LNode{// 定义单链表节点类型
    ElemType data;        // 数据域
    struct LNode *next;    //指针域
}LNode, *LinkList;

// 头插法
LinkList List_HeadInsert(LinkList &L){
    LNode *s;
    int x;

    L = (LinkList)malloc(sizeof(LNode));    // 创建头节点
    L->next = NULL;                            //初始化为空链表

    scanf("%d", &x);
    while( x != 999 ){
        s = (LNode*)malloc(sizeof(LNode));    // 创建新结点(分配空间,把地址赋值给 s)
        s->data = x;
        s->next = L->next;        // L 为头指针
        L->next = s;
        scanf("%d", &x);
    }
    return L;
}

// 尾插法
LinkList List_TailInsert(LinkList &L){
    int x;
    L = (LinkList)malloc(sizeof(LNode));
    LNode *s;
    LNode *r = L;    // 表尾指针

    scanf("%d", &x);
    while( x != 999 ){
        s = (LNode *)malloc(sizeof(LNode));
        s->data = x;        // 插入结点赋值
        r->next = s;        // 新结点插入表中
        r = s;                // 地址赋给尾结点,r 指向新的表尾结点,作为下一次插入的临时区
        scanf("%d", &x);
    }
    r->next = NULL;            // 尾结点指针置空
    return L;
}

// 按照序号查找结点值(带头结点)
LNode *GetElem(LinkList L, int i){
    int j = 1;                // 计数,初始值为1
    LNode *p = L->next;        // 头结点赋值给 p
    if(i == 0){
        return L;
    }
    if(i < 1){
        return NULL;
    }
    while (p && j < i){        // 从第1个开始查找
        p = p->next;
        j++;
    }
    return p;                // 返回第 i 个结点的指针,i 大于表长, p = NULL, 直接返回p即可
}

// 删除第 i 个结点
bool List_Delete(LinkList L, int i){
    LinkList p = GetElem(L, i - 1); // 查找删除位置的前驱结点
    if(p == NULL){
        return false;
    }
    LinkList q;
    q = p->next;            // q 指向被删除结点
    p->next = q->next;        // *q从链中断开 相当于 p->next = p->next->next;,但不能这么写,因为要释放qDLinkList
    free(q);                // 释放结点储存空间
    return true;
}

// 位置插入
bool ListFrontInsert(LinkList L, int i, ElemType e){
    // 创建新结点
    LinkList s = (LNode*)malloc(sizeof(LNode));
    s->data = e; 

    LinkList p = GetElem(L, i-1); // 插入位置前驱结点
    if(NULL == p){
        return false;
    }
    s->next = p->next; // 新结点指向当前位置对应结点
    p->next = s;    // 插入位置前驱结点指向当前插入结点
    return true;
}

// 打印链表
void List_Print(LinkList L){
    L = L->next;
    while (L != NULL){
        printf("%3d", L->data);
        L = L->next;
    }
    printf("\n");
}


int main(void){
    LinkList L;
    LinkList search;

    List_TailInsert(L);
    List_Print(L);

    search = GetElem(L, 2);
    if(search != NULL){
        printf("%d\n", search->data);
    }

    printf("位置插入:\n");
    ListFrontInsert(L, 2, 99);
    List_Print(L);
    printf("位置删除:\n");
    List_Delete(L, 4);
    List_Print(L);
    
    return 0;
}

双链表的实现和基本操作

// 双链表
// 结论:画示意图后参照写步骤
#include <iostream>
#include <stdio.h>
#include <stdlib.h>

using namespace std;

typedef int ElemType;

typedef struct DNode{    // 定义双链表节点类型
    ElemType data;        // 数据域
    struct DNode *prior, *next;    // 前驱和后继指针
}DNode, *DLinklist;

// 头插法
DLinklist DList_head_insert(DLinklist &DL){
    DNode *s;
    int x;

    DL = (DLinklist)malloc(sizeof(DNode));// 创建空链表
    DL->next = NULL;
    DL->prior = NULL;
    scanf("%d", &x);

    while(x != 999){
        s = (DNode*)malloc(sizeof(DNode));// 创建新节点
        s->data = x; // 数据域赋值

        s->next = DL->next; // *s 插入到 *DL 之后         ---1
        if(DL->next != NULL){
            DL->next->prior = s;// 第一个节点指向插入的 s     ---2
        }
        s->prior = DL;                                //    ---3

        DL->next = s;                                //    ---4 , 1、2必须在4之前
        scanf("%d", &x);
    }
    return DL;
}

// 尾插法
DLinklist DList_tail_insert(DLinklist &DL){
    int x;
    DL = (DLinklist)malloc(sizeof(DNode));
    DNode *s, *r = DL;

    DL->prior = NULL;
    scanf("%d",&x);
    while(x != 999){
        s = (DNode*)malloc(sizeof(DNode));
        s->data = x;
        r->next = s;
        s->prior = r;
        r = s;
        scanf("%d",&x);
    }
    r->next = NULL;
    return DL;
}

// 取元素
DNode *GetElem(DLinklist DL, int i){
    int j = 1;
    DNode *p = DL->next;

    if(i == 0){
        return DL;
    }
    if(i < 1){
        return NULL;
    }

    while(p && j < i){
        p = p->next;
        j++;
    }
    return p;
}

// 元素头部插入 (p 之后插入 s)
bool DListFrontInsert(DLinklist DL, int i, ElemType e){
    DLinklist p = GetElem(DL, i-1);
    if(p == NULL){
        return false;
    }
    DLinklist s = (DNode*)malloc(sizeof(DNode));
    s->data = e;

    s->next = p->next;
    p->next->prior = s;
    s->prior = p;
    p->next = s;
    return true;
}

// 元素删除
bool DListDelete(DLinklist DL, int i){
    DLinklist p = GetElem(DL, i-1);
    if(p == NULL){
        return false;
    }
    DLinklist q;
    q = p->next;
    if(q == NULL){
        return false;
    }
    p->next = q->next;
    if(q->next != NULL){
        q->next->prior = p;
    }
    free(q);                // 需要释放内存
    return true;
}

// 打印
void PrintDList(DLinklist DL){
    DL = DL->next;
    while(DL != NULL){
        printf("%3d\n", DL->data);
        DL = DL->next;
    }
    printf("\n");
}

int main(void){
    DLinklist DL;
    DLinklist search;

    // DList_head_insert(DL);
    DList_tail_insert(DL);
    printf("尾插法结果:\n");
    PrintDList(DL);

    search = GetElem(DL, 2);
    if(search != NULL){
        printf("序号查找:");
        printf("%d\n\n", search->data);
    }

    DListFrontInsert(DL, 3, 666);
    printf("3位置插入:\n");
    PrintDList(DL);


    DListDelete(DL, 3);
    printf("3位置删除:\n");
    PrintDList(DL);

    return 0;
}

课后代码题目

]]>
数据结构数据结构
<![CDATA[2.2—线性表的顺序表示]]>https://ariser.cn/index.php/archives/100/

顺序表的实现和基本操作

#include <iostream>
#include <stdio.h>
using namespace std;

// 取地址符的意义:https://blog.csdn.net/u011723466/article/details/27109249
// 没有取地址符,就变化不了实际的值。所以 PrintfList 直接 SqList L。
// 要改变 L ,当 元素 = e, 这时写 f(SqList &L, ElemType e){}
// 要改变 L,当 元素 = e,更改为 x,并读取x,这时写 f(SqList &L, ElemType &e){}
#define MaxSize 50

typedef int ElemType;

// 静态分配,长度就是 MaxSize
typedef struct{
    ElemType data[MaxSize];
    int length;
} SqList;

// 动态分配,长度见下方的调用
#define InitSize 100    // 表长度的初始定义
typedef struct {
    ElemType *data;
    int MaxSize_1, length;
}SeqList;

// e 插入到 L 中的第 i 个位置
bool ListInsert(SqList &L, int i, ElemType e){
    if(i < 1 || i > L.length + 1){// 判断 i 的范围是否有效
        //!!! 之所以可以在 length + 1插入,是因为:
        //尾部插入一个,顺序表仍然符合:连续性
        printf("插入的位置超出范围!\n");
        return false;
    }    
        
    if(L.length >= MaxSize){// 储存空间已满,不能插入
        printf("存储空间满!\n");
        return false;
    }        
        
    for(int j = L.length; j >= i; j--){    // i 元素及之后的元素后移
        L.data[j] = L.data[j - 1];
    }
    L.data[i - 1] = e;        //位置 i 放入e
    L.length++;            // 线性表长度加1
    
    return true;
}

// 删除 L 中的 第 i 个元素
bool ListDelete(SqList &L, int i, ElemType &e){
    if(i < 1 || i > L.length){
        return false;
    }

    e = L.data[i-1];        // 被删除元素赋给e
    // 因为data是数组,所以 i-1
    for(int j = i; j < L.length; j++){
        L.data[j-1] = L.data[j];    // 元素前移,当前等于后方的元素
    }

    L.length --;    //表长度减1
    return true;
}

// 查找 L 中值为 e 的第一个元素
int LocateElem(SqList L, ElemType e){
    for(int i = 0; i < L.length; i ++){
        if(L.data[i] == e){
            return i+1; // 返回位序为 i + 1
        }
    }
    return 0;
}

void PrintfList(SqList L){
    for(int i = 0; i < L.length; i++){
        printf("%4d\n", L.data[i]);
    }
    printf("\n");
}


int main(void){
    SqList L;
    ElemType del;

    L.data[0] = 1;
    L.data[1] = 2;
    L.data[2] = 3;
    L.data[3] = 4;
    L.length = 5;

    if(ListInsert(L, 2, 10)){
        printf("插入成功!\n");
        PrintfList(L);
    }else{
        printf("插入失败!\n");
    }

    if(ListDelete(L, 2, del)){
        printf("删除成功!\n");
        printf("删除的元素为: %d\n", del);
        PrintfList(L);
    }else{
        printf("删除失败!\n");
    }

    int ret = LocateElem(L, 3);
    if(ret){
        printf("查找成功!\n");
        printf("元素位置为:%d\n", ret);
        PrintfList(L);
    }else{
        printf("查找失败!\n");
    }

    return 0;
}

课后代码题

2.2.3-1

#include <iostream>
#include <stdio.h>
using namespace std;

#define MaxSize 50

typedef int ElemType;

// 静态分配,长度就是 MaxSize
typedef struct{
    ElemType data[MaxSize];
    int length;
} SqList;

bool Del_Min(SqList &L, ElemType &e){
    // e 为元素值
    if(L.length == 0){
        printf("顺序表为空!\n");
        return false;
    }

    e = L.data[0];
    int e_i = 0;    // 次序码
    for(int i = 0; i < L.length; i++){
        if(L.data[i] < e){
            e = L.data[i];
            e_i = i;
        }
    }

    L.data[e_i] = L.data[e_i + 1];
    L.length --;
    
    return true;
}

void PrintfList(SqList L){
    for(int i = 0; i < L.length; i++){
        printf("%4d\n", L.data[i]);
    }
    printf("\n");
}

int main(void){
    SqList L;
    ElemType del;

    L.data[0] = 1;
    L.data[1] = 2;
    L.data[2] = 3;
    L.data[3] = 4;
    L.data[4] = -5;
    L.length = 5;

    int ret = Del_Min(L, del);
    if(ret){
        printf("删除的最小元素为:%d\n", del);
        PrintfList(L);
    }else{
        printf("操作失败!\n");
    }
    return 0;
}

2.2.3-2

/**
 * 翻转线性表,空间复杂度为1
 */
#include <iostream>
#include <stdio.h>
using namespace std;
#define MaxSize 50
typedef int ElemType;

typedef struct{
    ElemType data[MaxSize];
    int length;
} SqList;

void PrintfList(SqList L){
    for(int i = 0; i < L.length; i++){
        printf("%4d", L.data[i]);
    }
    printf("\n");
}

void ReverseList(SqList &L){

    // 自己实现,空间复杂度为 L 的长度,其实没必要
    // SqList L2 = L;
    // for(int i = 0; i < L.length / 2; i++){
    //     L.data[i] = L.data[L.length - i -1];
    // }

    // for(int j = 0; j < L2.length / 2; j++){
    //     L.data[L.length - j -1] = L2.data[j];
    // }
    
    // 答案写法,提前记录再放到后面,只用一个缓冲区
    ElemType temp; 
    for(int i = 0; i < L.length / 2; i++){
        temp = L.data[i];
        L.data[i] = L.data[L.length - i -1];
        L.data[L.length - i -1] = temp;
    }
    
}

int main(void){
    SqList L;
    ElemType del;

    L.data[0] = 1;
    L.data[1] = 2;
    L.data[2] = 3;
    L.data[3] = 4;
    L.data[4] = 5;
    L.length = 5;

    PrintfList(L);
    ReverseList(L);
    PrintfList(L);

    return 0;
}

2.2.3-3

/**
 * 删除线性表中所有值为 x 的数据元素。时间复杂度O(n),空间复杂度O(1)
 */
#include <iostream>
#include <stdio.h>
using namespace std;

#define MaxSize 50
typedef int ElemType;

typedef struct{
    ElemType data[MaxSize];
    int length;
} SqList;

void PrintfList(SqList L){
    for(int i = 0; i < L.length; i++){
        printf("%4d", L.data[i]);
    }
    printf("\n");
}

void Delete_Elem_x(SqList &L, ElemType e){

    // 方法1, 不等于 e 的元素放入新数组
    int k = 0; //不相同元素的个数
    for(int i = 0; i < L.length; i++){
        if(L.data[i] != e){
            L.data[k] = L.data[i];
            k++;
        }
    }
    L.length = k;
    
    // 方法2
    // int k = 0; // 相同元素个数
    // for(int i = 0; i < L.length; i++){
    //     if(L.data[i] == e){
    //         k ++;
    //     }else {
    //         L.data[i - k] = L.data[i]; // 元素前移
    //     }
    // }
    // L.length = L.length - k;
}


int main(void){
    SqList L;
    ElemType del;

    L.data[0] = 1;
    L.data[1] = 2;
    L.data[2] = 2;
    L.data[3] = 4;
    L.data[4] = 5;
    L.length = 5;

    PrintfList(L);
    Delete_Elem_x(L, 2);
    PrintfList(L);

    return 0;
}

2.2.3-4,5

/**
 * 删除线性表中所有值为 s 到 t 的数据元素。
 */
#include <iostream>
#include <stdio.h>
using namespace std;

#define MaxSize 50
typedef int ElemType;

typedef struct{
    ElemType data[MaxSize];
    int length;
} SqList;

void PrintfList(SqList L){
    for(int i = 0; i < L.length; i++){
        printf("%4d", L.data[i]);
    }
    printf("\n");
}

bool Delete_between(SqList &L, ElemType s, ElemType t){
    
    // 我的做法
    if(!(s < t) && L.length == 0){
        return false;
    }
    int k = 0;
    for(int i = 0; i < L.length; i++){
        if(L.data[i] < s || L.data[i] > t){
            L.data[k] = L.data[i];
            k++;
        }
    }
    L.length = k;
    return true;
}

int main(void){
    SqList L;
    ElemType del;

    L.data[0] = 1;
    L.data[1] = 2;
    L.data[2] = 2;
    L.data[3] = 4;
    L.data[4] = 5;
    L.length = 5;

    PrintfList(L);
    Delete_between(L, 2, 4);
    PrintfList(L);

    return 0;
}

2.2.3-6

/**
 * 删除线性表中重复的数据元素。
 */
#include <iostream>
#include <stdio.h>
using namespace std;

#define MaxSize 50
typedef int ElemType;

typedef struct{
    ElemType data[MaxSize];
    int length;
} SqList;

void PrintfList(SqList L){
    for(int i = 0; i < L.length; i++){
        printf("%4d", L.data[i]);
    }
    printf("\n");
}

bool Delete_repetition(SqList &L){

    if(L.length == 0){
        return false;
    }

    int k = 0;
    for(int i = 0; i < L.length; i ++){
        if(L.data[i] != L.data[i + 1]){
            L.data[k] = L.data[i];
            k++;
            // 高级写法 L.data[k++] = L.data[i];
        }
    }

    L.length = k; // 虽然 K 起始于 0,表示下标,通常总长度要k+1,但最后执行了++
    return true;
}

int main(void){
    SqList L;
    ElemType del;

    L.data[0] = 1;
    L.data[1] = 2;
    L.data[2] = 2;
    L.data[3] = 2;
    L.data[4] = 5;
    L.data[5] = 5;
    L.length = 6;

    PrintfList(L);
    Delete_repetition(L);
    PrintfList(L);

    return 0;
}

2.2.3-7

/**
 * 两个有序顺序表合并成一个顺序表。
 */
#include <iostream>
#include <stdio.h>
using namespace std;

#define MaxSize 50
typedef int ElemType;

typedef struct{
    ElemType data[MaxSize];
    int length;
} SqList;

void PrintfList(SqList L){
    for(int i = 0; i < L.length; i++){
        printf("%4d", L.data[i]);
    }
    printf("\n");
}

// 标准答案,经典算法,应该记住。两个有序表连接成新的有序表
bool Connect_List2(SqList &L1, SqList &L2, SqList &LBig){
    if(L1.length + L2.length > LBig.length){
        return false;
    }

    int i = 0, j = 0, s = 0;
    while(i < L1.length && j < L2.length){
        if(L1.data[i] < L2.data[j]){
            LBig.data[s++] = L1.data[i++];
        }else{
            LBig.data[s++] = L2.data[j++];
        }
    }

    while(i < L1.length){
        LBig.data[s++] = L1.data[i++];
    }

    while(j < L2.length){
        LBig.data[s++] = L2.data[j++];
    }

    LBig.length = s;

    return true;
}

bool Connect_List(SqList &L1, SqList &L2, SqList &LBig){
    // 第一个答案,错误,没有考虑到“有序”,不能简单做连接工作
    // for(int i = 0; i < L1.length; i++){
    //     LBig.data[i] = L1.data[i];
    // }

    // for(int j = 0; j < L2.length; j++){
    //     LBig.data[L1.length + j] = L2.data[j];
    // }

    // 改良版,外层循环,再内层循环,将小的存入新表
    int t = 0; // L2表的次序码
    int s = 0; // 总表的次序码
    for(int i = 0; i < L1.length; i++){
        while(t < L2.length){
            if(L1.data[i] <= L2.data[t]){
                LBig.data[s++] = L1.data[i];
                break; // 取到 L1 中的值,必须要break L2的 while
            }
            else{
                LBig.data[s++] = L2.data[t];
                t++;
            }
        }
    }

    if(t < L2.length){ // 防止 L1 循环完,L2 直接 break, 使 L2 中的值丢失。
        for(int j = t; j < L2.length; j++){ // 取 break 之后的 t, L2.data[t] ~ L2.data[L2.length]
            LBig.data[s++] = L2.data[j];
        }
    }

    // 简化流行的写法,while直接做判断和循环,见标准答案
    // int j = t;
    // while(t < L2.length){
    //     LBig.data[s++] = L2.data[j++];
    // }

    LBig.length = s;

    return true;
}



int main(void){
    SqList L1;
    SqList L2;
    SqList LBig;
    ElemType del;

    L1.data[0] = 1;
    L1.data[1] = 3;
    L1.data[2] = 5;
    L1.length = 3;

    L2.data[0] = 2;
    L2.data[1] = 4;
    L2.data[2] = 6;
    L2.data[3] = 8;
    L2.length = 4;

    LBig.length = 7;

    PrintfList(L1);
    PrintfList(L2);
    PrintfList(LBig);

    Connect_List(L1, L2, LBig);

    PrintfList(L1);
    PrintfList(L2);
    PrintfList(LBig);

    return 0;
}

2.2.3-8

/**
 * 数组中两个线性表,编写函数实现两个线性表位置调换
 */
#include <iostream>
#include <stdio.h>

using namespace std;

#define MaxSize 50
typedef int ElemType;

typedef struct{
    ElemType data[MaxSize];
    int length;
} SqList;

void PrintfList(SqList L){
    for (int i = 0; i < L.length; i++){
        printf("%4d\n", L.data[i]);
    }
    printf("\n");
}

int main(void){
    SqList L;
    ElemType del;

    L.data[0] = 1;
    L.data[1] = 3;
    L.data[2] = 5;
    L.length = 3;
    
    PrintfList(L);

    return 0;
}

2.2.3-9

/**
 * 数组中两个线性表,编写函数实现两个线性表位置调换
 */
#include <iostream>
#include <stdio.h>

using namespace std;

#define MaxSize 50
typedef int ElemType;

typedef struct{
    ElemType data[MaxSize];
    int length;
} SqList;

void PrintfList(SqList L){
    for (int i = 0; i < L.length; i++){
        printf("%4d\n", L.data[i]);
    }
    printf("\n");
}

// 经典算法,折半查找
int Binary_Search(SqList L, ElemType x){

    int low = 0, high = L.length - 1, mid;

    while (low <= high){
        mid = (low + high) / 2;

        if (L.data[mid] == x){
            return mid;
        } else if(x < L.data[mid]){
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return -1;
}

int SearchExchangeInsert(SqList &L, ElemType x){
    int low = 0, high = L.length, mid;

    while (low <= high){
        mid = (low + high) / 2;

        if (L.data[mid] == x){
            int temp = L.data[mid];
            L.data[mid] = L.data[mid + 1];
            L.data[mid + 1] = temp;
            return 1; // 替换操作

        } else if (x < L.data[mid]){
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }

    
    //全部折半完无匹配
    if (low > high){
        // low 即最后一次折半所在的地方
        for(int i = L.length; i > low - 1; i--){
            L.data[i] = L.data[i - 1];
        }
        printf("%d,%d,%d\n", mid, low, high);
        L.data[high + 1] = x;
        L.length++;

        return 2; // 插入操作
    }

    return -1;
}

int main(void){
    SqList L;
    ElemType del;

    L.data[0] = 11;
    L.data[1] = 22;
    L.data[2] = 33;
    L.data[3] = 35;
    L.data[4] = 55;
    L.data[5] = 66;
    L.data[6] = 77;
    L.length = 7;
    
    PrintfList(L);
    printf("%d\n", SearchExchangeInsert(L, 34)); 
    PrintfList(L);

    return 0;
}
]]>
数据结构数据结构
<![CDATA[以RSA加密传输密钥(JS-PHP)]]>https://ariser.cn/index.php/archives/31/

介绍

网页表单提交中,如果直接用明文传输,特别是用户密码,很容易被抓包获取到信息。
这里以AJAX向PHP后台提交数据为例,用RSA对表单数据进行加密传输。

RSA简介

  1. RSA公钥加密算法是1977年由Ron Rivest、Adi Shamirh和LenAdleman在(美国麻省理工学院)开发的。RSA取名来自开发他们三者的名字。
  2. RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的所有密码攻击,已被ISO推荐为公钥数据加密标准。目前该加密方式广泛用于网上银行、数字签名等场合。
  3. RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。

算法核心

RSA的算法涉及三个参数,n、e1、e2。
其中,n是两个大质数p、q的积,n的二进制表示时所占用的位数,就是所谓的密钥长度。
e1和e2是一对相关的值,e1可以任意取,但要求e1与(p-1)*(q-1)互质;再选择e2,要求(e2*e1)mod((p-1)*(q-1))=1。
(n,e1),(n,e2)就是密钥对。其中(n,e1)为公钥,(n,e2)为私钥。[1]  
RSA加解密的算法完全相同,设A为明文,B为密文,则:A=B^e2 mod n;B=A^e1 mod n;(公钥加密体制中,一般用公钥加密,私钥解密)
e1和e2可以互换使用,即:
A=B^e1 mod n;B=A^e2 mod n;

实现流程

  1. 客户端公钥加密
  2. 服务端私钥解密
  • 生成公钥和私钥

~~这里用到支付宝提供一键生成工具,便于开发者生成一对RSA密钥,传送门
下载安装后,按照里面的教程生成一组公钥和私钥,目录结构如下:~~

上述用支付宝RSA生成工具的方法容易造成PHP识别不了密钥,这里用Openssl自动生成,参考:PHP RSA加密解密

  • Unix/Linux自带openssl,直接控制台运行:

    1. 私钥 :openssl genrsa -out rsa_private_key.pem 1024(去掉1024默认生成的是2048位)
    2. 公钥:openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
  • Windows需要另外下载安装openssl,参考:使用OpenSSL生成RSA证书

生成以下文件:

rsa_private_key.pem //私钥文件
rsa_public_key.pem  //公钥文件
rsa_private_key_pkcs8.pem //暂时用不上
  • 生成JS的RSA类库

    运行build_js.php生成RAS.js
    (要将build_js.php 和 生成的rsa_public_key.pem 放到同一目录下再命令行php build_js.php

  • JS引用RSA类库

<script type="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script src="RSA.js"></script>
<script>
    function f() {
        var username = $("#inputText1").val();
        var pswd = $("#inputText2").val();
        $.ajax({
            url:'json_test.php',
            data:{"username":rsa_encode(username), "pswd":rsa_encode(pswd)},
            type:'post',
            success: function(data){
                alert(data);
                window.location.reload()

            },
            error: function(XMLHttpRequest, textStatus, errorThrown) {
                alert(XMLHttpRequest.status);
                alert(XMLHttpRequest.readyState);
                alert(textStatus);
            },
        });
    }
</script>
  • PHP解密数据(用法可见function.php

<?php
/**
 * RSA私钥解密,需在php.ini开启php_openssl.dll扩展
 * @param String : after_encode_data 前端传来,经 RSA 加密后的数据
 * @return 返回解密后的数据
 */
function rsa_decode($after_encode_data)
{
    // 读取私钥文件
    $private_key = file_get_contents('rsa_private_key.pem');
    openssl_private_decrypt(
        base64_decode($after_encode_data),
        $decode_result,
        $private_key
    );
    return $decode_result;
}
?>

效果展示

调试窗口

]]>
Web开发,PHP,JavaScript,Web安全
<![CDATA[改良的MD5密码储存方案]]>https://ariser.cn/index.php/archives/18/

曾逛过一个服务器后台,发现就算看到数据库信息,也无法破解Admin用户的密码,因为MD5是不可逆的,稍微复杂一点就基本不可能破解。开始一直采用RSA存储密码还是存在破解的风险(进入到后台以及源码中的私钥)。

  1. 这里稍作改进,加密算法为:1. MD5[MD5[密码]+username]。2. 截取从第10截取16位长度
  2. PHP脚本实现如下:
$username = '////';
$password = '////';
$pswdCode = md5($password);
$mixCode = md5($pswdCode.$username);
$realCode = substr($mixCode, 9, 16);
echo $realCode;
]]>
Web开发
<![CDATA[微信图片地址转换(简单图床程序)]]>https://ariser.cn/index.php/archives/19/在:微信文章归档系统开发过程中,直接<img src="///">引用微信公众号里面的图片链接(打开文章后,图片上右键->新标签页打开),会出现以下情况:

解决方案:图片存到本地使用本地URL(图片格式webp很强大,也是微信用的,动图静图二合一的格式)

  1. 用户添加或修改首页图片操作的时候:
  2. 传到后台的图片URL先下载图片写入到本地(外部可访问)
  3. 根据时间戳生成图片名称
  4. 返回本地图片的URL

相当于一个简易的图床功能,源码如下:

/**
    * 简单图床程序,解决微信图片防止盗链问题,要设置www-data对img文件夹的写入权限
    * @desc 输入微信图片URL,存入本地,返回本地URL,涉及文件写入操作,不要放在common
    * @param $url
    * @return string
    */
private function simplePictureBed($url){
    $date = date('Ymd-His', $_SERVER['REQUEST_TIME']);
    $file_name = $date.'.webp';//拼接图片名称
    
    $img = file_get_contents($url);
    file_put_contents('/webdata/article/public/img/'.$file_name, $img);
    return 'http://'.$_SERVER['HTTP_HOST'].'/article/public/img/'.$file_name;
}

public function update($id, $newData) {
    $newData['img'] = $this->simplePictureBed($newData['img']);//转换图片地址
    
    $model = new ModelArticle();
    return $model->update($id, $newData);
}
]]>
PHP
<![CDATA[下拉菜单点击刷新页面]]>https://ariser.cn/index.php/archives/23/直接看代码:

<select id="select1">
<!-- /// --!>
</select>

<script>
//下拉菜单点击,加载数据
$("#select1").change(function(){//左侧写入ID
    let typeNum = $(this).children('option:selected').val();
    refresh(typeNum);//页面加载函数
});
</script>
]]>
Web开发,JavaScript
<![CDATA[bootstrap.paginator.min.js分页插件的使用]]>https://ariser.cn/index.php/archives/20/bootstrap-paginator.min.js是基于Bootstrap v2/v3的分页插件,但诸如AdminLTE3等采用最新Bootstrap4的前端框架,使用后根本不会出现样式。这里到处爬贴找到了解决方法:解决bootstrap4 使用 bootstrap-paginator不显示样式的问题

项目中的 /public/static/js/bootstrap-paginator.min.js是改进后的版本,适配Bootstrap v4;

插件基于Ajax的使用方法:Bootstrap-paginator + PageHelper 前后台分页对接

当然需要对接口进行设计

  • 后端接口所需参数
    /**
     * 获取分页列表特定数据(title+url)
     * @desc 根据类型筛选列表数据,支持分页,用于用户
     * @return array    items   列表数据
     * @return int      total   总数量
     * @return int      page    当前第几页
     * @return int      perpage 每页数量
     */

    public function getList(){
        $rs = array();

        $domain = new DomainArticle();
        $list = $domain->getList($this->type, $this->page, $this->perpage);

        $rs['items'] = $list['items'];
        $rs['total'] = $list['total'];
        $rs['page'] = $this->page;
        $rs['perpage'] = $this->perpage;

        return $rs;
    }
  • 数据库分页查询算法
public function getList($type, $page, $perpage) {
    $sql = 'SELECT a.*, t.name'.'
        FROM wechat_article  a ,article_type t
        WHERE a.type = :types AND a.type = t.type
        ORDER BY a.orders DESC
        LIMIT :limit1, :limit2';
    
    $params = array(':types' => $type, ':limit1' => ($page - 1) * $perpage, ':limit2' => $perpage);
    
    return $this->getORM()->queryAll($sql, $params);
//  没有定义关联字段的方法,所以直接原生SQL。因为栏目是动态未知的,所以不能直接在JS中case更改样式
//        return $this->getORM()
//            ->select('*, article_type.name')
//            ->where('type', $type)
//            ->order('orders DESC')
//            ->limit(($page - 1) * $perpage, $perpage)
//            ->fetchAll();
}
  • Ajax调用:
let perpage = 8; //每页数目
let json_data = {
        "type": typeNum,
        "page": 1,
        "perpage": perpage,
    };
json_data[token_key] = getCSRF();

function refresh(typeNum) {
    renderSelect('#select1', typeNum);
    renderSelect('#select3', typeNum);//添加模态框下拉菜单
    $('#total').empty();
    $('#table1').empty();
    let perpage = 8;
    $.ajax({ 
        url: "/article/public/?s=Article/MainList",
        type: "POST",
        async: false,//关闭异步加载,因为后台的csrf_token有时效性
        data: json_data,

        success: function(res, status, xhr){
            let data = res.data;
            let items = data.items;
            Article_items = items;
            console.log(res);
            if (!res.ret || res.ret != 200) {
                console.log(res.msg);
                $('#table1').append('<tr><td>出错了┗|`O′|┛,请联系管理员!</tr></td>');
                return;
            }
            $('#total').append(data.total);
            let str = '';
            if(data.total == 0){
                $('#table1').append('<tr><td>本栏目还没有发表文章!该更新了(๑•̀ㅂ•́)و✧</td></tr>');
                return;
            }

            for(let i = 0; i < items.length; i++){
                str = '<tr><td>'+items[i].name+'</td><td>'+items[i].orders+'</td><td><a href="'+items[i].url+'" target="_blank">'+items[i].title+'</a></td><td>'+items[i].addtime+'</td><td class="text-center"><a id="edit" title="编辑"><i class="fa fa-edit text-primary"></i></a>&nbsp;&nbsp;&nbsp;<a id="delete" title="删除"><i class="fa fa-trash text-danger"></i></a></td></tr>';
                $('#table1').append(str);
            }
            

            var options = {
                bootstrapMajorVersion:3, //bootstrap的版本要求
                currentPage:data.page,//当前页数
                totalPages:Math.ceil(data.total/data.perpage),//总页数,向上取整
                numberOfPages:data.perpage,//每页记录数
                
                itemTexts: function(type, page, current){//设置分页样式
                    switch (type) {
                        case "first":
                            return "首页";
                        case "prev":
                            return "上一页";
                        case "next":
                            return "下一页";
                        case "last":
                            return "末页";
                        case "page":
                            return page;
                    }
                },
                onPageClicked: function(event, originalEvent, type, page){
                    $('#table1').empty();
                    let json_data = {
                        "type": typeNum,
                        "page": 1,
                        "perpage": perpage,
                    };
                    json_data[token_key] = getCSRF();
                    $.ajax({
                        url:"/article/public/?s=Article/mainList",
                        type:"POST",
                        async: false,
                        data: json_data,
                        success: function(res, status, xhr){
                            let data = res.data;
                            let items = data.items;
                            Article_items = items;
                            console.log(res);
                            if (!res.ret || res.ret != 200) {
                                console.log(res.msg);
                                $('#table1').append('<tr><td>出错了┗|`O′|┛,请联系管理员!</tr></td>');
                                return;
                            }
                            
                            let str = '';  
                            if(data.total == 0){
                                $('#table1').append('<tr><td>本栏目还没有发表文章!该更新了(๑•̀ㅂ•́)و✧</td></tr>');
                                return;
                            }
                            for(let i = 0; i < items.length; i++){
                                str = '<tr><td>'+items[i].name+'</td><td>'+items[i].orders+'</td><td><a href="'+items[i].url+'" target="_blank">'+items[i].title+'</a></td><td>'+items[i].addtime+'</td><td class="text-center"><a id="edit" title="编辑"><i class="fa fa-edit text-primary"></i></a>&nbsp;&nbsp;&nbsp;<a id="delete" title="删除"><i class="fa fa-trash text-danger"></i></a></td></tr>';
                                $('#table1').append(str);
                            }
                        }
                    })
                }
            };
            //初始化分页框
            element.bootstrapPaginator(options);
        },
        error: function(error){
            console.log(error);
            alert("出错了┗|`O′|┛,请联系管理员!");
        }
    });
    renderSelect('#select2', typeNum);//编辑模态框的下拉菜单
}
]]>
Web开发,PHP,JavaScript
<![CDATA[隐藏网站后台(设置链接密码)]]>https://ariser.cn/index.php/archives/22/看代码,在需要隐藏的页面首部加上:

$path_pass = 'pswd';    //载入页面的密码 http://host/Login.php?p=pswd
$path = isset($_GET['p'])?addslashes(trim($_GET['p'])):'';
if($path != $path_pass || !$path){
    header('Location: index.php');
    exit();
}

访问地址为:http://host/Login.php?p=pswd,不加链接密码会跳转到其他页面。

此方法也可以用于隐藏WordPress等博客后台的地址。

]]>
PHP,Web安全
<![CDATA[后台限制IP登陆错误次数]]>https://ariser.cn/index.php/archives/21/借助session实现,作为一个接口发布:

public function Index(){
    $rs = array();
    
    $ip = getIP::Index();

    if(!isset($_SESSION[$ip])){
        $_SESSION[$ip] = 5;
    }

    $rs['code'] = -1;

    if(isset($_SESSION[$ip]) && $_SESSION[$ip] > 0)
    {
        $domain = new DomainAdmin();
        $flag = $domain->ifUser($this->username, $this->pswd);

        if($flag == true){
            $_SESSION['adminadmin'] = true;
            $rs['href'] = "admin.php";
            $rs['code'] = 1;
            //登陆成功
        }else
            {
                $_SESSION['adminadmin'] = false;
                $_SESSION[$ip] --;
                $rs['code'] = 0;
                $rs['count'] = $_SESSION[$ip];
                //账号或密码错误,返回code = 0 和 可用次数count
            }
    }else
        {
            $rs['code'] = -1;
            $_SESSION['adminadmin'] = false;
            $rs['count'] = $_SESSION[$ip];
            //IP登陆次数用完,锁定,一直返回code = -1
        }
    return $rs;
}

页面头部检测:

//Login.php 登陆页面头部
if(isset($_SESSION[$ip]) && $_SESSION[$ip] == 0){
    echo '<script>window.location="404.html"</script>';
}

//admin.php 后台页面头部
if(!isset($_SESSION["adminadmin"]) || !$_SESSION["adminadmin"] === true || $_SESSION[$ip] < 0){
    echo '<script>window.location="Login.php"</script>';
}

登录界面JS:

 function f() {
        let username = $("#inputText1").val();
        let pswd = $("#inputText2").val();
        let csrf_token = $("#csrf_token").val();
        $.ajax({
            type:'POST',
            url:'/article/public/?s=Login/Index',
            data:{
                "username": rsa_encode(username),
                "pswd": rsa_encode(pswd),
                "csrf_token": csrf_token,
            },
            
            success: function(res, status, xhr){
                let data = res.data;
                console.log(res);
                if (!res.ret || res.ret != 200) {
                    console.log(res.msg);
                    alert('通信错误,请联系管理员!');
                    return;
                }
                
                if(data.code == 1)
                {
                    window.location = data.href;
                }else if(data.code == 0){
                    alert('账号或密码错误!剩余可用次数为:'+data.count);
                    window.location.reload();
                }else if(data.code == -1){
                    window.location="404.html";
                }
            },
            error: function(XMLHttpRequest, textStatus, errorThrown) {
                console.log(XMLHttpRequest.status);
                console.log(XMLHttpRequest.readyState);
                console.log(textStatus);
                console.log(errorThrown);
                alert('参数出错,请刷新后重试!');
            },
        });
    }
]]>
PHP,Web安全
<![CDATA[Mac配置环境Nginx]]>https://ariser.cn/index.php/archives/5/环境:MacOS 版本:High Sierra 10.13.5

目录:

  • 修改Apache端口号
  • 安装、配置nginx
  • nginx解析php

修改Apache端口号

Mac自带有apache服务,可以直接开启及配置

开启apache:  sudo apachectl start
重启apache:  sudo apachectl restart
关闭apache:  sudo apachectl stop

这里把80端口让给nginx,apache默认设置为8080

1.修改httpd.conf

sudo vim /etc/apache2/httpd.conf


vim下/80搜索字符,按n和N向上下选择其他项,将Listen后的端口号修改8080


这里用#注释做下备份,Esc wq保存

2.修改httpd-vhosts.conf

sudo vim /etc/apache2/extra/httpd-vhosts.conf

貌似有两个地方需要修改重启apache,localhost:8080,出现It works!代表修改成功:

sudo apachectl restart

没修改hosts的用127.0.0.1:8080

安装、配置nginx

使用brew安装nginx

brew install nginx

修改配置文件,端口设置为80
默认为8080端口

给几个权限:

sudo chown root:wheel /usr/local/Cellar/nginx/1.15.2/bin/nginx
sudo chmod u+s /usr/local/Cellar/nginx/1.15.2/bin/nginx
sudo chown -R root:wheel /usr/local/etc/nginx/

这里的版本号要根据自己的做修改,用 brew info nginx 来查看路径

重启下nginx

nginx -t
nginx -s reload
brew services restart nginx

浏览器localhost,出现以下界面则代表安装成功:

如果依然是It works,建议清理下浏览器缓存

nginx解析php

上一步完成,一般并不能直接访问php网站,localhost/index.php会直接下载或Nginx An error occurred,nginx这时并不能直接解析php。因为Mac系统的php-fpm通常开启不了。 通常sudo php-fpm会显示: ERROR: failed to open error_log (/usr/var/log/php-fpm.log): No such file or directory
以及:No pool defined. at least one pool section must be specified in config file

修改php-fpm配置:

sudo cp /private/etc/php-fpm.conf.default /private/etc/php-fpm.conf
/private/etc/php-fpm.conf

修改error_log路径:

error_log = /usr/local/var/log/php-fpm.log

这时即可sudo php-fpm开启。 访问localhost/index.php(这个文件自己写一个)依然会出现以下问题: 1.访问 index.php 报 403 Forbidden.

vim /usr/local/etc/nginx/nginx.conf

找到 server 的 location 配置,给 index 加一个 index.php

location / {
  root  html;
  index index.html index.htm index.php;
}

2.访问 index.php 报 File not found. 找到server 下被注释的 location ~.php$(删除代码前面的 ‘#')

location ~ \.php$ {
  root      html;
  fastcgi_pass  127.0.0.1:9000;
  fastcgi_index index.php;
  fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
  include    fastcgi_params;
}

且更改

fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;

为:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

然后重启nginx:

sudo nginx -s reload //重载配置文件
sudo nginx -s stop //停止nginx服务
sudo nginx //开启nginx服务

参考的帖子: Mac下Nginx安装环境配置详解

Mac自带PHP启动php-fpm问题解决

php-fpm:No pool defined解决方法

]]>
Linux
<![CDATA[PHP学习随笔]]>https://ariser.cn/index.php/archives/6/20180624
变量名称不能以数字开头
没有单独创建变量的命令,会在首次赋值的时候被创建
作用域: local(局部) global(全局) static(静态)

函数内部,global修饰函数内变量,可以访问函数内全局变量 global $x;

超级全局变量:

$x = 75;
$y = 25;
function addition()
{
    $GLOBALS['z'] = $GLOBALS['x'] + $GLOBALS['y'];
}
addition();
echo $z;//函数外调用z变量,输出结果为100

static:

function myTest(){
    $x=0;
    echo $x;
    $x++;
}

myTest();
myTest();
myTest(); //第二行,不加static,每次函数执行后会删除所有变量,加了static后这个局部变量不会被删除,保存的都是最后一次执行的值
//默认输出结果为1,static修饰$x后输出为3

单双引号都可以修饰字符串 var_dump()返回数据类型和值,显示为int(-123) 数组:

$cars=array(BMW,123,2131);
var_dump()的结果:
array(3) { [0]=> string(3) BMW [1]=> string(5) Volvo [2]=> string(4) saab }
$arrlength=count($cars); //求数组长度

关联数组:

$age=array(Peter=>35,Ben=>37,Joe=>43);等价于:
$age['Peter']=35;
$age['Ben']=37;
$age['Joe']=43;

遍历关联数组:

foreach($age as $x=>$x_value){ //$x是变量,$_x_value是变量值
    echo Key=.$x.,Value=.$x_value;
}

数组排序:

$cars=array(BMW,123,2131);
sort($cars); 按照字母/数字升序排列
rsort($cars); ------------降序排列

$age=array(Peter=>35,Ben=>37,Joe=>43);
asort($age); //按照数组的值升序
ksort($age); //按照数组的键升序
//对应的降序则是arsort和krsort

0625 字符串操作:

strlen(Hello world!);//求长度

并置运算符:

echo $text1. .$text2; //连接字符串

strpos() 函数用于在字符串内查找一个字符或一段指定的文本。 如果在字符串中找到匹配,该函数会返回第一个匹配的字符位置。如果未找到匹配,则返回 FALSE

echo strpos(Hello world!,world); //结果为6

x===y 绝对等于,等于且类型相同 xy 不等于

两种表单信息获取:

<form method="post" action="<?php echo $_SERVER['PHP_SELF'];?>">
Name: <input type="text" name="fname">
<input type="submit">
</form>
 
<?php 
$name = $_REQUEST['fname']; //$name = $_POST['fname']; 
echo $name; 
?>

魔术常量:

echo '函数名为:' . __FUNCTION__ ; //返回类名
echo '这是第  ' . __LINE__ . '  行';//某个php文件里面的
echo '该文件位于  ' . __FILE__ . '  '; //完整路径,直到文件E:\PHP\TEST\index.php
echo '该文件位于  ' . __DIR__ . '  ';//该文件在E:\PHP\TEST

function test() {
        echo '函数名为:' . __FUNCTION__ ;  //返回函数名
}


class a{
    function test(){
        echo '函数名为:' . __METHOD__ ; //返回的是a::test
  }
}

namespace MyProject;
echo '命名空间为:', __NAMESPACE__, ''; // 输出 MyProject

0626 晚上时间用于整理博客和家人视频聊天了,睡觉前闲预习面向对象,明天搞定,后天命名空间。 0627

PHP面向对象:

类:

定义了一件事物的抽象特点。类的定义包含了数据的形式以及对数据的操作。比如动物Animal就是一个抽象类。

对象:

在现实世界里我们所面对的事情都是对象,如计算机、狗、自行车等。狗和羊就是类的实例,它们有行为、形态。

类的定义、创建对象、实例化:

class Animal{
    /*成员变量*/
    var $animal;
    public $animal2 = 'i am an animal !'; //类中变量用var或者public定义,var默认为公有属性

    /*成员函数*/
    public function display(){
        echo $this->animal2;    //$this在php中称作为伪变量,用于访问成员变量
    }

    public function get_display($arr)
    {
        $this->animal = $arr;
        echo i am a .$this->animal. !;
    }

}

/*对象实例化*/
$sheep = new Animal;
$Dog = new Animal;

/*调用成员方法*/
$sheep->display();
$Dog->get_display('Dog');

构造函数:

>具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。
class Animal{
    /*成员变量*/
    var $animal = 'Pig';

    /*构造函数*/
    function __construct($ani1){
        $this->animal = $ani1;
    }

    function Shout(){
        echo i am .$this->animal;
        echo <br></br>;
    }
}

//$pig = new Animal();
//$pig->Shout();

/*自动调用了__construct()进行了初始化*/
$dog = new Animal('Dog');
$sheep = new Animal('Sheep');

$dog->Shout();
$sheep->Shout();

析构函数:

对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数。

class Animal{
    /*成员变量*/
    var $animal = 'Pig';

    /*构造函数*/
    function __construct($ani1){
        $this->animal = $ani1;
        echo 在构造函数中初始化成功,动物为.$this->animal.<br></br>;
    }

    function Shout(){
        echo i am a .$this->animal.<br></br>;
    }
    /*析构函数*/
    function __destruct(){
        echo 析构函数被调用;
    }


}

$dog = new Animal('Dog');
$dog->Shout();

继承:

extends 来继承一个类,PHP 不支持多继承

方法重写:

父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。

class Animal{
    var $animal;

    function __construct($ani1){
        $this->animal = $ani1;
        echo I am .$this->animal.<br></br>;
    }

    function Shout(){
            echo I can shout !<br></br>;
    }

    function Action(){
        echo I can move !<br></br>;
    }
}

/*继承父类*/
Class Dog extends Animal {
    /*重写父类方法*/
    function Action(){
        echo I can run !<br></br>;
    }
}

$animal = new Animal('Animal'); //输出: I am Animal
$animal->Shout();//输出: I am Animal
$animal->Action();//输出: I can action.
echo <br></br>;

$dog = new Dog('Dog');//输出:I am Dog !,证明父类构造函数自动被继承(PHP5及以后版本,PHP4不会自动继承父类构造方法)
$dog->Shout();//输出:I can shout !,父类方法被继承
$dog->Action('run'); //输出: I can run !,父类方法被重写

访问控制:

通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现访问控制。

  1. public(公有):公有的类成员可以在任何地方被访问。
  2. protected(保护):受保护的类成员则可以被其自身以及其子类和父类访问。
  3. private(私有):私有的类成员则只能被其定义所在的类访问。
class MyClass{
        public $public = '公有、';
        protected $protected = '受保护、';
        private $private = '私有<br></br>';
        
        function printHello(){
            echo $this->public;
            echo $this->protected;
            echo $this->private;
        }
    }
    
    $obj = new MyClass();
    
    echo $obj->public;//正常执行,输出:公有
    echo $obj->protected;//产生致命错误
    echo $obj->private;//产生致命错误
    
    $obj->printHello();//正常执行输出:公有、受保护、私有

方法的访问控制:

类中的方法可以被定义为公有,私有或受保护。如果没有设置这些关键字,则该方法默认为公有。

首先定义一个类,里面三个类型的成员函数和一个合计的public函数。

class MyClass{
    public function __construct(){ }

    public function MyPublic(){ }

    protected function MyProtected(){}

    private function Myprivate(){ }

    function Foo(){//默认为公有方法
        $this->MyPublic();
        $this->MyProtected();
        $this->Myprivate();
    }
}

调用这四个成员方法:

$myclass = new MyClass;
$myclass->MyPublic();//正常执行
//$myclass->MyProtected();产生致命错误
//$myclass->MyPrivate();产生致命错误
$myclass->Foo();//public、protect、private都可以执行

对于子类继承:

class MyClass2 extends MyClass{
    //公有方法
    function Foo2(){
        $this->MyPublic();
        $this->MyProtected();
        //$this->Myprivate();致命错误,子类不能继承父类的私有方法
    }
}

继承然后重写:

class Bar{
    public function test(){
        $this->testPrivate();
        $this->testPublic();
    }

    public function testPublic(){
        echo Bar::testPublic<br></br>;
    }

    private function testPrivate(){
        echo Bar::testPrivate<br></br>;
    }
}

class Foo extends Bar
{
    public function testPublic() {
        echo Foo::testPublic<br></br>;
    }

    private function testPrivate() {
        echo Foo::testPrivate<br></br>;
    }
}

$myFoo = new foo();
$myFoo->test(); // 输出: 父类::testPrivate换行 子类::testPublic 换行 子类::testPublic
// 可见,重写父类函数,对private无效,对protected、public有效。

接口:

使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。

接口是通过 interface 定义,就像定义一个标准的类,但其中定义所有的方法都是空的。 接口中定义的所有方法都必须是公有,这是接口的特性。 要实现一个接口,使用 implements操作符。类中必须实现接口中定义的所有方法,否则会报一个致命错误。类可以实现多个接口,用逗号来分隔多个接口的名称。

抽象类

任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。
定义为抽象的类不能被实例化。 被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。 继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的。 首先定义两个接口,动物运动(走和跑)和活动(运动和活动)。

先放图:牧羊犬Bitzer和小羊Shaun—《Shaun the Sheep》很喜欢这部漫画

定义了一个抽象类Animal,包含Run和color两个方法(Dog和Sheep本身都具有的属性)。 定义了两个接口,分别是Write(Dog具有的功能)和Run_faster(Sheep具有的功能)。

abstract class Animal{
    //强制要求Dog类和Sheep类定义这些方法
    abstract protected function run();
    abstract protected function color($colors);
}

interface Write{//狗可以写字
    public function write();
}

interface Run_faster{//羊可以跑得更快
    public function run_faster();
}

定义Dog类继承动物类,实现Write接口。

class Dog extends Animal implements Write {
    private $Dog ;
    private $Colors;
    public function __construct(){
        $this->Dog = 'Dog';
    }

    protected function run(){
        echo $this->Dog. can run.<br></br>;
    }
    protected function color($color){
        $this->Colors=$color;
        echo $this->Dog. is .$this->Colors.. <br></br>;
    }
    public function write(){
        echo $this->Dog. can write. <br></br>;
    }
    public function Show(){
        $this->run();
        $this->color('yellow');
        $this->write();
    }
}

定义Sheep类继承动物类和实现Run_faster接口。

class Sheep extends Animal implements Run_faster {
    private $Sheep;
    private $Colors;
    public function __construct(){
        $this->Sheep = 'Sheep';
    }
    protected function run(){
        echo $this->Sheep. can run.<br></br>;
    }
    protected function color($color){
        $this->Colors=$color;
        echo $this->Sheep. is .$this->Colors.. <br></br>;
    }
    public function run_faster(){
        echo $this->Sheep. can run faster. <br></br>;
    }
    public function Show(){
        $this->run();
        $this->color('white');
        $this->run_faster();
    }
}

实例化Dog和Sheep

$dog = new Dog();
$sheep = new Sheep();

$dog->Show();
echo <br></br>;
$sheep->Show();

/**
 * 输出结果:
 *Dog can run.
 *Dog is yellow.
 *Dog can write.

 *sheep can run.
 *sheep is white.
 *sheep can run faster.
 */

一直以来对接口和抽象类两者概念没完全搞清楚。今天把两者放到一起对比和思考,有了更为深刻的理解。 这篇帖子:深入理解Java的接口和抽象类给了启迪。

接口:

大白话式解释 -

接口和抽象类理解

一句话总结,抽象类是对一种事物的抽象,而接口是对行为的抽象。

抽象的继承体现的“是不是” Dog和Sheep继承Animal这个抽象类,它们就属于这个动物这个种类了,可以run和具有color。

接口的实现体现的“有没有” Dog实现了Write这个接口,它就有了write这个功能。没有实现Run_faster接口,就没有run_faster的功能。 Sheep实现了Run_faster接口,它就可以run_faster。没有实现Write接口,也就没有write的功能。

常量:

常量: 类中始终保持不变的值。定义和使用常量的时候不需要使用 $ 符号。 常量的值必须是一个定值,不能是变量,类属性,数学运算的结果或函数调用。 自 PHP 5.3.0 起,可以用一个变量来动态调用类。但该变量的值不能为关键字(如 self,parent 或 static)。

class MyClass{
    const str = '常量值';
    function showConstant(){
        echo self::str.<br></br>;
    }
}

echo MyClass::str.<br></br>;

$classname = MyClass;
echo $classname::str . <br></br>; // 自 5.3.0 起

$class = new MyClass();
$class->showConstant();

echo $class::str . <br></br>; // 自 PHP 5.3.0 起
//输出四次常量值

卫冕冠军德国队输了,剩下一点面向对象明天了结然后开始选择性地学习相应知识了。Cookie、Session等。

Static关键字:

声明类属性或方法为 static(静态),就可以不实例化类而直接访问。 静态属性不能通过一个类已实例化的对象来访问(但静态方法可以)。 由于静态方法不需要通过对象即可调用,所以伪变量 $this 在静态方法中不可用。 静态属性不可以由对象通过 ->操作符来访问。 自 PHP 5.3.0 起,可以用一个变量来动态调用类。但该变量的值不能为关键字 self,parent 或 static。

class Foo{
    public static $my_static = 'foo';

    public function staticValue(){
        return self::$my_static;
    }
}
echo Foo::$my_static.<br></br>;//输出foo,无需实例化直接访问
$foo = new Foo();
echo $foo->staticValue().<br></br>;//输出foo

Final 关键字

PHP5新增。如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承。
写的时候直接摆错,提示final方法不可被覆盖(重写)-

(子类)调用父类构造方法

PHP 不会在子类的构造方法中自动的调用父类的构造方法。要执行父类的构造方法,需要在子类的构造方法中调用 parent::__construct()

class BaseClass{
    function __construct(){
        echo 主类构造方法<br></br>;
    }
}

class ChildClass extends BaseClass{
    function __construct(){
        parent::__construct();
        echo 子类构造方法<br></br>;
    }
}
class OtherSubClass extends BaseClass {
    // 继承 主类 的构造方法
}

$obj = new BaseClass();//echo主类
$obj = new ChildClass();//echo主类换行echo子类
$obj = new OtherSubClass();//echo主类

$_GET和$_POST

$_GET 变量用于收集来自 method=get 的表单中的值。 $_POST 变量用于收集来自 method=post 的表单中的值。

前端表单:

<form action="chuli.php" method="post"><!--method="get"-->
姓名:<input type="text" name="name">
年龄:<input type="text" name="age">
    <input type="submit" value="提交">
</form>

chuli.php

$name = $_POST["name"];//获取到之后直接放入变量
$age = $_POST["age"];
echo "欢迎: ".$name."
";
echo "年龄为: ".$age."
";



点击提交按钮后: post方式的URL: 

http://127.0.0.1/phpstudy/file.php


get方式的URL: 

http://127.0.0.1/phpstudy/file.php?name=Aris&age=20


两者本质都是TCP链接: 从带有 POST 方法的表单发送的信息,对任何人都是不可见的,并且对发送信息的量也没有限制。 然而,由于变量不显示在 URL 中,所以无法把页面加入书签。

在 HTML 表单中使用 method=get 时,所有的变量名和值都会显示在 URL 中。 注释:所以在发送密码或其他敏感信息时,不应该使用这个方法! 然而,正因为变量显示在 URL 中,因此可以在收藏夹中收藏该页面。在某些情况下,这是很有用的。 
]]>
Web开发,PHP
<![CDATA[微信开发3-openid+session身份验证]]>https://ariser.cn/index.php/archives/7/

其实到了这里才算做真正的"开发",前面的环境部署属于运维部分,并没有涉及到微信API调用之类的。

目的是获取用户的openid,与数据库储存的用户信息进行对比,通过则启动session,后面调用session验证以授予访问页面的权限,否则跳转其它页面。(有点冗余,可以看下面的结构图)。

从用户操作的角度来说,会便利化用户的操作。用户登陆过了该公众号,我们获取到的openid对于每个公众号是唯一不变的。省去了用户下载APP、注册登陆等步骤。

openID:


在微信里面,每个用户关注公众号,就会产生对这个公众号唯一的28位openID(微信自动生成)。openID具有唯一性和不可变性,可以作为用户对某一公众号身份的凭证。

session:

业务流程:


下面分别讲获取openid和创建运用session。

配置网页授权获取用户基本信息


在公众号管理界面:网页服务->网页账号->修改
这里填入授权回调页面域名,就是自己的域名。比如www.xxx.com

1.获取code

用户点击自定义菜单的按钮后,会跳转到目标页面,这个是开发人员自己设定的url链接。
比如我们想跳转到www.xxx.com/index.php,此时我们并不是让用户直接跳转到这个页面,而是把链接进行处理。

https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxxxxxxxxxxxxxxxxx
&redirect_uri=http://www.xxx.com/index.php
&response_type=code
&scope=snsapi_userinfo
&state=123#wechat_redirect

(这里为了方便查看换行写了,用的时候注意删去换行)
这里的xxxxxxxxxxxxxxxxxx为自己公众号的appid
&redirect_uri= 加上"回调链接",即要转到的地址;&response_type=加上"回调参数code",下一步会用到这个变量;
&scop=加上"snsapi_base 和 snsapi_userfo",不多解释,参考这个: 传送门
后面的&state写123就行

2.通过CODE向服务器请求Openid

这里用后台处理脚本实现,创建getOpenid.php,直接上代码:

<?php
function getOpenid(){
        $code = $_GET['code'];//获取code
        $appid = 'xxxxxxxxxxxxxxxxxx';
        $secret = 'ssssssssssssssssssssssssssssssss';
        $weixin = file_get_contents("https://api.weixin.qq.com/sns/oauth2/access_token?appid=".$appid."&secret=".$secret."&code="
                .$code."&grant_type=authorization_code");//通过code换取网页授权access_token,(利用code间接获取用户数据组)
        $jsondecode = json_decode($weixin); //对JSON格式的字符串进行编码
        $array = get_object_vars($jsondecode);//转换成数组(用户数据组)
        $openid = $array['openid'];//取出openid
        return $openid;
}
$openid = getOpenid();
?>
在要用到openid的地方,先引入文件 include ("getOpenid.php"),然后就直接可以用这个变量。

注意:这里建议最好取出来存入变量中。刚开始没有取出来,直接用到openid的时候就执行一次函数,这个时候有的时候根本获取不到。

最后找到原因:必须得先有"获取code"这一步的跳转,紧接getOpenid()才可以拿到openid。没有code回调参数的情况下执行这个函数是拿不到openid的。

另一种方法:$code = $_GET['code'];放到函数外部,存入到变量,就不需要每个页面都获取一次code,。函数内要先 global $code。

3.身份验证、创建session

紧接上一步,我们获取到了openid,就可以进行身份验证了。

例:openid1(未注册用户,不能访问站点) openid2(注册用户,可以访问站点)
于是我们用获取到的openid与数据库进行对比,返回相应的权限值。action.php:

<?php
include ("getOpenid.php");
$openid = getOpenid();

function ifUser(){
        global $openid;  //访问函数外全局变量,前面加上global
        /*
        *这里进行查数据库操作
        *$num为返回的行数
        *$num = $result->num_rows;
        */

        //如果已经存在该用户
        if ($num){
                session_start();//session启动
                $_SESSION["admin"] = true;//赋值真
        }else{//跳转其他界面
                echo '<script>window.location="Hello.html"</script>';
                die();
        }
}
?>
上面两个部分可以应该写到一起,要用的时候直接调用一个函数即可,这里还是以分开为准。

session参考的博客:传送门,以及传送门2

要访问的页面:www.xxx.com/index.php,我们就可以在index.php的开头写入判断部分。
通过则继续执行下方的代码,不通过则自动跳转其他界面。

<?php
include ("getOpenid.php");
$openid = getOpenid();
include ("action.php");
ifUser();//如果两个步骤放到一个PHP脚本,则只用引用一次,可以减少openid的出现次数
 
$admin = false; //防止全局变量造成安全隐患
session_start();//启动会话
if(!isset($_SESSION["admin"]) || !$_SESSION["admin"] === true){
    //session验证不通过就跳转到其它页面
    echo '<script>window.location="Hello2.php"</script>';
    die();
}
 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello Openid !</title>
</head>
<body>
    <?php 
    echo "Welcom !""<br>";
    echo "Openid is: ".&openid."<br>";
    ?>
</body>
</html>

思考:session可以作为储存的媒介,可以将诸如身份的信息存储到里面$_SESSION["admin"]='123';,要用的时候就进行调用 $x = $_SESSION["admin"]。

]]>
Web开发,PHP,微信开发
<![CDATA[微信开发2-微擎]]>https://ariser.cn/index.php/archives/8/创青春省赛搞完,期中考试考完,又可以闲下来搞这个了
尝试了下宝塔面板管理服务器,比较好用,于是准备结合微擎来往下开发

其实要一个自定义菜单转到外链的功能。
至于为什么用微擎:微信官方开发说明对新手不太友好,自己是卡在与服务器的绑定那停滞不前,官方给的web.py那些部署了还是连接失败,没脾气了。
微擎的话相当于将操作简化了,且功能十分多。 作为学习的话,后面还是会转向原生的开发。
环境: 腾讯云服务器(香港)centos7 + Xshell + 微信测试号

目录:

  • 宝塔面板安装及一些调试
  • 微擎&&微信公众号对接

宝塔面板安装及一些调试

宝塔面板,管理服务器更加方便,且有服务器安全监控,数据库和FPT等要稍微修改配置。

centos直接yum源安装:

yum install -y wget && wget -O install.sh http://download.bt.cn/install/install.sh && sh install.sh

(ps:一键安装的过程中最好是root身份,因为涉及到的一些目录可能需要权限)
安装之后的界面

安装之后停留在此,上面会给出宝塔的账号密码。 之后 http://XXXX:8888即可进入面板。(xxxx表示自己服务器地址或域名)

接下来是微擎的环境。微擎基于PHP,可用LNMP和LAMP,二选一。A和N分别指的是apache和nginx。
传送门:Centos 7.3搭建LNMP环境

当然,宝塔给出了一键部署:
给出了两种套件

两种方式的话,新手作为学习可以尝试手动SSH相应服务,修改配置。
学习到知识之后,可以一键部署。

这里给出几个设置的修改:

  1. FTP不能远程管理

    1. 服务器控制台和面板开放21端口
    2. 登陆宝塔面板管理系统,找到左侧的“软件管理”--“FTP软件”---点击“设置”
    3. 点击配置修改:查找“ForcePassiveIP”(位置188行左右)
    4. 删除前面的“#”将“192.168.0.1”修改为:服务器的IP地址,是服务器不是你客户端的IP地址!
    5. 重启FTP

2.MySql不能远程连接
服务器控制台和面板开放3361端口(安全问题,慎开,学会习惯用phpmyadmin)

微擎&&微信公众号对接

  • 安装微擎:

有了LNMP环境之后,我们把下载的微擎通过FTP上传到网站根目录,注意要解压出来

然后访问 http://你的域名/install.php 进行安装,之后设置参数

这里注意到要用微擎的管理员账号,如果忘记了,可以进行以下步骤: (类似微赞等平台通用,WordPress找时间测试下) 1,下载附件 password.php(第四页会给到源码和下载地址) 2,使用文本编辑器修改第一行 $auth = ‘你的访问密码, 如 :123456’; 3,上传至你的微擎根目录 4,访问:你的域名/password.php 操作并重置密码 5,删除 password.php (重要)

(温馨提示:请确定文件的USER权限!)
重置密码完成后需要尽快删除 password.php 避免资料泄露

  • 微擎与微信测试号对接:

微擎中打开:公众号->公众号设置
微擎设置界面

测试号/公众号里面: 先设置安全域名

两边进行对接和绑定,这里就对应微信官方文档给出的绑定服务器操作。

这里第一次终于直接绑定成功,之后项目原因换了服务器之后,绑定到另一个上面就又显示绑定失败,但微擎仍然可以对之进行管理。
可能这个是跟域名走的吧,原因是需要更新微擎版本。

这里给出项目测试号的二维码:

管理员密码就是WeLock。

### 附录:

password.php源码:

<?php //使用后记得删除文件
//因为使用造成的安全问题 本人概不负责
//转载请注明保留版权信息、网址和作者
//BY:http://www.ariser.cn
//定义你的访问密码后上传
$auth = '123';
 
define('IN_SYS', true);
require './framework/bootstrap.inc.php';
load()-?>web('template');
load()->web('common');
load()->model('user');
 
if($_W['ispost'] && $_GPC['auth'] == $auth && $auth != '') {
    $isok = true;
    $username = trim($_GPC['username']);
    $password = $_GPC['password'];
    if(!empty($username) && !empty($password)) {
        
        $member = user_single(array('username'=>$username));
        if(empty($member)) {
            message('输入的用户名不存在.');
        }
        $hash = user_hash($password, $member['salt']);
        $r = array();
        $r['password'] = $hash;
        pdo_update('users', $r, array('uid'=>$member['uid']));
        exit('<script>alert(密码修改成功, 请重新登陆, 并尽快删除本文件, 避免密码泄露隐患.);location.href = ./</script>');
    }
}
?>



    <meta charset="utf-8"></meta><meta content="IE=edge" http-equiv="X-UA-Compatible"></meta><meta content="width=device-width," initial-scale="1.0" name="viewport"></meta><link href="./resource/favicon.png" icon="" rel="shortcut"></link><title>密码找回工具 FOR 0.6 - 微擎 - 公众平台自助引擎 -  Powered by WE7.CC</title><link href="./web/resource/css/bootstrap.min.css" rel="stylesheet"></link><link href="./web/resource/css/font-awesome.min.css" rel="stylesheet"></link><link href="./web/resource/css/common.css" rel="stylesheet"></link><script src="./web/resource/js/require.js"></script><script src="./web/resource/js/app/config.js"></script><div class="main">
    <form action="" class="form-horizontal" enctype="multipart/form-data" form="" formcheck="" method="post" onsubmit="return">
        <div class="panel" panel-default="" style="margin:10px;">
            <div class="panel-heading">
                重置密码 <span class="text-muted">如果你的管理密码意外遗失, 请使用此工具重置密码, 重置成功后请尽快将此文件从服务器删除, 避免造成安全隐患</span>
            </div>
            <div class="panel-body">
                <?php if($isok) {??><div class="form-group">
                    <label class="col-xs-12" col-lg-2="" col-md-2="" col-sm-3="" control-label="">用户名:</label>
                    <div class="col-sm-9">
                        <input echo="" name="auth" type="hidden" value="<?php"></input> />
                        <input class="form-control" name="username" placeholder="请输入你要重置密码的用户名" type="text"></input></div>
                </div>
                <div class="form-group">
                    <label class="col-xs-12" col-lg-2="" col-md-2="" col-sm-3="" control-label="">新的登录密码:</label>
                    <div class="col-sm-9">
                        <input class="form-control" name="password" placeholder="" type="password"></input></div>
                </div>
                <?php } else {??><div class="form-group">
                    <label class="col-xs-12" col-lg-2="" col-md-2="" col-sm-3="" control-label="">请输入访问密码</label>
                    <div class="col-sm-9">
                        <input class="form-control" name="auth" placeholder="" type="password"></input></div>
                </div>
                <?php }??><div class="form-group">
                    <label class="col-xs-12" col-lg-2="" col-md-2="" col-sm-3="" control-label=""></label>
                    <div class="col-sm-9">
                        <button btn-block="" btn-primary="" class="btn" name="submit" type="submit" value="提交">提交</button>
                        <input name="token" type="hidden" value="{$_W['token']}"></input></div>
                </div>
            </div>
        </div>
    </form>
</div>
]]>
Web开发,PHP,Linux,微信开发
<![CDATA[微信开发1—测试平台部署]]>https://ariser.cn/index.php/archives/9/项目是微信开锁 借助微信的网页授权功能,获取用户openid等信息与数据库进行对比,来验证身份,从而开门

目录

  • 微信公众号的选择

  • 服务器的部署


微信公众号有三种类型:服务号、订阅号、企业号

这里以个人身份只能申请到最低级别的普通个人订阅号,什么都搞不了。 如果要申请更高级别的则要提供组织、企业等单位的资质证明,作为一个学生也很难实施(步骤繁琐、在一些流程需要老师出面)。
找老师简单交流后,知道了有微信公众号测试号,不需要验证什么的流程,直接拿来用,而且功能权限也很多,就很舒服。传送门:微信公众平台接口测试帐号申请

用自己的微信登陆即可

进去后是这样的
在页面中,你还可以看到微信的appID和appserect,在获得Token、修改创建自定义菜单或者其它需要验证权限的时候需要用到这两个密钥,要注意对这两个参数保密! 正式开发的时候,需要对URL和Token进行设置:URL即微信公众平台服务器请求你开发服务器的入口页面,注意不是你网站的域名!具体到网页。 Token相当于腾讯微信公众平台服务器与你服务器交互的密钥,在正式部署的时候,请务必设置的复杂一下,否则可能被黑客利用,伪装你的服务器向你的用户发送消息。 另外需要说明的是,微信公众平台有两个Token,名字一样,但是是两个概念。 一个是腾讯微信公众平台服务器和你的服务器交互的密钥,是通过在你的服务器上设置的; 另外一个是通过appID和appsecret 获取到的操作菜单、发送客服消息等所需的一个凭据,注意不要混淆! 其他操作参见:微信公众平台开发文档

服务器配置

安装/更新需要用到的软件

安装python2.7版本以上 安装web.py 安装libxml2, libxslt, lxml python

centos安装python2.7(且与python2.6共存教程)

更新yum和安装开发工具集:
yum -y update
yum groupinstall -y 'development tools'

安装python工具需要的软件包(不然安装setuptools和pip会出错,然后提示找缺少什么文件,所以提前装上):
yum install -y zlib-devel bzip2-devel openssl-devel xz-libs wget

用源码安装Python2.7:
wget http://www.python.org/ftp/python/2.7.13/Python-2.7.13.tar.xz //下载源码
xz -d Python-2.7.13.tar.xz   // 解压文件
tar -xvf Python-2.7.13.tar  // 进入解压后的文件夹
cd Python-2.7.13  //运行配置
./configure --prefix=/usr/local  // 编译和安装
make
make altinstall  //make altinstall不用影响原来的python版本

设置软连接:
ln -s /usr/local/bin/python2.7 /usr/bin/python //经过软连接以后我们再使用python命令的是时候就指向我们的2.7版本的python了

安装setuptools:
wget --no-check-certificate https://pypi.python.org/packages/source/s/setuptools/setuptools-1.4.2.tar.gz  //下载源码
tar -xvf setuptools-1.4.2.tar.gz   //解压文件
cd setuptools-1.4.2   // 进入解压后的文件夹
python2.7 setup.py install   // 安装


安装pip:
curl https://bootstrap.pypa.io/get-pip.py | python2.7
  • 安装web.py

方式一:到web.py官网下载源码,解压出来安装 python setup.py install方式二:easy install安装 上一步其实已经安装好setuptools,这里也可以用yum源来安装 yum `install python-setuptoolseasy_install web.pypip install web.py注意,以上命令适用于python2版本,python3版本的命令为: pip install web.py==0.40-dev1`

  • 安装libxml2, libxslt, lxml python

yum `install + 名称`

  • 编辑运行py程序:

创建main.py,vim编辑之后,python main.py运行 `

# -- coding: utf-8 --
  # filename: main.py
  import web
  
  urls = (
      '/wx', 'Handle',
  )
  
  class Handle(object):
      def GET(self):
          return hello, this is a test
  
  if name == '__main__':
      app = web.application(urls, globals())
      app.run()

如果出现“socket.error: No socket could be created“错误信息,可能为80端口号被占用,可能是没有权限,请自行查询解决办法。也执行命令:sudo python main.py 80 或者改为其它端口。
- 浏览器输入http://外网IP:80/wx检验是否成功:

![](http://111.230.220.47/wp-content/uploads/2018/05/2018051218342025.png) 
]]>Web开发,PHP,微信开发<![CDATA[实验三 Linux进程基本实验]]>https://ariser.cn/index.php/archives/10/

任务概要

  1. 实验题目
    在 Linux 环境下,用 C 语言编写一个程序,以树状结构(即体现父子关系)输出系统当前所有进程。
  2. 实验目的

    • 理解进程和程序、进程和线程的联系与区别;
    • 熟悉进程的重要数据结构、进程的状态、进程管理与控制及实现机制;
    • 练习获取进程执行的相关信息,理解进程的执行过程。
  3. 实验平台
    Vmware Tools 14、Xshell5、Ubuntu16
  4. 实验要求
    在 Linux 下,用C语言自己编写一个程序,要求能以树状结构(即能体现父子关系)打印出系统当前的所有进程;独立完成
  5. 设计思路和流程

    • Linux自带pstree
    • 编写pstree,分析思路
    • 编译运行

详细设计

  1. 系统自带pstree展示:

pstree命令以树状图显示进程间的关系(display a tree of processes)。ps命令可以显示当前正在运行的那些进程的信息,但是对于它们之间的关系却显示得不够清晰。在Linux系统中,系统调用fork()可以创建子进程,通过shell也可以创建子进程,Linux系统中进程之间的关系天生就是一棵树,树的根就是进程PID为1的init(systemd)进程。常用的参数如下:(因为pstree输出的信息可能比较多,所以最好与more/less配合使用。)
格式:pstree
以树状图显示进程,只显示进程的名字,且相同进程合并显示。
格式:pstree -p
以树状图显示进程,还显示进程PID。
格式:pstree <pid>
格式:pstree -p <pid>
以树状图显示进程PID为<pid>的进程以及子孙进程,如果有-p参数则同时显示每个进程的PID。
格式:pstree -a
以树状图显示进程,相同名称的进程不合并显示,并且会显示命令行参数,如果有-p参数则同时显示每个进程的PID。

  1. 自行设计pstree.c,思路
    在Linux内核中, /proc目录是一种文件系统,即proc文件系统,存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。/proc目录中包含许多以数字命名的子目录,这些数字表示系统当前正在运行进程的进程号,里面包含对应进程相关的多个信息文件。

本实验参考以上的pstree的结构,通过访问/proc 里面的含有进程号的文件夹,获取进程的信息,每个文件夹中都含有一个status,获取其中的pid,ppid及name,从而获取父进程的id,源程序中的数据结构,是为了方便完成,将获取的文件存入。

将每次读取一个status的信息存入到此结构中,将所有的进程文件夹全部存入到数组中,进行结构树的打印。打印过程中若发现其存在子进程,记录父进程,递归打印子进程,当无子进程时,返回父进程。

  1. 编译运行:
    touch pstree.c


写好的pstree放入其中,vim或直接图形界面复制

gcc -o pstree pstree.c


./pstree

打印出了进程树

proc -A 看到SSH进程号为2014

pstree 2014 -p 显示出的ssh进程的父子进程与打印出的进程树的结构显示的是一样的

  1. 实验体会:
    实验结果显示,进程树为层次递进的,父子进程的先后顺序表现得十分明显。所有进程的主进程都是systemd,每个进程都有其父进程统管。Linux的子进程的实现是用的fork(),且可以递归下去。打印的时候,如果下一层没有子进程,则返回父进程。

Linux 使用“进程控制块(PCB)”的数据结构 task_struct 来代表一个进程,该结构包含进程的所有信息。所有关联 PCB 通过链表被组织起来,就形成了父子关系。

  1. 实验源码:

    #include
    #include
    #include
    #include
    #include
    #include
    #include #include
    #include
    
    int s = 0;
    char default_path[1024] = "/proc/";
    typedef struct file_info {
    int pid;
    int ppid;
    char name[1024];
    int flag;
    int rec;
    }info;
    
    int my_getpid(char* str){
    int len = strlen(str);
    char num[10];
    int i,j,ret;
    
    if(strncmp(str,"Pid",3) == 0)
    {
    for(i = 0; i < len; i++) { if(str[i]>='0' && str[i]<='9') break; } for(j = 0;j < len - i;j++) { num[j] = str[i + j]; } num[j] = '\0'; ret = atoi(num); } else ret = 0; return ret; } int my_getppid(char *str) { int len = strlen(str); char num[10]; int i, j, ret; if(strncmp(str,"PPid",4) == 0) { for(i = 0;i < len;i++){ if(str[i] >= '0' && str[i] <= '9') break; } for(j = 0;j < len - i;j++){ num[j] = str[i + j]; } num[j] = '\0'; ret = atoi(num); } else ret = 0; return ret; } void print_pstree(info *file,int count,int ppid,int rec) { int i,j,k; for(i = 0;i < count;i++) { if(file[i].flag == 0 && file[i].ppid == ppid) { file[i].rec = rec + 1; file[i].flag = 1; for(k = 0;k < rec;k++) { printf(" >>>>");
    }
    printf("%s\n", file[i].name);
    print_pstree(file,count,file[i].pid,file[i].rec);
    }
    }
    
    }
    
    int main(){
    int i,j,k,total,s1,s2,count,t;
    char str[1024], dir[1024];
    struct dirent **namelist;
    strcpy(dir, default_path);
    total = scandir(dir,&namelist,0,alphasort);
    printf("path = %s, total = %d\n", dir, total);
    
    for(i = 0; i < total; i++) { strcpy(str, namelist[i]->d_name);
    if(str[0] >= '0' && str[0] <= '9') count++; } printf("process counts is %d\n",count); info file[1024]; i = 0; t = 0; while(i < total) { FILE *fp; char path[1024],name[1024]; int pid,ppid; strcpy(str,namelist[i]->d_name);
    strcpy(path,default_path);
    if(str[0] >= '0' && str[0] <= '9') { strcat(path, str); strcat(path,"/status"); fp = fopen(path,"r"); while(!feof(fp)) { fgets(str, 1024, fp); if((s1 = my_getpid(str)) != 0) { pid = s1; } if((s2 = my_getppid(str)) != 0) ppid = s2; if(strncmp(str, "Name", 4) == 0) { for(j = 4;j < strlen(str);j++) { if(str[j] >= 'a'&&str[j] <= 'z') break; } for(k = j;k < strlen(str);k++) { name[k - j] = str[k]; } name[k - j] = '\0'; } file[t].pid = pid; file[t].ppid = ppid; strcpy(file[t].name,name); } fclose(fp); t++; } i++; } int m; for( m = 0;m < count;m++) { file[m].flag = 0; file[m].rec = 0; } print_pstree(file,count,0,0); return 0; }
]]>
Linux
<![CDATA[Android开发初试 - 计算器]]>https://ariser.cn/index.php/archives/11/

目录

  • AndroidStudio
  • 简单计算器的实现
  • 遇到的问题及心得体会

开发工具-AndroidStudio

Android开发环境大致可分为以下几个部分:

  • jdk环境配置
    就不多做介绍,下载安装及配置环境变量
  • 编译器
    平常Java用的是IDEA,可以直接在这个里面添加SDK和ADT,eclipse也是一样

这里看了下知乎,貌似AndroidStudio专业一点,自带终端和logcat等,专为开发Android而生,于是就入坑了。

  • 安装Android SDK和ADT和AVD
    安卓的软件开发包就是Android SDK,可以自己手动安装然后到编译器里面配置也可以直接在AS里面一件配置好。

就直接传送门吧,过程比较顺利。对于ADT的作用,借用官方的一句话“为了使得Android应用的创建,运行和调试更加方便快捷,Android的开发团队为IDE定制了一个插件:Android Development Tools(ADT)。”,安装的过程也就是给编译器添加插件的过程。
AVD就是一个安卓虚拟机,方便调试,这里也可以插上自己的安卓机来调试

  • 配置AndroidStudio
    这里弄了两个东西。
  1. 装后发现自己的C盘少了好几G的空间,这肯定不能忍。这是由于AS默认SDK和ADT的位置是在C盘的,参考传送门。
  2. 代码配色:大爱Solarized Dark配色
  3. 另外一个好玩的插件:activate-power-mode.jar,感受一下:
  4. 还有几个插件,Sexy Editor(更酷炫的配色)、Translation(AS内置翻译插件)

整体结构

  • 布局
  • 事件监听
  • 细节调整
  • 编译运行、打包

详细介绍

项目创建好之后,需要对下面两个地方进行操作:

事件监听和计算功能:java/com.XXXXX.项目名/MainActivity
布局:res/layout/activity_main.xml

两部分代码如下:

activity_main.xml:

[ccen_xml]
&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        &gt;


        &lt;LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:orientation="horizontal"
                android:layout_weight="2"
                &gt;

                &lt;TextView
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:id="@+id/et_input"
                        android:textSize="40sp"
                        /&gt;
        &lt;/LinearLayout&gt;

        &lt;LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="horizontal"
                android:weightSum="4"

                &gt;

                &lt;Button
                        android:id="@+id/btn_clear"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1.5"
                        android:autoLink="none"
                        android:background="#D2D7D9"
                        android:text="C"
                        android:textAppearance="@style/TextAppearance.AppCompat.Display1" /&gt;

                &lt;Button
                        android:id="@+id/btn_del"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1.5"
                        android:background="#D2D7D9"
                        android:text="CE"
                        android:textAppearance="@style/TextAppearance.AppCompat.Display1" /&gt;

                &lt;Button
                        android:id="@+id/btn_divide"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:background="#D2D7D9"
                        android:text="/"
                        android:textAppearance="@style/TextAppearance.AppCompat.Display1" /&gt;
        &lt;/LinearLayout&gt;
        &lt;LinearLayout
                android:layout_width="fill_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:gravity="center"
                android:weightSum="4"
                &gt;

                &lt;Button
                        android:id="@+id/btn_7"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:background="#fafafa"
                        android:text="7"
                        android:textSize="30sp" /&gt;

                &lt;Button
                        android:id="@+id/btn_8"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:background="#fafafa"
                        android:text="8"
                        android:textSize="30sp" /&gt;

                &lt;Button
                        android:id="@+id/btn_9"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:background="#fafafa"
                        android:text="9"
                        android:textSize="30sp" /&gt;

                &lt;Button
                        android:id="@+id/btn_multiply"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:background="#D2D7D9"
                        android:text="×"
                        android:textAppearance="@style/TextAppearance.AppCompat.Display1" /&gt;
        &lt;/LinearLayout&gt;
        &lt;LinearLayout
                android:layout_width="fill_parent"
                android:layout_height="0dp"
                android:orientation="horizontal"
                android:layout_weight="1"
                android:weightSum="4"
                &gt;

                &lt;Button
                        android:id="@+id/btn_4"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:background="#fafafa"
                        android:text="4"
                        android:textSize="30sp" /&gt;

                &lt;Button
                        android:id="@+id/btn_5"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:background="#fafafa"
                        android:text="5"
                        android:textSize="30sp" /&gt;

                &lt;Button
                        android:id="@+id/btn_6"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:background="#fafafa"
                        android:text="6"
                        android:textSize="30sp" /&gt;

                &lt;Button
                        android:id="@+id/btn_minus"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:background="#D2D7D9"
                        android:text="-"
                        android:textAppearance="@style/TextAppearance.AppCompat.Display1" /&gt;
        &lt;/LinearLayout&gt;
        &lt;LinearLayout
                android:layout_width="fill_parent"
                android:layout_height="0dp"
                android:orientation="horizontal"
                android:layout_weight="1"
                android:weightSum="4"
                &gt;

                &lt;Button
                        android:id="@+id/btn_1"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:background="#fafafa"
                        android:text="1"
                        android:textSize="30sp" /&gt;

                &lt;Button
                        android:id="@+id/btn_2"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:background="#fafafa"
                        android:text="2"
                        android:textSize="30sp" /&gt;

                &lt;Button
                        android:id="@+id/btn_3"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:background="#fafafa"
                        android:text="3"
                        android:textSize="30sp" /&gt;

                &lt;Button
                        android:id="@+id/btn_plus"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="1"
                        android:background="#D2D7D9"
                        android:text="+"
                        android:textAppearance="@style/TextAppearance.AppCompat.Display1" /&gt;
        &lt;/LinearLayout&gt;

        &lt;LinearLayout
                android:layout_width="fill_parent"
                android:layout_height="0dp"
                android:orientation="horizontal"
                android:layout_weight="1"
                android:weightSum="4"
                &gt;


                &lt;Button
                        android:id="@+id/btn_0"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="2"
                        android:background="#fafafa"
                        android:text="0"
                        android:textSize="30sp" /&gt;


                &lt;Button
                        android:id="@+id/btn_equal"
                        android:layout_width="0dp"
                        android:layout_height="match_parent"
                        android:layout_weight="2"
                        android:background="#0DA7E7"
                        android:text="="
                        android:textAppearance="@style/TextAppearance.AppCompat.Display1" /&gt;

        &lt;/LinearLayout&gt;

&lt;/LinearLayout&gt;
[/ccen_xml]

MainActivity.java:

package com.example.ice.ariscalculator;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;



public class MainActivity extends AppCompatActivity implements View.OnClickListener{
        Button btn_0,btn_1,btn_2,btn_3,btn_4,btn_5,btn_6,btn_7,btn_8,btn_9,btn_del;
        Button btn_multiply,btn_divide,btn_plus,btn_minus;
        Button btn_clear,btn_equal;
        private TextView et_input;
        boolean clr_flag;    //判断et中是否清空
        @Override
        protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                //实例化对象
                setContentView(R.layout.activity_main);
                btn_0= (Button) findViewById(R.id.btn_0);
                btn_1= (Button) findViewById(R.id.btn_1);
                btn_2= (Button) findViewById(R.id.btn_2);
                btn_3= (Button) findViewById(R.id.btn_3);
                btn_4= (Button) findViewById(R.id.btn_4);
                btn_5= (Button) findViewById(R.id.btn_5);
                btn_6= (Button) findViewById(R.id.btn_6);
                btn_7= (Button) findViewById(R.id.btn_7);
                btn_8= (Button) findViewById(R.id.btn_8);
                btn_9= (Button) findViewById(R.id.btn_9);
                btn_plus= (Button) findViewById(R.id.btn_plus);
                btn_minus= (Button) findViewById(R.id.btn_minus);
                btn_multiply= (Button) findViewById(R.id.btn_multiply);
                btn_divide= (Button) findViewById(R.id.btn_divide);
                btn_clear= (Button) findViewById(R.id.btn_clear);
                btn_del= (Button) findViewById(R.id.btn_del);
                btn_equal= (Button) findViewById(R.id.btn_equal);
                et_input= (TextView) findViewById(R.id.et_input);

                //设置按钮的点击事件
                btn_0.setOnClickListener(this);
                btn_1.setOnClickListener(this);
                btn_2.setOnClickListener(this);
                btn_3.setOnClickListener(this);
                btn_4.setOnClickListener(this);
                btn_5.setOnClickListener(this);
                btn_6.setOnClickListener(this);
                btn_7.setOnClickListener(this);
                btn_8.setOnClickListener(this);
                btn_9.setOnClickListener(this);
                btn_plus.setOnClickListener(this);
                btn_minus.setOnClickListener(this);
                btn_multiply.setOnClickListener(this);
                btn_divide.setOnClickListener(this);
                btn_clear.setOnClickListener(this);
                btn_del.setOnClickListener(this);
                btn_equal.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
                String str=et_input.getText().toString();
                switch (v.getId()){
                        case   R.id.btn_0:
                        case   R.id.btn_1:
                        case   R.id.btn_2:
                        case   R.id.btn_3:
                        case   R.id.btn_4:
                        case   R.id.btn_5:
                        case   R.id.btn_6:
                        case   R.id.btn_7:
                        case   R.id.btn_8:
                        case   R.id.btn_9:
                                if(clr_flag){
                                        clr_flag=false;
                                        str="";
                                        et_input.setText("");
                                }
                                et_input.setText(str+((Button)v).getText());
                                break;
                        case R.id.btn_plus:
                        case R.id.btn_minus:
                        case R.id.btn_multiply:
                        case R.id.btn_divide:
                                if(clr_flag){
                                        clr_flag=false;
                                        str="";
                                        et_input.setText("");
                                }
                                if(str.contains("+")||str.contains("-")||str.contains("×")||str.contains("/")) {
                                        str=str.substring(0,str.indexOf(" "));
                                }
                                et_input.setText(str+" "+((Button)v).getText()+" ");
                                break;
                        case R.id.btn_clear:
                                if(clr_flag)
                                        clr_flag=false;
                                str="";
                                et_input.setText("");
                                break;
                        case R.id.btn_del: //判断是否为空,然后在进行删除
                                if(clr_flag){
                                        clr_flag=false;
                                        str="";
                                        et_input.setText("");
                                }
                                else if(str!=null&amp;&amp;!str.equals("")){
                                        et_input.setText(str.substring(0,str.length()-1));
                                }
                                break;
                        case R.id.btn_equal: //单独运算最后结果
                                getResult();
                                break;
                }
        }

        private void getResult(){
                String exp=et_input.getText().toString();
                if(exp==null||exp.equals("")) return ;
                //因为没有运算符所以不用运算
                if(!exp.contains(" ")){
                        return ;
                }
                if(clr_flag){
                        clr_flag=false;
                        return;
                }
                clr_flag=true;
                //截取运算符前面的字符串
                String s1=exp.substring(0,exp.indexOf(" "));
                //截取的运算符
                String op=exp.substring(exp.indexOf(" ")+1,exp.indexOf(" ")+2);
                //截取运算符后面的字符串
                String s2=exp.substring(exp.indexOf(" ")+3);
                double cnt=0;
                if(!s1.equals("")&amp;&amp;!s2.equals("")){
                        double d1=Double.parseDouble(s1);
                        double d2=Double.parseDouble(s2);
                        if(op.equals("+")){
                                cnt=d1+d2;
                        }
                        if(op.equals("-")){
                                cnt=d1-d2;
                        }
                        if(op.equals("×")){
                                cnt=d1*d2;
                        }
                        if(op.equals("/")){
                                if(d2==0) cnt=0;
                                else cnt=d1/d2;
                        }
                        if(!s1.contains(".")&amp;&amp;!s2.contains(".")&amp;&amp;!op.equals("/")) {
                                int res = (int) cnt;
                                et_input.setText(res+"");
                        }else {
                                et_input.setText(cnt+"");}
                }
                //s1不为空但s2为空
                else if(!s1.equals("")&amp;&amp;s2.equals("")){
                        double d1=Double.parseDouble(s1);
                        if(op.equals("+")){
                                cnt=d1;
                        }
                        if(op.equals("-")){
                                cnt=d1;
                        }
                        if(op.equals("×")){
                                cnt=0;
                        }
                        if(op.equals("/")){
                                cnt=0;
                        } else {
                                et_input.setText(cnt+"");}
                }
                //s1是空但s2不是空
                else if(s1.equals("")&amp;&amp;!s2.equals("")){
                        double d2=Double.parseDouble(s2);
                        if(op.equals("+")){
                                cnt=d2;
                        }
                        if(op.equals("-")){
                                cnt=0-d2;
                        }
                        if(op.equals("×")){
                                cnt=0;
                        }
                        if(op.equals("/")){
                                cnt=0;
                        }
                        if(!s2.contains(".")) {
                                int res = (int) cnt;
                                et_input.setText(res+"");
                        }else {
                                et_input.setText(cnt+"");}
                }
                else {
                        et_input.setText("");
                }
        }
}

布局:

采用的是LinearLayout线性布局,外层竖线布局内层嵌套横向布局。
外层的竖线线性布局,可以让按钮紧贴,方便调整Testview和每个横行的比例关系

android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"

android:orientation="horizontal"

示意图:

颜色的选取是参考win10的计算器。
取色技巧:QQ截屏,鼠标指的位置就会出现RGB值

事件监听:

设置按钮点击的事件执行OnClickListener() -> 里面加入case语句为每个按钮设置功能 -> 编写计算部分的函数(有点乱且没考虑特殊情况,打算在实现多项运算的时候重写)

计算函数大致思路就是读取三组字符串,两个数字以及计算符,对数字字符转换为double类型,再做计算。

细节调整:(更改图标或名称)

  1. resdrawable 放置icon.png(此图片是你需要修改的图标);
  2. 修改AndroidManifest.xml : android:icon="@drawable/icon"
  3. 编译运行即可。

打包生成apk:
不多说,直接传送门

遇到的问题有以下几点:

1.编译好的APP放到手机或模拟器闪退

这个东西真的可以把你的脾气全部磨干净~~
找到的原因是:布局里面删掉了按钮,在事件里面只删除了case里面的功能,忘记删除实例化对象那部分的代码,无限闪退。
找到原因的办法:logcat大法!!!!闪退的时候帮我直接定位到了多余的那一行,我就揪出了元凶。

复现闪退情况,logcat直接给我指出来多少行出了什么错误

2.导出为APK,手机安装证书出错

也是反反复复安装失败

新建无数个证书都没用,最后发现选错了选项,如下图:

这里直接选择v1就能生成完整带加密的APK了,如果选V2,出错得你怀疑人生

]]>
Android
<![CDATA[校园网电信宽带在线受理通道]]>https://ariser.cn/index.php/archives/12/饭可以不吃,网能不上?

校园网近期逐步降速,四月十号全面断网

湖南科技大学电信宽带网上申请预订通道⇐戳

湖南科技大学使用运营商网络指南⇐戳

校园网提质服务中心⇐戳

或者扫描下列二维码:

  • 具体资费如下:

盗图的请注明出处,或者也稍微根据资费P一下啊/笑哭

各套餐详解(长按以查看)

另移动联通也能找我办理啊,详情点击博客首页(有点慢,正在检修),下方的QQ、微信、微博都能找到我哦!

]]>
Others
<![CDATA[基于Web的高校占教系统(解决方案)]]>https://ariser.cn/index.php/archives/13/

把想法说出口,和团队一起讨论、进行开发,虽然道阻且艰,但其过程妙不可言

问卷调查入口

前台:

  • 查询
    1.空教室

    • 按日期查询
    • 按教室

      1. 南北校
      2. 楼群
        c.教室号

2.教室信息
(1)可租用时间
(2)功能(多媒体、空调)
(3)容量

  • 申请
    1.预约教室

提交页面表单:
(1)姓名学号由教务网数据库自动导入
(2)联系方式
(3)活动类型
a.班级活动(团活、班会、联谊)
填入班级,联谊可填入多个班级
b.部门活动
填入部门名称
c.社团活动
填入社团名称
d.校内外培训组织
填入组织名称
e.院级、校级活动(高级用户可选)
(4)申请简介
2.使用多媒体功能,接口

  • 用户信息查询
    预约管理(查询+推选+修改)

被驳回的处理

  • 使用指南+公告
    (校院级别大型活动占用教室,及时通知占教的用户)
  • 反馈入口(BUG, 意外情况)

后台:

一、查询、修改、删除
二、审核
三、导出到excle表、方便校院分析各班团学习、生活组织活动
四、与教务网(现代教育信息对接)
五、突发情况(校院会议)的教室申请,给与用户通知
六、学院每学期前提交晚自习的情况,(早晚自习占用的教室,在教务网和公众号上查询不到,这个得加入进去)

后台(权限)
查询 申请 审核 修改 驳回 系统权限

游客 √ X X X X X

学生 √ √ X √(自己) X X

高级用户 √ √ √ √ √ X

审核用户 √ X √ √ √ X

管理员 √ √ √ √ √ √

注:
1.高级用户(辅导员及以上)可以在已申请的教室上复占
2.高级用户及管理员可以添加晚自习内容
3.审核用户,需要分配校院学习部、校自理的人员来参与承担这个任务
4."自己"是指:普通用户(学生)只能修改自己的申请记录,且学生用户一次只能申请一间教室

解决的痛点:

一、费时费力,需要占教人实地一间间教室看
二、被擦掉的占教通知,会被复占,引起误会
三、黑板、门上留下的粘胶难以处理,永久性损坏
四、黑板上的占教通知影响老师上课

解决方案:

让占教简单化,如同看一场电影,购票选座。

实现:前台:HTML(包含CSS+JS) + JSP
后台:数据库,Mysql
开发工具:Eclipse/My eclipse
运行环境:tomcat
运用MVC框架(模型+视图+控制器),这个MVC框架自行百度百科,写入到报告书

数据库模型:

教室表(教室号,容量,功能,管理员,状态)
租用时间表(编号,时间,教室号)
学生表(姓名,学号,所属院系,联系方式)
预定信息表(预订编号,周数,学号,编号)
管理员表(工号,姓名,联系方式)
状态:00空闲 01有课 10占用

安全性:

学生登陆通过教务信息系统验证,对异常提交或提交无关内容的用户进行处理

创新点:

  1. 在线预约
  2. 给校院提供学生、班团活动开展的数据
  3. 响应式+多接入点(web+微信+小程序)
  4. 早晚自习数据导入,完善旧教务查课的功能
  5. 开源(仅提供给校内开发者,技术分享+功能完善)
    ps:因为如果项目发展到校外,要拿来卖钱
  6. 结合学生兴趣爱好,进行大数据分析,智能匹配教室

项目发展:

  1. demo(以五教为例),实现占教、审核等功能
  2. 功能初步完善,开启内测,提供给校方审核,逐步完善
  3. 交由校知名网络安全团队进行安全评估
  4. 校方审核通过,整体发通知,在线占教施行,逐步成为共识
  5. 给其它高校提供占教解决方案(定制)
  6. 超级占教助手上线(类似超级课程表),并接入更多功能
  7. 结合学生兴趣爱好,进行大数据分析,智能匹配教室(使项目具有学术价值,具体方案择日开会讨论)
]]>
Others
<![CDATA[K2刷潘多拉-认证哆点Drcom]]>https://ariser.cn/index.php/archives/14/

闲来无事,把寝室的K2拿来刷潘多拉玩玩,看能不能实现路由器内部验证,实现多设备用一个校园网账号上网。
简单原理:刷入开源的PanduoraBox,里面运行python程序模拟验证过程。

声明:只作为技术研究交流,不要用于其它非法途径,这个东西学校会封禁的。

刷潘多拉

  • 给路由器刷入Breed(类似PC的BIOS)

用Breed工具一键刷入

拔掉电源->按住reset键不松-十秒左右>插上电源->地址栏输ip以进入
(192.168.1.1和.2.1都试一下,因为可能变动)

  • 进入Breed刷潘多拉固件

重要:环境变量 -> 禁用 -> 设置
再可以在固件更新里面选择panduora固件更新了
(没有设置禁用环境变量会无法重启,一直进入breed)

原因在此,breed版本原因。
在这个地方卡了好大半天

加载完毕后等待或者手动重启路由器即可

如果进入这个页面,代表潘多拉已经刷成功了!

到这个地方,我已经睡觉了,后面的步骤还没开始。第二天早上,室友连接上去就可以直接上网,不需要验证了????

有点迷,在自己电脑上验证一次之后,其他设备直接接入,无需验证。

Fing界面,接入了五台设备

  • 刷Python脚本

接着往下,安装python2.7。
菜单中的Add python.exe to Path一定要选上。

打开控制台,输入python,如果出现版本信息,代表成功安装。

停更一段落,手头任务太多了。

]]>
Others
<![CDATA[实验二 Linux内核实验]]>https://ariser.cn/index.php/archives/15/

目录

  • 准备工作

    • SSH工具控制Linux
    • 下载Liunx内核
  • 编译系统内核

    • 配置内核
    • 编译内核
    • 清除文件
  • 增加模块

准备工作

  • 虚拟机系统:CentOS5.6,配置SSH
#安装SSH:
yum install openssh-sever SSH
#服务重启:
service sshd restart 
#设置SSH为开机自启:
chkconfig sshd on 
#查看是否安装SSH:
rpm -qa | grep ssh 
#查看是否运行SSH:
/etc/init.d/sshd status

  • 用SSH工具连接
    查看IP:ifconfig (Windows下是ipconfig)


X SHELL连接界面(SSH工具还有:putty等。手机上:JuiceSSH)

  • 下载Linux内核:
    下载的时候,校园网走的是移动的线路,封掉了很多ip,速度是奇慢无比


手机热点 + IDM,速度蹭蹭得往上飙

将下载好的内核复制到了root目录下。


编译内核系统

内核版本 2.6.32

解压压缩包,到/usr/src下:tar -xvf linux-3.16.56.tar.xz -C /usr/src (注意按TAB自动补齐)
文件夹重命名为学号:mv linux-3.16.56 1601020306

安装QT,过程中出现了各种各样的错误,也换了几种方法,参考:

  • 配置内核(根据需求自定义选择模块)
    进入到内核目录,执行 make menuconfig,发现出错了


yum install -y ncurses-devel 安装了一个东西,暂时不知道是什么左右,再运行make menuconfig

这么多选项,要知道哪个需要那个不需要不是容易事。这里找到自己Linux的配置,导入进去

cd到boot文件夹下
cp到内核根目录下

选择,填写文件名,返回第一个界面,选择,命名位.config后就
(这里make xconfig是一种全图形界面的配置方式,需要用到上文安装的QT的共享库)

  • 编译内核
    漫长的过程,我就去睡觉了,第二天早上来收菜QAQ。

  • 安装内核
    编译完成,至于时间,没有记录,一觉醒来电脑都自己进入休眠了


执行make modules_install和make install

打开编辑内核启动项的配置文件:
vim /boot/grub/grub.conf,默认0开始计数,因此需将default的☞改为0即可

查看内核信息,已经是3.16.56内核了

make distclean,清除内核安装后留下的痕迹。


添加模块

cd到lib/modules/(任意目录即可)
自定义的hello模块

创建了模块hello以及对应的makefile

这个过程持续了一个小时多,改了无数遍Makefile,各种错误,无果,暂时还在解决,最后的运行结果以及解决方案会更新到博客里面。

]]>
Linux
<![CDATA[Java - 初试]]>https://ariser.cn/index.php/archives/16/

Java开发环境配置

  • 下载安装JDK
    oracle官网,找到对应版本JDK下载,传送门

  • 配置环境变量
    右键计算机 -> 属性 -> 高级系统设置 -> 环境变量


新建一个变量名为“JAVA_HOME”的系统变量,变量值为“E:DevelopmentJavajdk1.8.0_92”(jdk的安装目录,根据个人所安装的目录修改)

再新建一个变量名为“CLASSPATH”,变量值为“.;%JAVA_HOME%libdt.jar;%JAVA_HOME%libtools.jar;”的系统变量,注意前面的点号和分号都是有的

打开“Path”系统变量,点击新建,添加“%JAVA_HOME%bin”和“%JAVA_HOME%jrebin”两个系统变量。Path使得系统可以在任何路径下识别java命令

验证是否配置好环境:window+R输入cmd,打开控制台或者开始-运行
分别输入java和java -version和javac,都正常运行即代表java已经正确安装,其中 java -version代表你安装的java的版本 。
如果出现类似"javac不是内部或外部命令"等提示语句,请再三检查你的环境变量是否正确配置,或者JDK和JRE安装目录是否重复,如果重复,可以选择再次运行下载的jdk安装程序重新安装。

  • 安装编译器
    安装Eclipse编译器,开始Java编程。

冒泡排序

老师留下的作业是用Java实现冒泡排序

核心算法为:外层循环遍历数组 -> 内层循环进行交换(小的在前大的在后)

实现代码:

for(int i=1; i&lt;=b.length; i++)
{
    for(int j=1; j&lt;=b.length-i; j++) { if(b[j-1] &gt; b[j]) {
        int x = b[j-1];
        b[j-1] = b[j];
        b[j] = x;
        }
    }
}

整个Java代码片段为:

程序优化升级

  1. 加入输入功能
    在第一个版本的基础上,结合网上搜到的知识,实现了输入任意数量的数字进行排序:


思路: 首先输入字符串的数组,中间用空格分隔,然后获取字符串长度,作为int[] 初始数组的长度,再将String字符串数组转化为int数组。

Eclipse运行结果如图,结合了上述方法,实现了任意长度数组的冒泡排序(长按图片以查看)

  1. 封装到类
    结合学习的面向对象知识,进一步改造:
]]>
Java
<![CDATA[实验一 Linux系统安装及操作]]>https://ariser.cn/index.php/archives/17/

目录

  • 虚拟机安装
  • Linux Mint镜像下载及安装
  • Linux Mint系统的基本操作及任务完成(C代码的编译)

虚拟机安装

平时使用虚拟机比较多,用于学习网络Web知识、Kali渗透测试、网站搭建、测试木马病毒以及体验一些镜像(凤凰OS、MacOS、Ubtun等)。
版本:Vmware workstation 12 pro,使用的注册机激活的。

安装过程在此不作赘述,另外分享几点心得:

  1. Vmware开机是自动启动相关服务,这里可以在服务里面设置为手动,在使用Vmware的时候手动在任务管理器里面开启,以避免浪费系统资源。
    任务管理器
  2. 所安装的Window10系统是Insider preview。预览版由于自动开启了Hyper-V(微软自带的虚拟机产品),导致Vmware出现“VMware Workstation and Hyper-V are notcompatible. Remove the Hyper-V role from the system before running VMwareWorkstation.”的报错,这里的话,到程序和面板设置关闭Hyper-V服务。
    二者共存的方法

Linux Mint镜像下载及安装

进入到Linux Mint的官网,下载页面有三种版本,百度了一下是其桌面系统的区别,这里选择第一种Cinnamon的64位。
Linux Mint官网
官网给的下载地址来自“清华大学开源软件镜像站”
分配了双核2G
安装虚拟机工具的时候,挂载镜像的时候发现没有操作权限,终端显示的$证明不是超级用户,于是:

#卸载桌面上的工具镜像
umount /dev/sr0
#挂载到media
mount /dev/sr0 /media
#进入到media文件夹 ls 查看文件 tar -zxvf解压文件到/root文件夹
cd /media


重启后,分辨率变得很舒服了。


Linux Mint系统的基本操作及任务完成(C代码的编译)

终端安装ATOM编译器的过程比较漫长,在安装的过程中用vim编辑了C文件:

#vim编辑器常见命令
a: (光标后)插入 A: 行尾插入
O: 上一行插入 u: 恢复
i: (光标前)插入 R: 行首插入
p: 粘贴 dd: 剪切当前行(2dd是剪切两行)
Esc退出后输入冒号,q: 退出,w: 保存

gcc命令,编译链接得到exe文件,执行,得到任务结果

中间碰到的一些其它问题会单独写到其它文章。

初学Linux,建议建立一个记事本,把Linux的命令都记录起来,每天训练,达到如同操作图形界面一样熟练。

]]>
Linux