PC使用已越狱iOS设备连接的VPN网络的方法

背景介绍

iOS上有很多免费VPN软件,比如天行VPN,然而这些软件目前都没有针对PC端推出免费客户端。
本文通过SSH动态端口转发,将iOS设备的VPN“共享”到PC端使用。
理论上,Android只要有支持动态端口转发的SSH服务端,应该也可以使用。

准备工作

  • 已越狱iOS设备一台(越狱是为了安装OpenSSH),本文用iPhone4S,安装好天行VPN(AppStore中免费下载),OpenSSH(Cydia中免费下载)。
  • PC端安装SecureCRT终端神器用于端口转发(百度搜索可以下载到)。
  • Chrome或者其他浏览器,搭配代理插件,本文使用Chrome+SwitchyOmega。

操作步骤

  • iPhone连接VPN
    • iPhone中打开天行VPN(或者其他VPN软件),连接上,中途如果提示要安装描述文件,允许即可。
    • 连接成功后,状态栏会有VPN图标。
      IMG_1085
    • 可以打开Safari看是否可以打开需要访问的网站,比如Google。
      IMG_1086
  • PC使用SecureCRT连接iPhone
    • 打开SecureCRT,输入iOS设备的IP和端口22,用户名root,默认密码alpine登录。
    • 此处强烈建议修改默认密码,方法是输入passwd后连续输入2次新密码,完成后还要修改默认账户mobile的密码,输入passwd mobile后连续输入2次新密码。不这样做的话,局域网中的任何人都可能能够入侵你的设备。

  • 创建动态端口转发
    • 右键连接的标题,进入Session Options设置框,在左侧设置区找到Port Forwarding(端口转发),点击Add按钮添加端口转发:
      • Name:名称,随便填。
      • Port:填一个不常使用的端口即可,比如8080。
      • 最重要的是,勾选下面的Dynamic forwarding using SOCKS 4 or 5。
      • 完成后点击OK即可,这样就把iOS设备的8080端口映射到本机的8080端口,并且通过这个端口出去的数据都会发送到iOS设备的8080端口,从iOS的网络出去,回包也会从iOS的网络中发回来,从而达到代理的效果。

  • 配置Chome代理
    • 此处使用Chome的SwitchyOmega插件作为演示,不过插件需要翻墙才能下载,也可以直接使用IE配置代理,后文介绍方法。
    • 打开SwitchyOmega选项,新建情景模式,按照下图填写代理设置,端口就是刚刚在SecureCRT中填写的端口。
    • 启用这个代理,打开http://www.ip38.com看看IP是否改变了,也有可能打不开网页,这种情况可能是代理没配置正确,正常情况如下图所示:

      尝试打开Google和Facebook,一切OK:

  • 为代理增加pac文件
    • 目前Chrome浏览器中访问的所有网页都会经过我们的代理,但是国内的网页并不需要通过VPN访问,于是需要一个pac配置文件对站点进行分流,只有部分国外的站点需要通过VPN访问,配置方法如下:
      • 打开SwitchyOmega配置,新建情景模式,选择Pac情景模式:

        在其中把下面的Pac文件内容粘贴上去,这个文件是从goagent中提取出来的。
        proxy
        注意其中有2处需要修改为本机的端口,如果修改后不能访问,尝试将SOCKS5修改为SOCKS
        var autoproxy = ‘SOCKS5 127.0.0.1:8080’;
        return ‘SOCKS5 127.0.0.1:8080’;

  • Windows全局代理配置
    如果没安装Chrome或者没安装SwitchyOmega插件,可以配置全局系统代理,Windows7以及之前的系统在Internet选项中配置Pac文件路径。
    Windows8和Windows10配置方法如下:
    找到设置-网络和Internet,选择代理,勾选“使用安装程序脚本”,将代理pac文件路径填进去,这个路径可以通过将文件拖入IE中,复制地址栏获得,比如我的是:file:///D:/Program%20Files%20(x86)/%E7%BF%BB%E5%A2%99/goagent-goagent/local/proxy.pac。
    完成后点击保存即可。

  • 验证
    完成之后启用,尝试打开Googleip38,如果Google能打开并且ip38显示为本机IP地址,即表示成功。

C++11多线程——单生产者多消费者模型

背景简介

单生产者多消费者模型适用于生产数据无需占用太多CPU,而消费数据需要大量CPU运算并且计算任务没有冲突的场景,将消费者计算任务分配到多核中加速运算。

示例目的

