情况是这样的,vpn拨号成功后,qq网站可以打开,但是百度打不开
一直停在"正在打开网页"

这里问题就是mtu没设置,

iptables -A FORWARD -p tcp --syn -s 172.16.0.0/24 -j TCPMSS --set-mss 1356;

这样设置后,重新拨vpn,可以正常打开所有网页。
1356这个值怎么得出来的

在服务器上用netstat -i查看接口,得到

Iface   MTU Met    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0   1500   0 102528561      0      0      0 194391413      0      0      0 BRU
eth1   1500   0 519820535    954 11553    924 208798037      0      0      0 BRU
lo    16436   0   151062      0      0      0   151062      0      0      0 LRU
ppp0   1396   0       19      0      0      0        8      0      0      0 OPRU 

可知ppp的最大mtu为1396,当然,对应的mss应为(mtu-20字节的IP头部+20字节的TCP 头部=)1356 【小知识1】计算机网络中的MSS:
MSS: Maximum Segment Size 最大分段大小
MSS最大传输大小的缩写,是TCP协议里面的一个概念。
MSS就是TCP数据包每次能够传输的最大数据分段。为了达到最佳的传输效能,TCP协议在建立连接的时候通常要协商双方的MSS值,这个值TCP协议在 实现的时候往往用MTU值代替(需要减去IP数据包包头的大小20Bytes和TCP数据段的包头20Bytes)所以往往MSS为1460。通讯双方会 根据双方提供的MSS值得最小值确定为这次连接的最大MSS值。
MTU就是最大传输单元(英语:Maximum Transmission Unit,缩写MTU)是指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为单位)。超过这个值就要分包传输,分包就有可能丢包,网络传输就会失败。

平时路由器里也有个mtu的设置,一般默认是1500,其实我们也可以修改成不需要分包的最大值。
首先要检测网关的MTU在电脑端打开dos窗口,执行:

ping -f -l 1472 192.168.0.1 

其中192.168.0.1是网关IP地址),1472是数据包的长度。请注意,上面的参数是“-l”(小写的L),而不是“-1”。 如果能ping通,表示数据包不需要拆包,可以通过网关发送出去。
如果出现: Packet needs to be fragmented but DF set. 或中文提示,表示数据包需要拆开来发送。此时,减少数据包长度,再执行上面的ping命令。从1400到1472之间多试几次,就能找到合适的数据包长度了。数值在可用的情况下尽量取大值。

注意:把数据包正常的长度加上数据包头28字节,就得到可以修改的MTU值。比如:测的1400,要加上28后得到1428,把1428填到路由器里面。如果检测到网关的MTU值是1500,不需要修改。

(1) 安装
Ubuntu安装ecryptfs:

sudo apt-get install ecryptfs-utils

Centos安装

sudo yum install ecryptfs-utils

为了稳妥起见,安装程序包后,装入eCryptFS内核模块是个好的做法:

sudo modprobe ecryptfs

(2)创建加密目录并挂载
假设你加密后存储的文件目录是~/data_secret,而解密后使用的目录是~/data。先创建好这两个目录,然后把加密目录挂载到解密目录:

user@localhost:~$ sudo mount -t ecryptfs ~/data_secret ~/data
[sudo] password for user:(管理员密码)
Passphrase:(加密密码,一定要记住)
Select cipher:
1) aes: blocksize = 16; min keysize = 16; max keysize = 32 (loaded)
2) blowfish: blocksize = 16; min keysize = 16; max keysize = 56 (not loaded)
3) des3_ede: blocksize = 8; min keysize = 24; max keysize = 24 (not loaded)
4) cast6: blocksize = 16; min keysize = 16; max keysize = 32 (not loaded)
5) cast5: blocksize = 8; min keysize = 5; max keysize = 16 (not loaded)
Selection [aes]:
Select key bytes:
1) 16
2) 32
3) 24
Selection [16]:
Enable plaintext passthrough (y/n) [n]:
Enable filename encryption (y/n) [n]:y(允许文件名加密)
Filename Encryption Key (FNEK) Signature [aaaa222233331234(记住,以后会用到)]:
Attempting to mount with the following options:
  ecryptfs_unlink_sigs
  ecryptfs_fnek_sig=aaaa222233331234
  ecryptfs_key_bytes=16
  ecryptfs_cipher=aes
  ecryptfs_sig=aaaa222233331234
