1.命令行界面

命令行界面(英语:Command-lineInterface,缩写:CLI)是在图形用户界面得到普及之前使用最为广泛的用户界面,它通常不支持鼠标,用户通过键盘输入指令,计算机接收到指令后,予以执行。

2.终端

在大型机 (Mainframe) 和小型机 (Minicomputer)的时代里,计算机曾经非常昂贵且巨大,不像现在这样人手一台。这些笨重的计算机通常被安置在单独的房间内,而操作计算机的人们坐在另外的房间里,通过某些设备与计算机进行交互。这种设备就叫做 终端 (Terminal),也叫终端机。

2.1控制台(console)

历史上,终端是链接到计算机的一种带输入输出功能的外设。
但是有一种终端于总不同,它和计算机是一体的,是计算机的一部分。这个特殊的终端就是 控制台(console)。

2.2终端模拟器 (Terminal Emulator)

随着计算机的进化,我们已经见不到专门的终端硬件了,取而代之的则是键盘与显示器。

但是没有了终端,我们要怎么与那些传统的、不兼容图形接口的命令行程序(比如说 GNU 工具集里的大部分命令)交互呢?这些程序并不能直接读取我们的键盘输入,也没办法把计算结果显示在我们的显示器上……(图形界面的原理我这里就不多说了,它们编程的时候图形接口还在娘胎里呢!)

这时候我们就需要一个程序来模拟传统终端的行为,即 终端模拟器 (Terminal Emulator)。

严格来讲,Terminal Emulator 的译名应该是「终端仿真器」。
对于那些命令行 (CLI) 程序,终端模拟器会「假装」成一个传统终端设备;而对于现代的图形接口,终端模拟器会「假装」成一个 GUI 程序。一个终端模拟器的标准工作流程是这样的:

捕获你的键盘输入;
将输入发送给命令行程序(程序会认为这是从一个真正的终端设备输入的);
拿到命令行程序的输出结果(STDOUT 以及 STDERR);
调用图形接口(比如 X11),将输出结果渲染至显示器。
终端模拟器有很多,这里就举几个经典的例子:

GNU/Linux:gnome-terminal、Konsole;
macOS:Terminal.app、iTerm2;
Windows:Win32 控制台、ConEmu 等。

在专门的终端硬件已经基本上仅存于计算机博物馆的现代,人们通常图省事儿,直接称呼终端模拟器为「终端」。

2.3.TTY

简单说,tty就是终端的总称。

最早的 Unix 终端是 ASR-33 电传打字机。而电传打字机 (Teletype / Teletypewriter) 的英文缩写就是 tty,即 tty 这个名称的来源。
由于 Unix 被设计为一个多用户操作系统,所以人们会在计算机上连接多个终端(在当时,这些终端全都是电传打字机)。Unix 系统为了支持这些电传打字机,就设计了名为 tty 的子系统(没错,因为当时的终端全都是 tty,所以这个系统也被命名为了 tty,就是这么简单粗暴),将具体的硬件设备抽象为操作系统内部位于 /dev/tty* 的设备文件。

3. Shell —— 提供用户界面的程序

大家都知道,操作系统有一个叫做 内核 (Kernel) 的东西,它管理着整台计算机的硬件,是现代操作系统中最基本的部分。但是,内核处于系统的底层,是不能让普通用户随意操作的,不然一个不小心系统就崩溃啦!

但我们总还是要让用户操作系统的,怎么办呢?这就需要一个专门的程序,它接受用户输入的命令,然后帮我们与内核沟通,最后让内核完成我们的任务。这个提供用户界面的程序被叫做 Shell (壳层)。

其实 Shell 只是提供了一个用户操作系统的入口,我们一般是通过 Shell 去调用其他各种各样的应用程序,最后来达成我们的目的。比如说我们想要知道一个文件的内容,我们会在 Shell 中输入命令 cat foo.txt,然后 Shell 会帮我们运行 cat 这个程序,cat 再去调用内核提供的 open 等系统调用来获取文件的内容。虽然并不是 Shell 直接去与内核交互,但广义上可以认为是 Shell 提供了与内核交互的用户界面。