模拟实际多线程中单生产者多消费者,实现多线程模型,充分利用多核CPU特性进行并行计算。

设计要点

  1. 一个生产者产生一个数字,多个消费者处理这个数字(将其值加到公共的Sum中)。
  2. 消费者处理需要模拟一定的延时。
  3. 消费者获得生产者生产的数据,需要对数据加锁,防止其他消费者同时计算。
  4. 消费者取走数据后要通知生产者生产新的数据。
  5. 目前实现的是长度为1的队列,存储在CurrValue中,即生产者产出数据只有1个。当值为0时表示队列空,生产者可以工作;当值不为0时表示数据内容,消费者可以取出数据,消费者在取出数据后,需要将其置为0表示数据已取出。
  6. 代码使用GTest框架运行,可以将TEST(CppThreadTest, ConditionVariable)改成main函数并去除相应的校验直接执行。

代码示例(含注释)

代码实现过程以及每行的详细注释。

内部机制解析

  1. 为什么要条件变量?使用其他方法可以吗?
    这里使用条件变量检测条件并不是必须的,主要是为了解决以下方法的缺陷:

    1. 在消费者线程中通过while的方式检测,这样会导致CPU忙检测。
    2. 在消费这线程中通过while的方式检测,在数据未准备好时,通过usleep延时一段时间,比如100ms,虽然解决了CPU忙的问题,但是这样会导致部分数据处理时间过长,延时过大。
  2. 条件变量调用的wait内部做了什么?
    调用条件变量的wait的线程需要先获得lock,内部会对锁进行unlock使得其他并行线程也进入到wait中,然后进入阻塞状态,直到收到notify通知,wait返回的线程会获得锁,但是并不一定会达到可用的条件,比如上例中消费者在处理完数据解锁后,锁被其他消费者抢走,此时生产者并没有生产好数据,此处就需要条件变量的第二个参数来判断条件是否成立,如果不成立,则重新回到wait等待中,在打开代码中所有流程输出语句后,可以看到下面的情况。
    生产19997———–
    线程15抢到锁
    线程15即将wait,释放锁
    线程15wait后,value=19997
    线程15通知生产者
    线程15释放锁,处理19997
    生产19998———–
    线程16抢到锁
    线程16即将wait,释放锁
    线程16wait后,value=19998
    线程16通知生产者
    线程16释放锁,处理19998
    线程0抢到锁
    线程0即将wait,释放锁
    线程8抢到锁
    线程8即将wait,释放锁
    生产19999———–
    线程0wait后,value=19999
    线程0通知生产者
    线程0释放锁,处理19999
    解析一下:

    1. 在线程16释放锁后,通知生产者,生产者并没有被唤醒,而被线程0抢到了锁。
    2. 线程0抢到锁之后,进行wait,wait对锁进行unlock,被线程8抢到锁,线程8进入wait状态后,继续释放锁,最终被生产者线程抢到锁,继续生产。
  3. wait还有一个重载可以不用传入第二个判断条件成立的参数,但是此时需要在wait外部进行判断:
  4. 在生产者最后完成所有生产任务,置Stop为true后,需要调用GetCV.notify_all();通知所有的消费者线程,不然可能会有部分消费者线程阻塞在wait中永远出不来。在消费者调用wait,拿到锁后,需要判断退出条件,因为最后生产者置退出标志位后通知所有线程,此时数据并未准备好,但是Stop标志位已经表示可以退出,此时消费者线程需要做退出操作。

待解疑问

  1. 目前GetCV和SetCV使用同一个Mutex,是否需要分别使用?
    楼主尝试过使用不同的Mutex,但是会被锁住。
  2. notify和unlock的调用顺序问题,谁先谁后?有什么区别?
    维基百科条件变量词条中描述如下,目前没有一个示例能看出区别,需要补充:

    即将离开临界区的线程是先释放互斥锁还是先notify操作解除在条件变量上挂起线程的阻塞?表面看两种顺序都可以。但一般建议是先notify操作,后对互斥锁解锁。因为这既有利于上述的公平性,同时还避免了相反顺序时可能的优先级倒置。这种先notify后解锁的做法是悲观的(pessimization),因为被通知(notified)线程将立即被阻塞,等待通知(notifying)线程释放互斥锁。很多实现(特别是pthreads的很多实现)为了避免这种“匆忙与等待”(hurry up and wait)情形,把在条件变量的线程队列上处于等待的被通知线程直接移到互斥锁的线程队列上,而不唤醒这些线程。

参考资料