WARNING: Based on the contents of [/root/.ecryptfs/sig-cache.txt],
it looks like you have never mounted with this key
before. This could mean that you have typed your
passphrase wrong.

Would you like to proceed with the mount (yes/no)? :yes(第一次使用的密码会有这个提示,输入yes继续)
Would you like to append sig [aaaa222233331234] to
[/root/.ecryptfs/sig-cache.txt]
in order to avoid this warning in the future (yes/no)? :yes(记住这个密码标识,以后不再提示)
Successfully appended new sig to user sig cache file
Mounted eCryptfs

(3)文件操作
加密目录~/data_secret被解密并挂载到~/data,我们可以在~/data下进行普通文件操作:

user@localhost:~/data$ echo "test" > test
user@localhost:~/data$ ls
test
user@localhost:~/data$ cat test
test

(4) 卸载加密目录
使用命令卸载加密目录:

sudo umount ~/data

卸载后~/data为空,而 ~/data_secret则显示乱码(对应加密后的test文件):

user@localhost:~$ ls data
user@localhost:~$ ls data_secret/
ECRYPTFS_FNEK_ENCRYPTED.FWb9phlX-ctK-UTNypKqMObbsrr32asfab1dl2LDDBryfZQ7xaRV.R0dJBavE--
user@localhost:~$ cat data_secret/FWb9phlX-ctK-UTNypKqMObbsrr32asfab1dl2LDDBryfZQ7xaRV.R0dJBavE--(一堆乱码)

(5)快速挂载
以后每次开机后访问加密数据前都需要重新挂载,你可以使用sudo mount -t ecryptfs ~/data_secret ~/data命令来挂载,但每次都要手工输入一堆选项也挺烦人的。为了以后方便挂载,在~/.bashrc中加入别名:

alias mount_data="sudo mount -t ecryptfs $HOME/data_secret $HOME/data -o ecryptfs_cipher=aes,ecryptfs_key_bytes=16,ecryptfs_enable_filename_crypto=y,ecryptfs_passthrough=n,ecryptfs_fnek_sig=aaaa222233331234(需从前面记录)"

以后每次使用前只需要执行mount_data即可:

user@localhost:~$ mount_data
[sudo] password for user:(管理员密码)
Passphrase:(加密密码)
Attempting to mount with the following options:
ecryptfs_unlink_sigs
ecryptfs_fnek_sig=aaaa222233331234
ecryptfs_key_bytes=16
ecryptfs_cipher=aes
ecryptfs_sig=aaaa222233331234
Mounted eCryptfs
user@localhost:~$ cat data/test
test

(6) 重装系统或移动数据
你只需要记住加密密码和ecryptfs_fnek_sig参数,则即使你要重装系统或移动数据,也可以用同样的命令对数据进行解密并挂载。

谷歌支付文档

以上是谷歌支付 Google Play In-app Billing 的服务器验证说明文档。

支付的时候,Android 客户端 通过调用 getBuyIntent() 方法获得以下 3个 参数:

  1. RESPONSE_CODE
  2. INAPP_PURCHASE_DATA
  3. INAPP_DATA_SIGNATURE

1.png

其中,INAPP_PURCHASE_DATA 是一段 json 字符串,包含订单的信息,具体内容如下:
2.png

有几个字段我们必须关注的:

developerPayload : 这个是客户端的透传参数,建议放置自身的交易流水号(自有服务器的订单号)
purchaseState :支付的结果, 0 (支付了), 1 (取消), 2 (退款)
productId :商品ID
orderId : 谷歌的订单ID (当处于沙箱环境的时候,没有这个字段)

以下是 PHP 服务端的实例:

<?php