{

Shell 通常可以分为两种:
命令行 Shell 与 图形 Shell。顾名思义,前者提供一个命令行界面 (CLI),后者提供一个图形用户界面 (GUI)。Windows 下的 explorer.exe 就是一个典型的图形 Shell(没错,它确实是,因为它接受来自你的指令,并且会帮你与内核交互完成你的指令)。

常见或历史上知名的命令行 Shell 有:
适用于 Unix 及类 Unix 系统:
sh (Bourne shell),最经典的 Unix shell;
bash (Bourne-Again shell),目前绝大多数 Linux 发行版的默认 shell;
zsh (Z shell),我个人最喜欢的 shell;
fish (Friendly interactive shell),专注于易用性与友好用户体验的 shell;
Windows 下的 cmd.exe (命令提示符) 与 PowerShell。
还有其他各种五花八门的 Shell

4.shell与终端的分工

现在我们知道,终端干的活儿是从用户这里接收输入(键盘、鼠标等输入设备),扔给 Shell,然后把 Shell 返回的结果展示给用户(比如通过显示器)。而 Shell 干的活儿是从终端那里拿到用户输入的命令,解析后交给操作系统内核去执行,并把执行结果返回给终端。

不过 Shell 与终端的分工有一些容易混淆的地方,这里以例子进行说明:

  • 终端将用户的键盘输入转换为控制序列(除了字符以外的按键,比如 左方向键 → ^[[D),Shell 则解析并执行收到的控制序列(比如 ^[[D → 将光标向左移动);
  • 不过也有例外,比如终端在接收到 Ctrl + C 组合键时,不会把这个按键转发给当前的程序,而是会发送一个 SIGINT 信号(默认情况下,这会导致进程终止)。其他类似的特殊组合键有 Ctrl-Z 与 Ctrl- 等,可以通过 stty -a 命令查看当前终端的设置。
  • Shell 发出类似「把前景色改为红色(控制序列为 033[31m)」「显示 foo」等指令;
  • 终端接收这些指令,并且照着 Shell 说的做,于是你就看到了终端上输出了一行红色的 foo。
  • 除非被重定向,否则 Shell 永远不会知道它所执行命令的输出结果。我们可以在终端窗口中上下翻页查看过去的输出内容,这完全是终端提供的 feature,与 Shell 没有半毛钱关系;
  • 命令提示符 (Prompt) 是一个完全的 Shell 概念,与终端无关;
  • 行编辑、输入历史与自动补全等功能是由 Shell 提供的(比如 fish 这个 Shell 就有着很好用的历史命令与命令自动补全功能)。不过终端也能自己实现这些功能,比如说 XShell 这个终端模拟器就可以在本地写完一行命令,然后整条发送给远程服务器中的 Shell(在连接状况不佳时很有用,不然打个字都要卡半天);
  • 终端中的复制粘贴功能(Shift + Insert 或者鼠标右键等)基本上都是由终端提供的。举个例子,Windows 默认的终端对于复制粘贴的支持很屎,而换一个终端(例如 ConEmu)后就可以很好地支持复制粘贴。不过 Shell 以及其他命令行程序也可以提供自己的复制粘贴机制(例如 vim)。

使用iptables设定特定端口连接数(万能方法)
限制端口连接数量
首先输入命令service iptables stop关闭iptables(注意:iptables可能会有问题,貌似在旧版本中不被认为是一个服务,而是防火墙,这个问题我还没有解决,如果你解决了请告诉我一声,谢谢)
限制端口并发数很简单,IPTABLES就能搞定了,假设你要限制端口8388的IP最大连接数为5,两句话命令:

iptables -I INPUT -p tcp --dport 8388 -m connlimit --connlimit-above 5 -j DROP
iptables -I OUTPUT -p tcp --dport 8388 -m connlimit --connlimit-above 5 -j DROP

我再举个例子,比如你想限制从1024-10240的端口

iptables -I INPUT -p tcp --dport 1024:10240 -m connlimit --connlimit-above 5 -j DROP
iptables -I OUTPUT -p tcp --dport 1024:10240 -m connlimit --connlimit-above 5 -j DROP

保存IPTABLES规则即可(service iptables save),其他端口以此类推。
输入命令service iptables start启动
最后用命令查看是否生效

iptables -L -n -v

限制端口速度
首先输入命令service iptables stop关闭iptables
限制端口并发数很简单,IPTABLES就能搞定了,假设你要限制端口5037的最大连接速度为60个包每秒,两句话命令:

iptables -A INPUT -p tcp --sport 5037 -m limit --limit 60/s -j ACCEPT
iptables -A INPUT -p tcp --sport 5037 -j DROP

也就是限制每秒接受60个包,一般来说每个包大小为64—1518字节(Byte)。
限制指定ip的访问速度
原理:每秒对特定端口进行速度控制,比如每秒超过10个的数据包直接DROP,从而限制特定端口的速度

iptables -A FORWARD -m limit -d 208.8.14.53 --limit 700/s --limit-burst 100 -j ACCEPT 
iptables -A FORWARD -d 208.8.14.53 -j DROP

最后说一下如何解决防火墙重启后失败的问题

iptables-save >/etc/sysconfig/iptables
echo 'iptables-restore /etc/sysconfig/iptables' >> /etc/rc.local
chmod +x /etc/rc.d/rc.local

对于ssr客户端
限制设备连接数
打开你的配置文件,假设你在 /root 文件夹中安装的ShadowsocksR服务端,那么就是:

vi /root/shadowsocksr/user-config.json

找到协议参数(参数为空 "" 时,默认限制 64个设备数)

"protocol_param": "",

在协议参数中设置你要限制 每个端口最大设备连接数(建议最少2个),比如 限制最大 5个设备同时链接,那么改为:

"protocol_param": "5",

注意:协议参数仅在服务端 协议设置(protocol)为 非原版(origin)协议并不兼容原版(_compatible) 时才有效!
限制端口速度
还是上文的那个地方,第一个是单线程限制,另外一个是总限制

"speed_limit_per_con": 0,
"speed_limit_per_user": 0,

当你使用这个端口 下载某个文件时,单线程下载限速 100KB/S ,多线程下载(比如5个线程)就是 500KB/S 了,Youtube是单线程。

<?php
$spider = new SpiderExample();
///question/267782048
$result = $spider->getData('https://www.zhihu.com/question/267782048/answer/330283932', [], $method = 'post');
var_dump($result);

/**
 * Class SpiderExample
 */
class SpiderExample
{

    private $cookie;

    /**
     * SpiderExample constructor.
     * @param string $cookieFile
     */
    public function __construct($cookieFile = './tmp')
    {
        header("Content-type: text/html; charset=utf-8");
        set_time_limit(0);
        //设置cookie文件
        $this->cookie = tempnam('./tmp', 'cookieSpiderExample' . date('Y-m-d'));
    }

    /**
     * 需要登录的先登录
     * @param $url
     * @param $postData
     * @param string $method
     * @return array|bool
     */
    public function login($url, $postData, $method = 'POST')
    {
        return $this->query(['url' => $url, 'data' => $postData], true, $method);
    }

    /**
     * 获取数据
     * @param $url
     * @param $postData
     * @param string $method
     * @return array|bool
     */

    public function getData($url, $postData, $method = 'POST')
    {
        return $this->query(['url' => $url, 'data' => $postData], false, $method);
    }

    /**
     * @param array postData
     * [
     *      'url'=>'',//链接
     *      'data'=>''//数据
     * ]
     * @param bool type 是否是登录请求
     * @param string method POST请求/get
     * @return array|bool
     */
    private function query($postData, $type = false, $method = 'POST')
    {
        $curl = curl_init();
        if ($type)
            $cookie_type = CURLOPT_COOKIEJAR;
        else
            $cookie_type = CURLOPT_COOKIEFILE;

        curl_setopt_array($curl, array(
            CURLOPT_URL => $postData['url'],
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => "",
            CURLOPT_MAXREDIRS => 10,
            $cookie_type => $this->cookie,
            CURLOPT_TIMEOUT => 120,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => $method,
            //是否验证https
            CURLOPT_SSL_VERIFYHOST => 0,
            CURLOPT_SSL_VERIFYPEER => 0,
            //是否跟随跳转
            CURLOPT_FOLLOWLOCATION=>true,
            CURLOPT_POSTFIELDS => http_build_query($postData['data']),
            CURLOPT_HTTPHEADER => array(
                "Cache-Control: no-cache",
                "Content-Type: application/x-www-form-urlencoded",
            ),
        ));
        $response = curl_exec($curl);
        $err = curl_error($curl);
        curl_close($curl);
        if ($err) {
            return false;
        } else {
            return ['code' => 0, 'data' => $response];
        }
    }

}

/**
 * 获取阶乘
 * @param int $n
 * @return float|int
 */
function factorial(int $n):int
{
    return array_product(range(1, $n));
}

/**
 * 排列数
 * @param int $n 数组长度
 * @param int $m 排列长度
 * @return float|int
 */
function A($n, $m)
{
    return factorial($n) / factorial($n - $m);
}

/**
 * 组合数
 * @param int $n 数组长度
 * @param int $m 排列长度
 * @return float|int
 */
function C($n, $m)
{
    return A($n, $m) / factorial($m);
}

/**
 * 获取组合
 * @param array $a 一个有序数组
 * @param int $m 组合长度
 * @return array
 */
function combination(array $a, int $m): array
{
    //定义空返回结果
    $r = [];
    //数组长度
    $n = count($a);
    //m 只能是小于数组长度的正整数
    if ($m <= 0 || $m > $n) {
        return $r;
    }
    //循环
    for ($i = 0; $i < $n; $i++) {
        //获取一个初始数
        $t = [$a[$i]];
        if ($m == 1) {
            //长度为1时直接给结果赋值
            $r[] = $t;
        } else {
            //取出初始值后面的数组
            $b = array_slice($a, $i + 1);
            //递归
            $c = combination($b, $m - 1);
            foreach ($c as $v) {
                //array_merge() 函数把一个或多个数组合并为一个数组
                $r[] = array_merge($t, $v);
            }
        }
    }

    return $r;
}

/**
 *
 * 全排列
 * @param array $a 一个数组
 * @param int $m 组合长度
 * @return array
 */
function arrangement($a, $m)
{
    $r = array();
    $n = count($a);
    if ($m <= 0 || $m > $n) {
        return $r;
    }
    for ($i = 0; $i < $n; $i++) {
        $b = $a;
        //取出$t初始值 $b为剔除初始数的数组
        $t = array_splice($b, $i, 1);
        if ($m == 1) {
            $r[] = $t;
        } else {
            $c = arrangement($b, $m - 1);
            //遍历合并
            foreach ($c as $v) {
                $r[] = array_merge($t, $v);
            }
        }
    }

    return $r;
}


$res = arrangement(range(1, 3), 2);
var_dump($res);
die;

五位的大概有3000万种可能 七位有几百亿可能,虽然重复概率很低,大流量商用建议继续优化

<?php
/**
 * 短链接类
 */
class urlManager {

    //前缀
    public static $prefix = 'weiyixi';

    //后缀
    public static $suffix = 'ixiyiwei';

    //
    public static $index = 0 ;

    //32个基础字符
    public static $base_key = array (
        "a" , "b" , "c" , "d" , "e" , "f" , "g" , "h" ,
        "i" , "j" , "k" , "l" , "m" , "n" , "o" , "p" , "q" , "r" , "s" , "t" ,
        "u" , "v" , "w" , "x" , "y" , "z" , "0" , "1" , "2" , "3" , "4" , "5" ,
        "6" , "7" , "8" , "9" , "A" , "B" , "C" , "D" , "E" , "F" , "G" , "H" ,
        "I" , "J" , "K" , "L" , "M" , "N" , "O" , "P" , "Q" , "R" , "S" , "T" ,
        "U" , "V" , "W" , "X" , "Y" , "Z", "-", "_");

    /**
     * 创建短链接 - 5 位
     *
    网上摘录方法:

    1)将长网址md5生成32位签名串,分为4段, 每段8个字节;

       2)对这四段循环处理, 取8个字节, 将他看成16进制串与0x3fffffff(30位1)与操作, 即超过30位的忽略处理;

       3)这30位分成6段, 每5位的数字作为字母表的索引取得特定字符, 依次进行获得6位字符串;

       4)总的md5串可以获得4个6位串; 取里面的任意一个就可作为这个长url的短url地址;
     *
     */
    public static function create5($long_url,$k=0){
        $out='';
        if($k>3) return;

        $hex = md5(self::$prefix.$long_url.self::$suffix);
        $subhex = substr($hex, $k * 8, 8);

        $int = hexdec($subhex);

        for($i=0; $i<5; $i++)
        {
            $index = $int & 0x3f;
            //最后一位如果是“-”, 则替换为a,避免部分手机上链接显示错误的问题
            if($i == 4 && $index == 62)
                $index = 0;
            $out .= self::$base_key[$index];
            $int = $int >> 6;
        }

        return $out;
    }

    /**
     * 创建短链接 - 7 位
     *
    网上摘录方法:

    1)将长网址md5生成32位签名串,分为3段, 每段42位;

       2)这42位分成7段, 每6位的数字作为字母表的索引取得特定字符, 依次进行获得7位字符串;

     *
     */
    public static function create7($long_url,$k=0){
        $out='';
        $hex = md5(self::$prefix.$long_url.self::$suffix);
        switch($k)
        {
            case 0:
                $subhex = substr($hex, 21, 11); //最后42位
                break;

            case 1:
                $subhex = substr($hex, 11, 11);    //中间42位
                break;

            case 2:
                $subhex = substr($hex, 0, 11);    //最前面的42位,最前2位抛弃
                break;

            default:
                return;
        }

        $int = hexdec($subhex);
        if($k==1)
            $int = $int >> 2; //此处最后2位已被$k=0时所用,故抛弃

        for($i=0; $i<7; $i++)
        {
            $index = $int & 0x3f;
            //最后一位如果是“-”, 则替换为a,避免部分手机上链接显示错误的问题
            if($i == 6 && $index == 62)
                $index = 0;
            $out .= self::$base_key[$index];
            $int = $int >> 6;
        }

        return $out;
    }
}