function checkGooglePlayBilling() {
    $inappPurchaseData = isset($_REQUEST['INAPP_PURCHASE_DATA']) ? $_REQUEST['INAPP_PURCHASE_DATA'] : null ;
    $inappDataSignature =isset($_REQUEST['INAPP_DATA_SIGNATURE']) ? $_REQUEST['INAPP_DATA_SIGNATURE'] : null ;
    $googlePublicKey = 'Google Play Developer Console 中此应用的许可密钥';

    $publicKey = "-----BEGIN PUBLIC KEY-----". PHP_EOL .
                                                     chunk_split($google_public_key, 64, PHP_EOL) . 
                                                     "-----END PUBLIC KEY-----";

    $publicKeyHandle = openssl_get_publickey($publicKey);
    $result = openssl_verify($inappPurchaseData, base64_decode($inappDataSignature), $publicKeyHandle, OPENSSL_ALGO_SHA1);
    if (1 !== $result) {
            retuan false;
    }

    $data = json_decode(inappPurchaseData, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        return false;
    }

    //判断订单号,订单情况,自行解决
    if ($data['developerPayload'] != 'xxxx') {
        return false;
    }

    //判断订单完成情况
    if ($data['purchaseState'] != 0) {
        return false;
    }

    return true;
}

进阶
当然,如果需要进一步的验证的话,可以通过 Google Play Developer API 来验证订单的真实情况。

以下是 Google Play Developer API 验证订单接口的文档:

api验证文档

Google通过Google Play Developer APIGoogle Play开发人员API提供收据验证,在API中这两个是需要注意的:Purchases.products:get和Purchases.subscriptions:get

Purchases.products:get:获取可用于验证非自动更新产品购买,其中Purchases.subscriptions:get用于验证和重新验证自动续订产品订阅。

要使用这些接口,您必须知道packageName,productId,purchaseToken,所有这些都可以在您购买时需要客户端传过来的。 您还需要一个access_token,您可以通过创建Google API服务帐户获得。

要开始使用服务帐户,请先访问Google Play Developer Console API访问设置页面,然后单击创建新项目按钮:

1.jpg

您现在应该看到一个新的链接项目和几个新的部分,在服务帐户部分,单击创建服务帐户按钮。

2.jpg

您将看到一个信息框,其中包含创建您的服务帐户的说明。 点击指向Google Developers Console的链接,并且会生成一个新的标签。
3.jpg

现在单击创建新客户端ID,从选项中选择服务帐户,然后单击创建客户端ID。

4.jpg

将下载一个JSON文件,这是您将用于交换access_token的JSON Web令牌,以便保持安全。

接下来,将tab切换回Google Play Developer控制台,然后在信息框中单击完成。 您应该在列表中看到您的新服务帐户。 点击服务帐户电子邮件旁边的授权访问。
5.jpg

接下来在该用户的“选择角色”下,选择“财务”,然后单击“添加用户”。
6.jpg

您现在已经设置了您的服务帐户,并具有执行收据验证的所有必要访问权限。 下一步是交换你的JWT一个access_token。

access_token在一个小时的交换后过期,因此需要一些服务器代码来处理这个问题,Google已经提供了许多语言的多个库来处理这个(列举并不详尽):

我不会详细介绍,因为有很多关于如何使用这些库的文档,
下面是调用接口
https://www.googleapis.com/auth/androidpublisher 作为OAuth2,client_email从作为发行人的JWT和您可以从private_key获取的公钥和密码不存在将用于签名_key。

一旦你有access_token,你至少在下一个小时前,要按照上述段落中的相同过程请求一个新,上面的谷歌提供的client可以帮你自动完成。

要检查消耗品(非自动续订)购买的状态,请将http获取请求发送到:https://www.googleapis.com/androidpublisher/v2/applications/packageName(填包名)/purchases/products/productId(填谷歌商品id)/purchaseToken/purchaseTokenString(填客户端返回的那段json里的purchaseToken)?access_token=你的access_token

如果您获得了200个HTTP响应代码,则所有内容购买有效。 404将意味着您的令牌无效,因此购买最有可能是欺诈尝试。 401将意味着您的访问令牌无效,403将意味着您的服务帐户访问不足,请检查您是否已在Google Play开发者控制台中为访问帐户启用了财务报告权限,详见最后一张图。