步骤/目录： 0.raid介绍 1.背景介绍 2.r730xd的详细参数与扩展 3.esxi系统的安装 4.esxi虚拟机安装TrueNAS 5.esxi虚拟机安装Win10 6.esxi虚拟机安装Ubuntu 7.其它本文首发于个人博客https://lisper517.top/index.php/archives/1562/，转载请注明出处。本文的目的是使用r730xd组建NAS。本文写作日期为2024年7月14日，参考文章将会在正文中列出。0.raid介绍raid技术可以将多块物理硬盘合并成一个存储单位使用，比如说在电脑上显示为1个盘符。raid最大的作用是提升安全性或者读写性能。常用的raid如下（来自于 这篇文章 ）：RAID 0：条带化（数据分块）但没有冗余，提供较高的读写性能。 RAID 1：镜像，数据完全复制到另一个驱动器，提供容错能力。 RAID 5：条带化加分布式奇偶校验，提供数据冗余和读取性能。 RAID 6：类似于RAID 5，但提供更高级别的容错能力。 RAID 10：RAID 1+0，将RAID 1镜像组合成RAID 0条带化，提供较高的容错能力和读写性能。 RAID 50：RAID 5组合成RAID 0，提供较高的性能和容错能力。 RAID 60：RAID 6组合成RAID 0，提供更高级别的性能和容错能力。总的来说，追求简单就raid1（至少2块硬盘），追求速度就raid10（至少4块硬盘），raid1与raid10都只能使用一半的硬盘空间。想提高读取性能、空间利用率、容错率就raid50、raid60。另外需要注意的是，大量数据时，raid5硬盘恢复的失败率会上升。软硬raid的区别：（1）软raid不需要专门的硬件设备，可以在不同的操作系统中读写数据；缺点是消耗CPU、内存，高负载时性能不如硬raid。（2）硬raid需要专门的阵列卡，读写性能高；缺点是必须特定的硬件才能读写数据。另外一个重要的区别是，用软raid时硬盘无法作为系统盘，只有硬raid才能把系统盘也组raid。IT（initiator target）即直通模式，阵列卡只作为总线适配器，把硬盘直接交给操作系统处理，方便做软raid。一般是便宜的阵列卡支持IT模式。1.背景介绍在笔者之前的文章中，曾经用 Ubuntu + samba 的方式组建NAS，使用的硬件为 2盘位硬盘座 + 树莓派 + UPS （见 树莓派+Samba+frp搭建NAS（网络云存储） ），后来将树莓派改成了旧笔记本。这套配置的主要问题有：（1）树莓派的CPU不够强，旧笔记本的CPU可以、但散热不行，所以都无法运行一些其它占资源的东西，比如模拟器多开。（2）扩展性的限制，硬盘座需要通过USB3.0接到树莓派或者笔记本上，想再加硬盘就要再买硬盘座，但USB接口是比较少的。如果换大型硬盘柜则USB接口又容易发热。（3）达不到AIO的要求。AIO即all in one，用一台主机实现NAS、远程云游戏主机、网页服务器等多种功能，树莓派性能孱弱，旧笔记本又没有雷电接口（不能外接显卡）。综合下来，最好的选择是换一台淘汰的存储服务器，能插显卡的。买旧服务器唯二的缺点是：风扇噪音大（一般在60-100dB），耗电高。经过一系列比对，笔者最终在黄鱼上自提购买了Dell的r730xd，原因如下：（0）github上有针对Dell服务器的风扇调节工具，可以把风扇噪音降低到40dB，放在卧室也不影响入睡。作为对比，耳语大概在30-35dB。（1）耗电量，与卖家当面沟通，双CPU（2630L）、12硬盘，正常跑一些服务的情况下，功率为200W，不关机的话每月144度电，可以接受（按0.5计算，约72元/月，和便宜的云服务器差不多）。（2）保有量大，相关资料多，问题好解决，而且Dell的手册也比较详细。（3）工业设计成熟，看起来有一种信息技术革命的美感，赏心悦目。r730xd在2023年左右经过了一些炒作，价格略上涨，涨价前准系统据说只要￥650。笔者购买时￥1550，胜在卖家已经调好了，包括2颗2630L的CPU，4根2133频率的16GB、ECC内存，1张双千兆电口+双万兆光口的网卡（intel X520/I350），H330 mini阵列卡，1个750W电源，3张PCIe扩展卡，iDrac已授权，另外￥400收了卖家的单风扇1050ti。如果不是自提，最好在发货前让卖家拍照，因为对于准系统的定义不太一样。另外自购了2690v4的CPU（￥330），两个千兆光转电模块（￥150），750W电源1个（￥62），额外一张H330 mini卡备用（￥50，后来发现自带的H330 mini已经刷了直通模式，就买了个没刷直通的），硬盘用东芝的N300-16T（618活动￥1889）。2.r730xd的详细参数与扩展关于r730xd的详细介绍，见 参考文章一 和 参考文章二 ，实物介绍见 b站视频 ，官方手册见 r730xd官方手册 。笔者挑一些重点说：（1）存储，r730xd有几个版本，前面板可以放12个3.5英寸硬盘或者24个2.5寸，后面板有2个2.5寸盘位。内部可以扩展出2或4或6个3.5寸盘位（也可能是2.5寸？），但是散热不佳、需要风扇拉满。所以家用一般就是12个3.5寸盘位+后面板2个2.5寸。笔者本打算2个2.5寸用SSD，组RAID1装ESXI和其它系统镜像，但是发现阵列卡只连到了前面板，那么就需要1个能接PCIe的阵列卡 + mini SAS 8643连接线，再分线到后面板，比较麻烦（见 b站视频 ）；剩下的所有3.5寸组RAID1装一般文件，因为对读取速率要求不高，搞RAID10之类的太复杂了（单纯组raid1，一块硬盘坏了，把好的硬盘拿下来就能读，不需要花时间恢复数据，还可能恢复失败）。（2）内存，24条内存插槽，最高支持2400频率的DDR4，3072GB内存容量（24*128GB）。但是注意V4的CPU才能支持2400，V3的CPU最高只有2133。E5的CPU都很便宜，完全没必要省这个钱。另外，插内存时有讲究，单CPU与双CPU也有区别，详见 这篇文章 。简要来说，用同规格的内存，大小可以不同。如果单CPU，就按A1、A2、A3……顺序插；如果双CPU，就按A1、B1、A2、B2……插（标号在内存插槽旁边）。（3）CPU，首选E5-2640 V4，追求更高的就上E5-2690 V4。（4）阵列卡，可用H330 MINI、H730 MINI、H730P MINI，或者通过提升卡的PCIe装其它支持PCIe的阵列卡。数字越大越贵，只有H330是没有电池、缓存的，但是H330支持直通模式（需要自己刷固件，见 b站视频 ）。阵列卡的作用是组raid。（5）网卡，可用博通BCM5720（四口千兆）、Intel 99GTM（双千兆I350电口+双万兆X520电口）、Intel C63DV（双千兆I350电口+双万兆X520光口），其它还有很多。（6）idrac，即Dell的ipmi。ipmi是服务器的一种功能，通电后ipmi就会打开，然后可以通过ipmi远程开关机或管理服务器。大部分r730xd的idrac都是已注册的enterprise版，如果未注册就去tb花80激活一下。（7）提升卡，可插3张raiser提升卡进行扩展，提供额外的x8、x16的PCIe槽，可装显卡、网卡等。（8）显卡，装在提升卡上（官方论坛说不支持显卡，其实是可以装的）。如果买r730，能装2块大显卡，最大可以 32*4*12cm（最多同时装4个单宽显卡或2个双宽显卡）；r730xd则稍逊一筹，最多3个单宽显卡或1个双宽显卡。另外需要注意显卡是否需要额外供电，提升卡2、3都可以为显卡供电，最大可支持2张300W显卡（用Tesla显卡还要用第三方转接线，因为线序不同）。安装显卡可参考 b站视频 。（9）电源，有495W、750W、1100W三种规格。可以装1或2个电源，双电源会稍微增加功率，但1个电源坏了可迅速切换到另一个电源（需要设置电源设备冗余，2个电源必须相同）。电源的风扇偶尔会突然开启3秒左右，这是怕服务器的风扇不足以给电源散热而设置的策略，是正常的。（10）风扇，共有6个，适配的比较多。（11）vFlash，r730xd的在后面板的2.5寸盘位上方。作用是插入SD卡，在系统挂了等极端情况下从SD卡引导启动。这个对于普通家用的意义不大。3.esxi系统的安装参考了 b站视频 。（1）准备工作1）在第一次安装系统前，如果你不知道服务器idrac的ip地址，则需要配一台VGA接口的显示器（或者VGA转HDMI线。但是VGA转HDMI线有时候用不了，建议直接买一台小的VGA接口显示器和VGA线），然后先设置后面的静态ip，再从网页访问idrac进行剩余的设置。2）下载驱动等文件，到 Dell官网 下载针对r730xd的一些驱动文件，包括idrac（文章写作时最新是2.86.86.86），ESXI（7.0 U3，注意选择下载iso格式），BIOS（2.19.0）。其实只下idrac和ESXI就行了，其它东西可以在后面一键更新。3）登录idrac（注意是 https://idrac的IP ），默认账号密码为root:calvin，或者问卖家。在 idrac设置 - 网络 里，将IPv4、v6打开，都启用DHCP，然后在路由器里设置静态ipv4地址。你也可以在idrac里设置静态ip，但笔者觉得在路由器里统一管理更方便。4）更新idrac，在 idrac设置 - 更新与回滚 里，使用刚才下载的最新版idrac更新（需要注意的是，如果你的idrac版本过旧，则可能需要逐个版本更新idrac）。5）降低风扇噪音，到 github 下载最新版的DellEmcFansController。目前是1.0.2，甚至可以对每个风扇分别调速。一般设置到25%就比较合适。6）idrac的其它设置，见原视频。主要是 idrac设置 - 网络 里的几项：启用NIC-勾选 NIC选择-专用 自动协商-打开 启用 LAN 上的 IPMI-勾选 信道权限级别限制-管理员 启用 VLAN ID-不勾选 在 idrac设置 - 用户验证 里，更改root密码 在 服务器 - 虚拟控制台 里，把 插件类型 选择为 HTML57）一键更新固件。在idrac的 服务器 里，启动虚拟控制台（服务器的远程桌面），并打开电源（打开/关闭电源），启动过程比较慢，不停按F10以进入Lifecycle Controller。选择 获取最新固件 - 网络共享(CIFS、NFS、HTTP或HTTPS服务器) - HTTPS 。笔者尝试时显示 “IP地址或共享名称无效” ，应该是被墙了，换个时间或者fq。8）格式化硬盘。将准备使用的硬盘用diskgenius等软件格式化为ext4文件系统，装到r730xd上。（2）安装esxi在虚拟控制台的右上角有 虚拟介质 ，把刚才下的ESXI的iso镜像映射为CD/DVD，然后重启服务器，再次进入Lifecycle Controller。选择 OS部署 - 部署OS ，不组RAID（如果后面板作为系统盘且也想组raid，则需要多加一块能接PCIe的阵列卡，专门分到后面板）， 引导模式 选 BIOS ，可用的操作系统 选 Any Other Operating System ， 手动安装 ，介质选映射的ESXI。等待一段时间后，显示蓝底白字的Boot菜单，不需要操作，自动从ESXI引导，再等待ESXI加载。完成后，根据提示分别按Enter、F11，选择安装盘，键盘选择 US Default ，设置root密码（需要设置的复杂一点），最后F11确认。等待安装完成后，重启。可以注意到重启界面提示esxi在60天后需要购买许可证，这将在后续进行处理。另外需要注意，esxi本身只有不到2GB，但是默认会占用系统盘的119GB作为虚拟闪存。如果你的系统盘比较小，在前文的b站视频中有介绍如何减少系统保留区。（3）esxi的初步设置服务器启动esxi系统后按F2，输入root密码进入设置，选择 Configure Management Network ，设置IPv4（用空格键选择），在这里设置静态IPv4（或者在路由器里设置静态IP也行），并打开IPv6、DHCPv6；在 Network Adapters 里选择管理esxi时用哪个网口。退出到设置页首，进入 Troubleshooting Options ，开启SSH功能。最后，通过此步设置的静态IPv4地址访问esxi网页端（同样是https）。在 主机 - 管理 - 许可 - 分配许可证 中解除esxi的60天试用限制，可以自行搜索，或尝试以下的许可证（建议就用vSphere ESXi 7.0 Enterprise Plus的4个许可证）：vSphere ESXi 7.0 Enterprise Plus ======================================= JJ2WR-25L9P-H71A8-6J20P-C0K3F HN2X0-0DH5M-M78Q1-780HH-CN214 JH09A-2YL84-M7EC8-FL0K2-3N2J2 JA0W8-AX216-08E19-A995H-1PHH2 ======================================= vCenter 7.0 Standard 104HH-D4343-07879-MV08K-2D2H2 410NA-DW28H-H74K1-ZK882-948L4 406DK-FWHEH-075K8-XAC06-0JH08 ======================================= VSAN 7.0 Enterprise Plus HN0D8-AAJ1Q-07D00-6U924-CX224 50008-221DH-M7E99-A9CKM-A1030 HN0NH-62051-H75E9-P38RM-0H870 ======================================= vSphere 7 Enterprise Plus with Add-on for Kubernetes J1608-4GJEQ-071L8-9VA0H-2MUK6 M10DH-8YL47-474L1-DV3U0-8H054 MM4MR-2G005-H7039-JVCAP-2RAL8 ======================================= VMware Horizon 7 Enterprise 100 CCU(s) 9H0AK-4Y192-H8JAR-0H7R0-1RZJM笔者提前将硬盘格式化为ext4格式，将r730xd关机，插上硬盘并开机。在esxi的 存储 里，点进该硬盘， 新建数据存储 ，为硬盘取名， 使用全部磁盘 、 VMFS6 。（4）VMware Workstation Pro可以从网页管理ESXI，也可以使用VWP、从桌面端管理ESXI。截至2024年04月27日，最新版本为17.5.2，官方下载地址为 VWP-17.5.2 。17大版本的激活码有：MC60H-DWHD5-H80U9-6V85M-8280D 4A4RR-813DK-M81A9-4U35H-06KND NZ4RR-FTK5H-H81C1-Q30QH-1V2LA JU090-6039P-08409-8J0QH-2YR7F 4Y09U-AJK97-089Z0-A3054-83KLA在网页端管理ESXI可能会有一些问题，笔者遇到的是无法给已经创建的虚拟机添加新标准硬盘（添加新标准硬盘后无法点击 保存 按钮）。网页端有问题的话可以改用桌面端试试。4.esxi虚拟机安装TrueNAS（1）TrueNAS的安装这里参考了 这篇文章 。首先到 TrueNAS官网 下载TrueNAS-core，这里选择最新的TrueNAS-13.0-U6.2。如果下的太慢也可以选择到 freenas镜像站 下载。如果都下的很慢就用IDM试试。下载完成后，到esxi网页端，点击 存储 - 数据存储浏览器 ，进入esxi的文件管理器，新建文件夹并把TrueNAS镜像放到esxi里。进入 虚拟机 - 创建/注册虚拟机 ，如下选择：创建新虚拟机 客户机操作系统系列 选择 其他 客户机操作系统版本 选择 FreeBSD 13或更高版本（64），因为用的TrueNAS 13 CPU选择6核，这个看自己 内存至少16GB 硬盘1设置50GB，这是装系统的 添加两块存储盘（ 添加硬盘 - 新标准硬盘 ） CD/DVD驱动器1 选择 数据存储ISO文件 ，选择TrueNAS镜像 在 虚拟机选项 里，确认 引导选项 - 固件 是 BIOS创建虚拟机后，在 虚拟机 里打开电源， Boot TrueNAS Installer ，选择 Install/Upgrade ，用空格键选择装系统的位置（50G硬盘），设置root密码， Boot via BIOS 。安装完成后选择 Reboot System ，在控制台界面会输出局域网ip，可浏览器访问、管理TrueNAS。记得在路由器里设置静态ip。（2）TrueNAS的基础设置在 System - General 里，把语言设置为 Simplified Chinese ，时区设置为 Asia/Shanghai 。在 服务 里，把SMB打开，并勾选 自动运行 （开机自启动）。在 系统 - 电子邮件 里，可以设置SMTP发送邮件。在 系统 - 更新 里，把自动更新关闭。在 存储 - 池 里，创建新池，可能会发现如下提示：Warning: There are 2 disks available that have non-unique serial numbers. Non-unique serial numbers can be caused by a cabling issue and adding such disks to a pool can result in lost data.这是因为笔者的H330Mini刷了直通模式（参考 proxmox论坛），但esxi没告诉TrueNAS硬盘的序列号。在esxi中如下操作（参考 truenas论坛 ）：关闭TrueNAS虚拟机，选择TrueNAS虚拟机，点击 编辑 - 虚拟机选项 - 高级 - 配置参数 的 编辑配置... ，添加一项键值对，即 disk.EnableUUID=true然后重启TrueNAS，在 存储 - 磁盘 中选择对应磁盘并 编辑磁盘 ，可以看到其序列号。另外需要提醒一下，如果阵列卡没刷直通模式，并且用阵列卡组raid，那么raid在阵列卡这一层就组好了（硬raid）。比如你组了硬raid1，TrueNAS就只能看到1个硬盘，只能用条带模式。如果没有非常非常高的硬盘吞吐量，建议还是在TrueNAS层面组软raid。继续创建池。选择对应的硬盘后， 数据VDev 下方为 镜像Mirror ，即raid1。 条带Stripe 是raid0，如果有3块以上硬盘还可能显示RAIDZ1（raid5，一重奇偶校验，至少三块硬盘）、RAIDZ2（raid6，双重奇偶校验，至少四块硬盘）、RAIDZ3（TrueNAS独特的三重奇偶校验，至少五块硬盘）。创建时也会删除硬盘上原有内容。在 池 中，选择刚才创建的池的右侧的附加菜单（竖着的3个点）， 添加数据集 （数据集相当于文件夹）。在 账户 - 用户 里新建一个用户及配套的用户组，把该用户的主用户组改成刚新建的用户组。对刚才创建的数据集编辑权限，把 所有者 中的用户和群组改成刚新建的，并且 Apply User 、 Apply Group ，其它选项如图：把everyone删掉。最后打开windows的SMB共享：即可在windows系统访问硬盘。（3）TrueNAS的使用问题①一次断电后重启，磁盘仍然能连接上，但存储池挂了。使用如下命令：zpool import -m -R /mnt/ "池名" 提示有I/O错误，可以恢复到之前一个时间点。按照提示使用：zpool import -F 池名存储池成功上线，然后再次重启。5.esxi虚拟机安装Win10到微软官网下载工具，用该工具可以下载Win10的iso镜像，然后安装。为了玩游戏，可以显卡直通，在esxi网页端， 管理 - 硬件 中筛选支持直通的设备，可以看到显卡，将其切换直通（勾选显卡会自动勾选Audio，应该是显卡带的声卡）。在Win10虚拟机创建时（或者创建后也可以修改），选择 添加其他设备 - PCI设备 ，把显卡和声卡都添加上；在内存中把划分给Win10的内存全部预留并锁定。编辑参数、添加键值对 hypervisor.cpuid.v0=FALSE ，作用是不让显卡知道是在虚拟环境中运行，一般用于独立显卡，否则用不了驱动。另外，可以开启一个网口直通给Win10使用，以使用IPv6。6.esxi虚拟机安装Ubuntu这里选择的是Ubuntu-22.04 LTS，和上面的步骤差不多（参考 这篇文章 ），如果想给Ubuntu直通显卡，可参考 这篇文章 ，笔者尝试后无法装驱动。安装完成后，Ubuntu会显示无法卸载 /cdrom 挂载点，直接关闭电源，重启Ubuntu前把 CD/DVD驱动器1 的连接断开。最后，目前docker对我国关闭了服务。如果需要安装docker，可以参考 这篇文章 ，或者翻越。7.其它到这里已经差不多了，truenas做NAS，win10做云游戏主机，ubuntu做网页服务。如果有需要还可以装OpenWRT做软路由，装CentOS玩，本文就不一一介绍了。
步骤/目录： 1.准备工作 （1）另一台路由器 （2）CR6609降版本 （3）下载文件 2.开启SSH 3.连接SSH 4.刷入pandora 5.刷入breed 6.刷入AX1800固件本文首发于个人博客https://lisper517.top/index.php/archives/554/，转载请注明出处。本文的目的是在小米CR6609路由器上开启SSH，刷入pandora、breed固件，最后刷Redmi AX1800的固件。本文使用的方法应该也适用于所有CR660X的路由器。本文写作日期为2024年2月8日，主要参考了 参考文章一 和 参考文章二 。1.准备工作（1）另一台路由器需要另一台刷好openwrt的路由器（最好reset一下）。也可以用虚拟机，但是后面的流程可能有一些不同，笔者没有试过。（2）CR6609降版本本文使用的开启SSH的方法适用于1.0.103及以下的版本，所以最好回退CR6609版本。笔者实验时不知道当前固件版本，也没找到旧版本固件，测试时CR6609后台显示版本号6.0.xxx，这个6开头的版本可能对应某个1.0.103以后的版本。（3）下载文件两位原贴主提供了网盘：百度网盘一 （提取码: love）百度网盘二 （提取码: pvg4）阿里云盘下载3个网盘中的所有东西。或者直接下载 笔者提供的压缩包 。2.开启SSH笔者在每一步操作后会加上自己的理解，不一定对。openwrt路由器通电并用LAN口连接电脑，用WinSCP将 xqsystem.lua 文件上传到 /usr/lib/lua/luci/controller/admin 下，注意用SCP协议。（上传脚本）网页登录openwrt后台，在 Network - Interfaces 中 Edit 第一个 LAN ，将 IPv4 address 改为 169.254.31.1 ；然后关闭DHCP功能，该 LAN 的 DHCP Server - General Setup 中，勾选 Ignore interface ，最后 Save & Apply 。这之后需要换到 169.254.31.1 以操控该路由器。（ 169.254.31.1 可能是小米路由器留下的后门）在win10电脑中， 设置 - 网络和Internet - 状态 - 更改适配器选项 中，选择连接到openwrt路由器的网卡，右键单击，选择 属性 - 网络 页，找到 Internet 协议版本 4 (TCP/IPv4) ，更改它的属性，从 自动获得IP地址 改为 使用下面的IP地址 ， IP地址 填 169.254.31.3 ， 子网掩码 为默认的 255.255.0.0 即可。（保证等一下CR6609获得相同的IP）在 Network - Wireless 中，更改默认的名为 OpenWrt 的Master网络，ESSID（wifi名）改为 abc ， wifi密码改为 12345678 ，最后 Save & Apply 。如果不只一个Master网络（比如一些路由器有2.4G、5G两个甚至更多wifi），都需要改一下，但是各个wifi不要重名。（CR6609需要连接到openwrt路由器的wifi。同时确保等下没有CR6609之外的其它设备连接openwrt路由器）浏览器访问 http://169.254.31.1/cgi-bin/luci/api/xqsystem/token ，返回如下：{"token":"; nvram set ssh_en=1; nvram commit; sed -i 's\/channel=.*\/channel=\"debug\"\/g' \/etc\/init.d\/dropbear; \/etc\/init.d\/dropbear start;","code":0}将电脑与openwrt的连接断开，电脑从 169.254.31.3 改回 自动获得IP地址 。openwrt路由器不要断电。（测试脚本。 ssh_en=1 表示ssh已经enable， "code":0 是成功的状态码）电脑与CR6609的LAN口连接，登录路由器后台。笔者的后台是 192.168.31.1 ，这里以及后续的后台IP请自行查看是否需要更改，比如其它的CR660X可能是 192.168.1.1 、 192.168.2.1 、 192.168.10.1 。浏览器的网址会变成：http://192.168.31.1/cgi-bin/luci/;stok=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/web/home#router这样的形式。记住这个32位的stok（该stok在每次登录CR6609后台时都会变化）。浏览器访问：http://192.168.31.1/cgi-bin/luci/;stok=（STOK）/api/misystem/extendwifi_connect?ssid=abc&password=12345678将网址中的 （STOK） 改为刚才的stok。等待一会儿后，显示：{"msg":"connect succces!","code":0}有可能显示其它的，比如：提示1646的，请检查下第一步的DHCP是否关闭提示1619的，请检查第一步IP是否设置正确，或者openwrt路由器下连接了其他设备提示1655的，请再来一次，因为这一步确实有小概率会失败。最后访问：http://192.168.31.1/cgi-bin/luci/;stok=（STOK）/api/xqsystem/oneclick_get_remote_token?username=xxx&password=xxx&nonce=xxx返回：{"token":"; nvram set ssh_en=1; nvram commit; sed -i 's/channel=.*/channel=\u0022debug\u0022/g' /etc/init.d/dropbear; /etc/init.d/dropbear start;","code":0}最后的 "code":0 说明CR6609的SSH开启成功。到这一步时，已经可以关闭openwrt路由器了。3.连接SSH通过PuTTY或者MobaXterm等SSH工具登录CR6609，但是还需要得到root密码。首先看CR6609背面的标签，使用管理密码登录。笔者就是通过管理密码成功登录root；如果不行，到 这个网址 ，通过CR6609背面的SN号，计算并得到root密码。如果访问不了，请用bing自行搜索 小米路由器 SN root 。4.刷入pandoraSSH连接到CR6609后，将压缩包内的 pb-boot.img 文件放到 /tmp 下，然后进行以下操作：mtd write /tmp/pb-boot.img Bootloader reboot这之后会断开连接。然后需要拔掉CR6609的电源，用牙签顶住reset键不放，接上电源8s后放开reset，浏览器登录 192.168.1.1 进入PandoraBox。5.刷入breed先刷上breed。breed又称不死鸟，给刷机后的路由器多一层保障。在潘多拉里 选择文件 来上传固件，上传 breed-mt7621-xiaomi-r3g.bin ，然后 恢复固件 即可。路由器会自动重启，等待一会儿后访问 192.168.1.1 可看到breed界面。6.刷入AX1800固件笔者需要小米路由器AP组网。原版的CR6609对mesh组网支持不好（官方固件没办法做子路由），最佳的选择是刷红米AX1800的固件。三家运营商送的CR660X路由器其实就是 Redmi AX1800 的改版，区别有：红米AX1800是白壳，CR660X是黑壳； 前者内存是128MB，后者是256MB（运营商送的路由器反倒升级了）；目前小米商城已下架红米AX1800，二手价格则是￥70左右。一般来说 小米路由器官网 上的 Redmi AX1800 ROM也适用于CR660X，如果用起来有问题（网口速度下降之类的），可以使用笔者包里的1.0.88旧版本（1.0.93则是本文写作时的最新版本固件）。总之，刷好潘多拉和不死鸟后，现在需要刷红米AX1800的系统。进入 192.168.1.1 的breed界面，先备份一下目前的系统（Bootloader、epprom）；然后选择 固件更新 - Bootloader ，选择包里的 Bootloader-CR6608.bin （这个应该对CR660X都通用），勾选 自动重启 ，点击 上传 ，这样就刷好CR6608的Bootloader。然后使用 小米路由器修复工具 （注意它会被杀毒软件报错），根据它的提示，按照如下步骤操作：运行MIWIFIRepairTool.x86.exe，选择固件（笔者使用文章写作时官网的最新1.0.93固件）； 路由器断电后按住reset，插电后橙灯闪烁时放开reset； 选择网卡（LAN口连接的网卡），下一步即可。等蓝灯闪烁时将路由器断电重启。如果一直停留在 以太网配置成功，可以开始刷机操作 ，是windows的防火墙原因（参考 这篇贴子 ）。可以在设置中关闭防火墙（ 控制面板\系统和安全\Windows Defender 防火墙 中， 启用或关闭Windows Defender防火墙 ，把两个防火墙都关闭）。刷完固件后再打开。
步骤/目录： 1.背景介绍 2.测试IPv6 3.购买域名，DNS解析 4.安装ddns-go 5.端口被封禁的解决方法 6.安全措施 7.进阶搭配使用本文首发于个人博客https://lisper517.top/index.php/archives/246/，转载请注明出处。本文的目的是使用 IPv6 + DDNS 以访问内网ubuntu机器。本文写作日期为2024年1月4日，主要参考了 这篇文章 。1.背景介绍在以前的文章中，笔者介绍过frp，也介绍过DDNS。通过frp，购买一台有公网IPv4地址的云服务器，可以将内网机器开放到公网，还可以搭配NPM给内网机器分配不同的子域名。缺点有：（1）服务器成本；（2）如果被封禁80、443端口，需要换ip，但除了vultr等少数厂商外，很多厂商都不能免费换ip；（3）受服务器网速限制，家里的上传带宽一般都不能跑满。优点则是：（1）内网机器不直接暴露端口，获得了一层额外的保护。DDNS（动态DNS），如果你有幸获得了公网IPv4地址（前几年的电信据说比较容易获得），一般是一个动态的公网v4，每次拨号都会变化。通过DDNS，一个固定域名可以与你每次都不同的v4地址绑定，从而方便使用。参考 路由器与DDNS 。随着IPv6的普及，现在基本上一半的网址都能通过v6访问（手机基本上都支持v6）。由于v6能提供的地址太多，协议也比较新鲜，美国暂时没有管制v6发放，现在使用v6的机器都能分配到公网v6地址。但是v6地址太长且经常变化，使用不方便，所以需要搭配DDNS（动态DNS）使用。最后需要提醒，v6 + DDNS 的方案会将内网机器直接暴露到公网，所以一定要设置好防火墙。对于linux系统，这里推荐ufw防火墙（见 ufw简介与树莓派安装ufw ）和fail2ban端口封禁（见 Fail2ban提升服务器安全性 ）。2.测试IPv6第一步确认自己的路由器、光猫能否访问IPv6。先到光猫、路由器后台找到v6相关设置，把v6打开，如果路由器自带v6防火墙可以暂时先打开。然后访问 IPv6 连接测试 和 ddnspod提供的v6检测 （如果有代理，此时需要关闭代理），正常情况下会显示电脑的v6地址，并且结果相同。接下来最好把光猫改桥接，因为光猫的性能较差，而且光猫的防火墙喜欢拦截入站的v6请求。直接打电话给运营商转人工服务，要求光猫改桥接，几分钟就好了。如果不知道自己的宽带账号密码，可以顺便问一下。接着到主路由管理页面，上网方式从DHCP（或者静态IP）改到PPPoE，输入宽带拨号的账号密码即可。另外，ipv6、ipv4的DNS也可以手动改一下，以缓解DNS污染，这里提供cloudflare和google的：cloudflare: 1.1.1.1；1.0.0.1 2606:4700:4700::1111 2606:4700:4700::1001 google: 8.8.8.8；8.8.4.4 2001:4860:4860::8888 2001:4860:4860::8844路由器PPPoE能成功上网后，查看路由器的v6地址，复制到 IP查询 ，看一下路由器是否公网v6，也可以在cmd中 ping -6 你的v6地址 能否ping通，不是的话可以先关闭路由器防火墙，还是不行的话问一下运营商。最后，在浏览器访问路由器的v6地址（注意网址栏的v6地址要用 [] 括起来），应该能看到路由器的登录界面。显示 ERR_CONNECTION_REFUSED ，那一般是防火墙的问题。这里如果看不到也没关系，后面的方案是把内网的其它机器开放而非路由器开放到公网。3.购买域名，DNS解析以下购买域名的厂商称为域名厂商，提供DNS的厂商称为DNS厂商。购买一个自己的专属域名，最便宜的依然推荐 namesilo ，因为没什么套路，域名续费也很便宜。如果不会用namesilo，也可以在cloudflare、阿里云、腾讯云等购买，当然可能会稍微贵一点。普通的top域名一般一年在￥9.9-99比较合理。如果在namesilo购买域名，还要设置一下DNS解析，因为namesilo的DNS不是很给力。把DNS服务器修改到DNS厂商，笔者最推荐cloudflare。然后转到DNS厂商，把域名解析到路由器的v6地址进行测试（cloudflare），等几分钟（解析记录需要几分钟扩散），浏览器访问该域名，也许能看到路由器的登录页面（看不到也没关系）。最后，登录到你的DNS厂商，获得一个token令牌。以阿里云为例，在头像处的下拉菜单中找到AccessKey，创建一个，保存好（忘记保存就再创建一个），AccessKey ID相当于用户名，AccessKey Secret相当于密码。各家DNS厂商可能有一些细小的区别，其它厂商可能叫token令牌、API之类的。总之它会给你一串只出现一次，但可以重复创建的字符作为令牌，用令牌可以对接认证到该厂商进行一些服务。权限最高的称为全局令牌，只拥有某些权限的是局部令牌或者子令牌，一般来说尽量用局部令牌，并且令牌要严防泄露，如果不小心泄露了要及时重新创建。4.安装ddns-go路由器每次重启获得的v6地址都会变化，即动态ip地址，家庭内的其它电脑也一样。现在需要的最后一步就是让电脑每次开机时，通过token对接到DNS厂商，更改DNS解析，将你的域名解析到新的v6地址。好在这一步有现成的工具，即 ddns-go 。接下来就演示在linux下，通过docker安装ddns-go。另外提醒一下，群晖、win、mac也是可以用ddns-go的。需要说明的是，一些路由器也可以进行类似的操作，把路由器暴露到公网。笔者不推荐这样做，因为一般都需要刷固件、新手容易刷坏，而且路由器设置防火墙也稍微要复杂一点。如果想折腾，可以到 恩山论坛 看看有没有自己路由器对应的教程。安装好docker、docker-compose后（参考 Docker入门（一） 和 Docker入门（二） ），在linux上如下操作：mkdir -p /docker/ddns-go/conf/ddns-go cd /docker/ddns-go vim docker-compose.yml写入以下内容：version: '3.8' services: ddns-go: image: 'jeessy/ddns-go:latest' container_name: ddns-go network_mode: host volumes: - /docker/ddns-go/conf/ddns-go:/root restart: always用ufw打开ddns-go使用的9876端口，并运行ddns-go：ufw allow 9876 comment "ddns-go" ufw status docker-compose config docker-compose pull docker-compose up然后网页访问 linux机器ip:9876 ，配置一下。ddns-go的配置比较简单，选一个DNS厂商（也可以多个DNS厂商配置都针对一条解析，能提高解析传播速度。目前支持 Cloudflare、阿里云Alidns、腾讯云Dnspod、华为云、Callback、百度云、Porkbun、GoDaddy、Google Domain），填入该厂商的token，关闭v4，开启v6即可。等待几分钟到半个小时后，通过公网访问该linux机器的网页服务或者SSH测试，记得打开端口。5.端口被封禁的解决方法笔者发现IPv6的80和443被封禁了，但其它端口还能访问。具体表现为内网能通过域名访问，外网不行（但外网ping又能获得正确的IPv6地址），如果把网页服务改到其它端口则外网能访问。解决方法见 可视化管理Nginx-Nginx Proxy Manager 。6.安全措施最后提醒，v6 + DDNS 会将内网机器端口直接暴露到公网。笔者建议的安全措施如下：（1）设置复杂密码。担心记不住密码，浏览器可以记忆，或者用vaultwarden等密码管理工具（参考 VaultWarden ）。（2）SSH设置仅密钥登录，并且密钥设置密码。（3）su用户和sudo用户分开。su指切换用户的权限，sudo指执行命令权限，这样的话即使密钥泄露，登录以后还是要通过密码切换到sudo用户才能执行其它命令。（4）用好ufw（见 ufw简介与树莓派安装ufw ，推荐用 ufw allow from 192.168.1.0/24 to any port 22 这样指定来源ip的形式），fail2ban（见 Fail2ban提升服务器安全性 ）。同时也要理解frp这类穿透能无视ufw。（5）用好docker，做好备份。（6）设置非常规端口，或者更进一步用NPM等反代工具（只需要暴露80、443，扫端口的一般不会去猜子域名）。当然，v6因为地址太多了，其实被扫IP的风险相比v4已经大大降低。7.进阶搭配使用（1）NPM，方便管理各种子域名，见 可视化管理Nginx-Nginx Proxy Manager 。（2）如果要将内网的其它机器也通过该linux机器开放到公网，可以使用 NPM + frp ，也就是开放的机器作frps，其它内网机器为frpc。
步骤/目录： 1.背景介绍 2.前期准备 3.谷歌云免费试用 4.问题本文首发于个人博客https://lisper517.top/index.php/archives/93/，转载请注明出处。本文的目的是免费使用Google云服务器。本文写作日期为2023年12月13日。1.背景介绍2023年11月12日，阿里云出现了非常严重的大范围故障，涉及到了所有地区的所有产品，据不可靠消息，是因为裁员太狠，导致降本增笑。阿里云之前也出过一些小范围的故障，但这次确实影响最大，让人不得不考虑其它备用方案。谷歌云作为old money家的儿子，实力毋庸置疑，前几年还推出了免费试用一年的服务，具体来说是注册后送$300、可在一年内使用。目前免费使用的时间降到了3个月，但也很不错。另外，它的e2-mirco能一直免费（限俄勒冈、爱荷华、南卡，1GB出站流量，该出站流量不包括中国，所以到中国的流量钱需要另付？）。除了谷歌云，亚马逊的EC2（新用户限时免费1年）、微软Azure的B系列（也是限免1年）也可以练练手。长期使用的话，这几家都有费用计算器，稍微算一下就会发现有点贵。如果想长期使用云服务器，可以参考 VPS主机排行榜单 ，该榜单长期有效。搭建个人博客，推荐买香港或者亚洲服务器，最好能备案。阿里、腾讯云的香港服务器都限制网速3.75MB/s（国外厂商的服务器在国内连也差不多。一般非内陆服务器，白天可以，晚上都会有点卡），内陆服务器640KB/s，优点是不会被墙，有时活动价能低至90/年（日常香港最低24￥/月，1TB流量）；如果没赶上活动、又想尽快上手，可以选择一些老厂如Vultr（最低配512MB内存，500GB流量，$3.5/月），Linode（1GB内存，1T流量，$5。目前Linode日本优于Vultr日本），另外注意有些国外厂商是没有香港服务器的。综合各项性能来看，vultr服务器性价比很高，它的价格便宜，可以免费换区、换ip（因为是纯按时间收费，销毁、重开一台机器即可），唯一的缺点是从国内直连时有丢包（没有针对国内网络做优化。但是有优化的都没这么便宜），能满足日常建站、跨越的需求，可惜暂时没有HK地区。最后要提醒，有些新服务器厂商+超低价，贪便宜上车，就要随时做好厂商跑路的准备，只能短时间玩玩。2.前期准备（1）保证能达到翻越的状态。（2）注册一个谷歌账号（用谷歌浏览器就能注册），笔者注册的时候没什么困难，印象不深。没有的可以参考 这篇文章 ，大致是从QQ邮箱app尝试绑定google邮箱账号，然后就能顺利注册。（3）其次要有一张带Visa标（mastercard万事通、American Express美国运通也可以，但实用性、办理难度肯定Visa最好，因为Visa使用最广）的信用卡。一般的选择是申请银联、Visa的双标信用卡，在银行app上申请，寄到手后去网点面签即可，这个信用卡日常或者出国旅游、留学也可使用（但是办的时候注意问是否收转换费。比如你在日本消费，需要先从日元换美元再从Visa卡里扣钱，有些信用卡就要收转换费，一般是1.5%），已毕业工作的话应该比较好申请。如果是学生，可参考 这个视频 。注意现在能办的双标卡都是没有芯片的磁卡，很容易被复制、盗刷，发现被盗刷时要及时将卡冻结。3.谷歌云免费试用到 谷歌云官网 ，点击右上角的 免费开始使用吧 ，登录谷歌邮箱账号，选择香港或者其它地区，付款资料填信用卡号、卡的有效年月、卡背的末三位验证码即可。接下来购买服务就行了。如果不知道用云服务器干什么，可以先从Compute Engine的E2开始使用，它差不多就是国内云的轻量应用服务器。而且所有用户每个月都能免费用一台e2-micro实例（1核2vCPU，1GB内存），这个和＄300赠送金是分开的。对比之下，阿里云内陆1核CPU、2GB内存、40GB硬盘、5Mbps（640kb/s）网速，日常价￥145/月，必须有活动时买才划算（阿里云新人活动价有￥100左右1年的）。选择合适的配置，区域和可用区（区域是大区，以城市命名；可用区是城市里的分区，又分a、b、c共3个），开启实例，然后进行一些配置。需要提醒的是，如果想用GPT，不要选香港区域，因为GPT对香港IP不开放。没有特别需要时，选亚洲区域，延迟低。服务器设置可参考 Docker实践——如何查阅wiki百科及V2Ray 。4.问题笔者在使用谷歌云服务器时，PuTTY能连接，但WinSCP连不上，显示 No supported authentication methods available (server sent publickey) 。参考了 这篇文章 ，用MobaXterm解决了部分问题，可以从云服务器下载、但不能上传。暂时的解决方法是，对于小文件上传，先传到网站服务器上，再到谷歌云服务器用wget下载。目前将WinSCP升级到最新版本，升级时可选从PuTTY导入会话，导入后可用WinSCP上传下载。另外，默认的谷歌云只能使用ipv4，并且有免费的静态外网v4地址。如果想开启ipv4、ipv6双栈，可以参考 这篇文章 。
步骤/目录： 1.SSH简介 2.Windows系统开启SSH本文首发于个人博客https://lisper517.top/index.php/archives/84/，转载请注明出处。本文的目的是在Windows系统中开启SSH。本文实验日期为2023年5月5日，使用的电脑是家用台式机，win10系统。1.SSH简介以下内容来自 SSH 基本知识SSH（Secure Shell）是一种网络协议，用于加密两台计算机之间的通信，并且支持各种身份验证机制。它主要用于保证远程登录和远程通信的安全，任何网络服务都可以用这个协议来加密。历史上，网络主机之间的通信是不加密的，属于明文通信。这使得通信很不安全，一个典型的例子就是服务器登录。登录远程服务器的时候，需要将用户输入的密码传给服务器，如果这个过程是明文通信，就意味着传递过程中，线路经过的中间计算机都能看到密码。1995年，芬兰赫尔辛基工业大学的研究员 Tatu Ylönen 设计了 SSH 协议的第一个版本，同时写出了第一个实现，即SSH1。写完以后，他就把这个工具公开了，允许其他人免费使用。SSH 可以替换 rlogin、TELNET、FTP 和 rsh 这些不安全的协议，所以大受欢迎，用户快速增长，1995年底已经发展到五十个国家的20,000个用户。SSH 1 协议也变成 IETF 的标准文档。1995年12月，由于需求越来越大，Tatu Ylönen 就成立了一家公司 SCS，专门销售和开发 SSH。这个软件的后续版本，逐渐从免费软件变成了专有的商业软件。1996年又提出了 SSH 2 协议（或者称为 SSH 2.0）。这个协议与1.0版不兼容，在1997年进行了标准化，1998年推出了软件实现 SSH2。但是，官方的 SSH2 软件需要付费，而且 SSH1 的有些功能也没有提供。1999年，OpenBSD（一个致力于提供免费、开源的类UNIX操作系统的组织） 的开发人员决定写一个 SSH 2 协议的开源实现，即 OpenSSH 项目。该项目最初是基于 SSH 1.2.12 版本，那是当时 SSH1 最后一个开源版本。但是，OpenSSH 很快就完全摆脱了原始的官方代码，在许多开发者的参与下，按照自己的路线发展。OpenSSH 随 OpenBSD 2.6 版本一起提供，以后又移植到其他操作系统，成为最流行的 SSH 实现。目前，Linux 的所有发行版几乎都自带 OpenSSH。现在，SSH-2 有多种实现，既有免费的，也有收费的，而 OpenSSH 无疑是最受欢迎的。SSH 的软件架构是服务器-客户端模式（Server - Client）。在这个架构中，SSH 软件分成两个部分：向服务器发出请求的部分，称为客户端（client），OpenSSH 的实现为 ssh；接收客户端发出的请求的部分，称为服务器（server），OpenSSH 的实现为 sshd。另外，OpenSSH 还提供一些辅助工具软件（比如 ssh-keygen 、ssh-agent）和专门的客户端工具（比如 scp 和 sftp）。需要提醒的是有些工具提供了便捷的SSH客户端管理，比如PuTTY，但它的ppk私钥与OpenSSH的私钥不通用，需要用它的puttygen工具转换一下。2.Windows系统开启SSH（1）开启sshd服务器在 开始-设置-应用-可选功能 里，在 已安装功能 中搜索 ssh ，一般来说win10默认安装了OpenSSH客户端，但是如上文所述，想通过SSH连接到win10电脑需要的是服务器。在 添加功能 中搜索 ssh 找到OpenSSH服务器并安装，然后在 C:\Windows\System32 中找到 cmd.exe ，将之创建一个桌面快捷方式，右键管理员启动，运行 net start sshd 开启sshd服务器。一般这时候会显示：发生系统错误 1058。 无法启动服务，原因可能是已被禁用或与其相关联的设备没有启动。这是因为sshd服务被禁用。在开始菜单旁边的搜索栏（放大镜）中寻找 服务 ，找到 OpenSSH SSH Server 和 OpenSSH Authentication Agent ，右键 属性 ，把 启动类型 从 禁用 改为 自动 ，再次 net start sshd 即可。显示如下：OpenSSH SSH Server 服务正在启动 . OpenSSH SSH Server 服务已经启动成功。如上设置完成后，sshd开机后都会自启动。（2）用户名与密码如果你用另一台win10电脑的cmd登录，输入如下命令：ssh 用户名@IP地址笔者更加推荐用PuTTY登录。但是不论用什么工具登录，有些时候可能会忘记用户名和密码。在 开始-文档 的上面有一个灰色头像图标，可以更改目前用户的一些设置，把鼠标放在上面一会儿也可以看到用户名；在 登录选项 中选择 密码 可更改密码。（3）密钥登录用密钥登录更加安全。可以用OpenSSH自带的生成工具做密钥，也可用PuTTY生成。在 C:\Windows\System32\OpenSSH 中可以找到 ssh-keygen.exe ，或者你可以在cmd中执行如下命令：ssh-keygen -t rsa选择路径与密钥名，设置密码（也可以不设置，这个密码是在每次使用公钥时用于打开公钥的，安全性更高但每次都要输入密码）。接下来的操作和linux类似，首先打开 C:\ProgramData\ssh\sshd_config 更改为以下设置（这个文件在首次运行sshd后自动创建，需要管理员权限才能修改，推荐用vscode而非记事本打开）：PubkeyAuthentication yes #允许密钥访问 AuthorizedKeysFile .ssh/authorized_keys #存放私钥的文件 PasswordAuthentication no #可选，禁止密码登录，提高安全性 #Match Group administrators # AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys #可能是administrator的私钥文件前面有 # 注释的是默认设置，如果和上面一致就不用更改。值得注意的是最后两项不是默认设置，需要注释掉。每个用户自己有一个文件存放授权私钥，具体来说是 C:\Users\用户名.ssh\authorized_keys ，不过这个文件需要新建。把刚才生成的 .pub 文件（公钥）改名为无后缀的 authorized_keys 放到 C:\Users\用户名.ssh 下即可，以后如果要新增公钥记录就新建一行、复制粘贴到 authorized_keys 文件中。如果用PuTTY登录，可使用puttygen打开该 .pub 文件对应的私钥，生成一个PuTTY可以用的ppk私钥，尝试连接即可。另外需要提醒一下，在cmd中使用 ssh -i 密钥 连接到其它机器时，会提示权限问题（ It is recommended that your private key files are NOT accessible by others. ），大概是说这个密钥太公开了、所有人都能访问到。此时需要对密钥 右键-属性-安全 ，删掉所有其它人的权限，只保留该密钥所有者的完全控制，才能在cmd中使用该密钥。如果是linux机器上用openssh，一般没有这个问题，有的话用chown和chmod改一下就行。
步骤/目录： 0.Samba的陷阱 1.需求介绍 2.Samba与frp 3.ftp 4.webdav本文首发于个人博客https://lisper517.top/index.php/archives/59/，转载请注明出处。本文的目的是使用树莓派搭建可公网访问的NAS，并且比较Samba, ftp与webdav这三种方式的异同。本文实验日期为2022年10月11日。使用的是树莓派4B（内存8G版），系统为Pi OS 64位桌面版（2022年4月4日更新，镜像名 2022-04-04-raspios-bullseye-arm64.img ）。0.Samba的陷阱当你在windows中打开 我的电脑 ，此时打开的东西叫文件管理器（Windows Explorer）。从文件管理器向其它地方拷贝东西，WE会要求目标硬盘先创建一个同等大小的空镜像（内容全部为0的镜像），再填充数据；Samba会调用 ftruncate() 函数来创建，而该函数在FAT文件系统上运行奇慢，需要花很长时间才能创建好空白镜像，WE以为Samba卡死了，最终会返回0x8007003B错误（ 参考回答 ）。FAT文件系统创建空镜像这么慢，归根结底是不支持sparse file（稀疏文件），所以它创建空镜像时真的会在硬盘上挨个写入0；而支持sparse file的，直接指定空镜像文件的头尾区块、存在metadata中即可，不用管这些区块上原本是1还是0。在Samba的设置中有一项隐藏的默认值 strict allocate = no ，如果改成yes，当单个文件大小超过500MB，Samba自己就会要求先生成空镜像再填充数据（参考 wiki-Samba 的 SMB/SMB2 write activities 一节）。综上所述，不需要改Samba的配置，只要保证Samba挂载的不是FAT文件系统（exFAT、FAT32）而是ext4、NTFS就行了（ 参考回答 ）。先创建空镜像再写入，本意是好的，只是FAT执行坏了。1.需求介绍在之前 树莓派+Samba+frp搭建NAS（网络云存储） 一文中，曾介绍过使用Samba在局域网内搭建NAS，并且使用千兆路由器后达到了不错的内网分享速度。在那篇文章中还将samba使用的445端口通过frp映射到公网ip，试图在公网也能访问NAS，但实际上并没有成功。本文将再次尝试，并且除了Samba之外，还尝试使用ftp与webdav方式搭建。需要注意的是，文中提到的所有方法都是树莓派直连硬盘，也就是说用linux把硬盘分享为NAS；如果你用windows电脑直连硬盘分享NAS，本文可能帮不了你。最后，本文中使用的都是云服务器+frp，这样NAS的带宽会被严重限制为云服务器的带宽，有条件的建议还是用DDNS，见之前的文章 路由器与DDNS （如果你家用的是移动的宽带，也可以不用看那篇文章，就用云服务器+frp）。2.Samba与frp在之前的文章中，成功在树莓派上搭建了Samba，内网已经能流畅访问，千兆路由器下，文件读写最快能达到113MB/s，而机械硬盘的最高读写速度也才150MB/s左右，已经很不错了。后来试着用frp开放到公网，怀疑因为云服务器带宽不够（640KB/s），导致windows上连接超时。之后换用 3.75MB/s 的云服务器，还是无法连接，这时才怀疑是否操作有些问题。最终，通过一番寻找，笔者成功解决了Samba开放到外网、用windows连接的问题。这里就跳过Samba的搭建，直接讲如何配置frp，windows如何连接。云服务器、frps和frpc的其他配置见前文，frpc配置好后添加一个samba的映射即可：[samba] type = tcp local_ip = localhost local_port = 445 remote_port = 44500这里 remote_port 选择44500而不是445，主要是因为安全性。445端口本来就是windows用来共享文件夹的，据有些文章说以前 永恒之蓝 病毒针对445、139等端口搞了一些事情，导致现在445、139等端口被全网封禁（139、138、137也和文件共享有关，据说如果445走不通就会尝试这些端口）。所以现在映射445到服务器其他端口，你可以自己选一个大一点的，这里以44500为例。设置完frpc.ini后，在云服务器的防火墙中打开44500端口。到这里，其实已经可以连接到Samba了，mac、linux都可以指定端口号，在文件浏览器中输入 smb://云服务器ip或域名:44500/共享名 即可，然后输入用户名密码即可；linux用命令行挂载时，需要如下操作：apt install -y cifs-utils #CentOS把apt换成yum即可 mkdir /mnt/test mount -t cifs -o username="用户名",password="密码",port=44500 //云服务器ip或域名/共享名 /mnt/test但是windows不支持把Samba换到其他端口，只能使用445端口交流。好在有其他的方法，比如将云服务器的44500端口在windows电脑上连接到445端口。用管理员模式运行cmd，进行如下操作（这里参考了 这篇文章 ）：netstat -aon | findstr "445"如果有输出任何字符的话，说明系统占用了445端口，一般这个进程的pid为4，相关的服务名为Server。cmd中输入 services.msc ，按Enter打开，出现一个管理服务的界面，找到一个名称为 Server 的服务，右键单击，选择属性，在 常规 分页中把它的 启动类型 从 自动 改为 禁用 。这个Server服务估计是管理一些共享文件夹的，不过笔者实验时docker desktop也需要用它（但是windows想要连接Samba必须关掉Server）。之后重启电脑，再次用管理员模式打开cmd，再次输入 netstat -aon | findstr "445" ，确认这次没有输出任何字符、只输出了空行。然后运行以下命令：netsh interface portproxy add v4tov4 listenaddress=127.0.0.1 listenport=445 connectaddress=云服务器ip或域名 connectport=44550 netsh interface portproxy show all #查看一下 #之后使用netsh interface portproxy delete v4tov4 listenaddress=127.0.0.1 listenport=445即可删除这条端口转接设置那么现在云服务器的44500端口就接到windows电脑的445端口上，你往本机的445端口发送什么东西，都会被转发到云服务器的44500端口（像iptables配置的端口转发一样）。此时你在文件浏览器中输入 \\localhost\共享名 ，或者用之前文章提到的 此电脑 -> 计算机 -> 映射网络驱动器 也可以，同样填 \\localhost\共享名 ，之后输入用户名密码即可。如果Samba开放到公网后想用android手机连接，ES文件浏览器不能用（可能需要开会员才能连公网SMB，不是很想开），笔者找到的其他android的SMB客户端又要用Google Play下载。如果需要用android连NAS，建议看后面的内容，或者你把手机刷成其他海外版固件。3.ftpftp是一项上古技术了，很早期就被用于计算机间分享文件，现在也有很多镜像站的网站名称中有ftp。早期的ftp不需要验证，后来发展出了vsftp，即 very secure ftp ，所以拿来做公网开放的NAS也还行。在树莓派上进行如下操作：apt-get install vsftpd nano /etc/vsftpd.conf把vsftpd的配置文件进行如下修改：在最开头加上两行： pasv_enable=YES 和 port_enable=YES 再加上两行： pasv_min_port=65000 和 pasv_max_port=65001 把 #write_enable=YES 前面的#去掉 把 #local_umask=022 前面的#去掉然后重启一下vsftpd服务：service vsftpd restart和samba一样，ftp也需要用户名、密码登录，而且ftp用户、密码对应同名的系统用户、密码（但是samba虽然用户同名，密码却独立于系统密码）；默认情况下，使用某个系统用户的用户名、密码登录，登录后就会看到该用户的家目录，所以这里可以创建一个系统用户ftp_user：useradd -m -s /bin/bash ftp_user passwd ftp_user然后你可以挂上需要共享的硬盘，把硬盘mount到/home/ftp_user下。如果有防火墙的话，打开20、21、65000、65001端口（比如ufw、iptables），就可以用客户端连接了。在windows机器上，打开资源管理器，直接在地址栏输入 ftp://ftp机器ip地址:21/ 即可连接，会提示输入用户名密码（ftp_user和 passwd ftp_user 设置的密码）。在linux机器上，带图形界面的话也可以在资源管理器输入 ftp://ftp机器ip地址:21/ ；在命令行界面，可以使用 curlftpfs 扩展：yum install -y curlftpfs #ubuntu是apt mkdir /mnt/ftp_test curlftpfs -o codepage=utf8 ftp://用户名:密码@ftp机器ip地址:端口号 /mnt/ftp_test即可把ftp文件系统挂载到/mnt/ftp_test下。在Mac上，笔者太过贫穷没有Mac，但是估计也是在地址栏输入 ftp://ftp机器ip地址:21/ 就行了。ftp最重要的缺陷可能是无法实时播放。笔者在内网实验时，很多视频播放器都支持播放ftp视频（即从URL打开），比如开源的SMPlayer；但是输入URL时，如果你的FTP有密码（不允许匿名连接），则可能还要在URL中加上用户名密码，比较麻烦；据说用WebDrive（收费）等软件可将FTP服务器挂载到一个驱动器号下、实时打开视频，但是笔者没有继续实验。ftp也有优点，比如ES文件浏览器可以轻松支持公网FTP；开放到公网时，从windows客户端连接时可以随意换端口（从21换成2121或者其他），不像samba那样只能在windows客户端上把本机的445和公网ip的samba端口连接。单从传输速度看，ftp和samba差不多。但是综合考虑，笔者还是推荐samba。关于ftp公网开放，使用之前文章提到的frp可以实现，笔者没有继续深究。最后稍微讲解一下ftp的配置。ftp的连接方式分为2种，PORT主动方式和PASV被动方式，默认使用PORT方式。客户端使用21端口（或者其他自行设置的端口）连接到服务器后，需要传输文件时，PORT方式是客户端打开一个随机端口（这个端口在一定范围内）、服务器端用20端口向客户端建立连接（受客户端防火墙影响），PASV方式则是服务器打开一个随机端口（用pasv_max_port和pasv_min_port确定上下限）、客户端连接到服务器的这个随机端口（受服务器端防火墙影响）。从方便的角度考虑，PASV不需要客户端在每次连接时放行端口，应该更好一点。更多ftp的配置可参考 这篇文章 。4.webdav关于在树莓派上设置webdav的内容，可参考 这篇文章 。据说webdav在开放到公网后，从windows客户端连接时比samba简单一点，笔者没有验证，主要是samba确实已经够用了，而且兼容性上samba也是最优选，参考笔者的命令可很方便地随时随地打开samba的NAS，实时查看、编辑文件，很多电视盒子、机顶盒等设备都支持SMB。
1.需求介绍 2.解锁SSH （1）降回开发版固件 （2）连接SSH 3.固化SSH （1）备份 Bdata 和 crash 分区 （2）修改 Bdata 和 crash （3）刷入 Bdata 和 crash （4）切换系统版本 （5）稳定版固件固化SSH 4.其他设置 附录：小米路由器修复工具的使用本文首发于个人博客https://lisper517.top/index.php/archives/58/，转载请注明出处。本文的目的是为小米AX6S路由器固化SSH，这样即使升级官方固件，SSH也会开放。本文实验日期2022年10月11日，电脑为windows10系统。1.需求介绍在之前 寝室多设备连接校园网的解决方案 一文中，曾经为小米路由器4C刷上原版openwrt固件（固件可以理解为操作系统），在路由器上运行脚本实现校园网自动拨号、保持连接。实际上，如果使用性能更好的路由器，刷上其他固件以后还能做很多其他事情，但这一切都要通过基础的SSH来进行；可是路由器刷上其他固件后，由于不如原厂固件贴合，常常有路由器重启、wifi信号弱等问题。综上，使用原厂固件，优点是系统稳定，缺点是没有打开SSH、自由度不高；使用其他固件可以打开SSH、得到更多功能，但缺点是不如原厂固件稳定、wifi信号差。所以，在使用原厂固件的情况下开启SSH功能，成为一个比较折衷的选择。本文的目的就是对AX6S路由器，在使用原厂固件时打开SSH功能，并且将SSH功能固化，即使原厂固件升级或者路由器重启时也保持SSH打开。操作步骤和工具完全参考了 这个项目 。该项目包含的文件可到原网页下载，或者使用 笔者的备份 进行下载。本文中不含图片，项目中的md文档包含图片，更加直观。另外需要说明的是，不同厂家或者不同型号的路由器，开启SSH的方法不尽相同，其他型号的路由器打开SSH不能完全使用本文的方法。2.解锁SSH对于小米AX6S路由器，一般是把原厂固件降回开发版固件（默认开启Telnet和SSH功能），然后用SN号计算出root密码，连接到SSH。（1）降回开发版固件浏览器进入AX6S主页：192.168.31.1，找到 常用设置 -> 系统状态 -> 升级检测 ，点击 手动升级 -> 选择文件 ，选择 miwifi_rb03_firmware_stable_1.2.7.bin 这个文件上传、开始升级。这个文件在 小米路由器官网 无法找到，因为现在AX6S和许多其他型号都不提供开发版固件下载（目前好像只有AX9000，就是售价1299￥的那款路由器提供开发版固件）。但是它的小米路由器修复工具挺不错的，救砖能力强大，建议下载一个，网址为 https://bigota.miwifi.com/xiaoqiang/tools/MIWIFIRepairTool.x86.zip 。升级过程中注意路由器不要断电了。升级的同时，到 root密码计算网站 ，输入AX6S下方标签贴的SN号（或者在路由器管理页面也可以看到），经过一定计算得出root密码（小米官方固件的开发版都可以这样得到root密码）。路由器升级完成后指示灯会重新变蓝，刷新一下 192.168.31.1 可以确认，管理密码是一样的，但是能看到背景里有水印：only for test。（2）连接SSH使用putty.exe，ip为 192.168.31.1 ，端口就是22，进行连接。但有时开发版也可能只开启了Telnet、未打开SSH，那么就需要用Telnet开启SSH。对于win10机器，在 windows设置 里搜索 启用或关闭windows功能 ，在下面的框中勾选 Telnet 客户端 后确定，这样就开启了Telnet功能。然后在cmd中输入 telnet 192.168.31.1 ，输入root和密码即可用telnet连接到AX6S。接下来进行如下操作以解锁SSH：nvram set telnet_en=1 nvram set ssh_en=1 nvram set uart_en=1 nvram set boot_wait=on nvram commit /etc/init.d/dropbear enable /etc/init.d/dropbear start再次使用putty连接AX6S的SSH，成功。至此，SSH解锁成功。为了让原厂固件升级后或路由器重启后SSH功能也保持开启，还需要进行一些额外操作。3.固化SSH以下的命令使用SSH或Telnet输入都可以。（1）备份 Bdata 和 crash 分区使用 cat /proc/mtd 查看 Bdata 和 crash 对应的dev名，笔者实验时分别为 mtd5 和 mtd7 。接下来使用如下命令备份它们：nanddump -f /tmp/Bdata_mtd5.img /dev/mtd5 nanddump -f /tmp/crash_mtd7.img /dev/mtd7如果 mtd5 和 mtd7 不是你的 Bdata 和 crash 对应的dev名，记得更改。然后用WinSCP连接到AX6S（文件协议选SCP），到/tmp下将 Bdata_mtd5.img 和 crash_mtd7.img 这两个备份的文件保存到windows机器上。在进一步改动之前，再额外把这两个文件备一份副本。（2）修改 Bdata 和 crash使用HxD.exe打开 crash_mtd7.img ，将开头的四位改成 A5 5A 00 00 后保存。对于 Bdata_mtd5.img 要稍微复杂一点。同样用HxD.exe打开，进行下列操作：将 telnet_en、ssh_en、uart_en 的值都从 0 修改为 1 。（在右侧）复制字符串 boot_wait=on ，粘贴到末尾。具体是复制这串字符，把鼠标移到 b74_5G.. 的两个小数点中间，单击右键，选择 覆盖方式粘贴 。点击工具栏的 编辑 -> 选择块 ，起始偏移输入 4 ，结束偏移输入 FFFF ，点击确定。点击工具栏的 分析 -> 校验码 ，选择 CRC-32 ，点击确定。下方会弹出一个框，有一列校验和，假设这个校验和为 12345678 ，就把它逆序后输入到前4位，也就是说前四位应该修改为 78 56 34 12 。最后，点击保存。（3）刷入 Bdata 和 crash使用WinSCP将修改好的 crash_mtd7.img 放回到/tmp下，覆盖原来的文件，然后执行以下命令：mtd -r write /tmp/crash_mtd7.img crashAX6S会自己重启。之后重新用putty、WinSCP连接，同样把修改好的 Bdata_mtd5.img 放回到/tmp下，然后执行以下命令：mtd -r write /tmp/Bdata_mtd5.img BdataAX6S也会重启。再次用putty连接，输入以下命令：mtd erase crash #可以修复指示灯不亮等问题。笔者实验时，开启SSH后Internet灯就没亮过，使用这条命令后能亮 rebootAX6S再次重启。（4）切换系统版本实际上这里的开发版也可以切换到稳定版，只是换一下启动分区。putty使用如下命令：nvram get flag_last_success如果返回值为1，说明现在开发版系统的分区号为1，则稳定版的分区号为0。使用如下命令切换到稳定版：nvram set flag_last_success=0 nvram set flag_boot_rootfs=0 nvram commit reboot路由器会重启。如果 nvram get flag_last_success 返回0，那么把后面4条命令中的前两条最后一个数字从0换成1即可。（5）稳定版固件固化SSH经过刚才的步骤，我们使用稳定版也可以开启SSH了。但是默认情况下稳定版的SSH是关闭的，你可以使用Telnet连接AX6S，并用下面的命令临时开启SSH：sed -i 's/channel=.*/channel=\"debug\"/g' /etc/init.d/dropbear /etc/init.d/dropbear restart这样虽然开启了SSH，但重启路由器后SSH又会关闭。解决办法是写一个开启SSH的脚本，路由器开机后自动运行该脚本、开启SSH。使用上面的命令临时开启SSH，然后进行如下操作：mkdir /data/auto_ssh vi /data/auto_ssh/auto_ssh.sh复制如下内容（复制后，选中putty框，按一下i，shift+鼠标右键粘贴）：#!/bin/sh host_key=/etc/dropbear/dropbear_rsa_host_key host_key_bk=/data/auto_ssh/dropbear_rsa_host_key # 如果存在备份的SSH密钥，将备份的密钥链接到dropbear使用的密钥 if [ -f $host_key_bk ]; then ln -sf $host_key_bk $host_key fi # 当前固件为稳定版时，需要执行下面命令开启SSH channel=`/sbin/uci get /usr/share/xiaoqiang/xiaoqiang_version.version.CHANNEL` if [ "$channel" = "release" ]; then sed -i 's/channel=.*/channel="debug"/g' /etc/init.d/dropbear /etc/init.d/dropbear restart fi # 备份SSH密钥 if [ ! -s $host_key_bk ]; then i=0 while [ $i -le 30 ] do if [ -s $host_key ]; then cp -f $host_key $host_key_bk 2>/dev/null break fi let i++ sleep 1s done fi按esc，按shift+:，输入wq后按enter即可保存。再进行下列操作：chmod +x /data/auto_ssh/auto_ssh.sh uci set firewall.auto_ssh=include uci set firewall.auto_ssh.type='script' uci set firewall.auto_ssh.path='/data/auto_ssh/auto_ssh.sh' uci set firewall.auto_ssh.enabled='1' uci commit firewall这样，就写了一个开启SSH的脚本，并把它添加到开机自启的服务中了，现在可以使用 reboot 重启，看看是否在重启后能连上SSH。如果恢复出厂设置，或者再次刷机后，脚本会消失，自己重新添加一下、设置开机自启就行了。如果要关闭开机自启SSH，使用如下命令：uci delete firewall.auto_ssh uci commit firewall reboot最后，原文还介绍了安装shellclash和刷入openwrt的方法，感兴趣的可参考md文件或 github原址 。4.其他设置为了安全性，需要进行一些设置。用 passwd root 为root用户更改密码，建议改的复杂一点。另外，建议SSH使用密钥登录、禁止密码登录。下面的内容看不懂也没关系，一般来说用复杂的root密码已经够了（除非你用DDNS或其他方式把路由器开放到公网）。在为AX6S设置SSH前需要了解，小米路由器的原厂固件是从openwrt改来的（官方介绍是这样说的），对于openwrt，管理SSH的工具是dropbear；或者你也可以直接更改 /etc/config/dropbear ，可以把这个文件当成 /etc/ssh/sshd_config ，但是格式上不同。使用puttygen生成一个公钥和私钥，然后putty连接AX6S后进行如下操作：vi /etc/dropbear/authorized_keys把公钥复制到这里，然后试试能否用putty密钥连接AX6S，注意一定要用密钥连接上再进行接下来的操作。有时会提示 server refused our key ，但是你还是能输入root的密码登录SSH，这种情况不算是密钥登录。然后看一下默认的SSH设置：vi /etc/config/dropbear笔者实验时，原来的设置为：config dropbear option PasswordAuth 'on' option RootPasswordAuth 'on' option Port '22' option BannerFile '/etc/banner'参考 openwrt官网docs 可以了解更多。下面关闭密码登录：uci set dropbear.@dropbear[0].PasswordAuth="0" uci set dropbear.@dropbear[0].RootPasswordAuth="0" uci commit dropbear /etc/init.d/dropbear restart最后，笔者实验时发现，每次路由器重启后， /etc/dropbear/authorized_keys 这个文件会被删除掉，导致无法用密钥连接。这个问题可以修改 /data/auto_ssh/auto_ssh.sh 开机脚本来解决：vi /data/auto_ssh/auto_ssh.sh在开头添加几行，最后全文件如下：#!/bin/sh echo "你的公钥" > /etc/dropbear/authorized_keys #添加其他公钥，使用如下命令 #tee -a /etc/dropbear/authorized_keys < "其他公钥" host_key=/etc/dropbear/dropbear_rsa_host_key host_key_bk=/data/auto_ssh/dropbear_rsa_host_key # 如果存在备份的SSH密钥，将备份的密钥链接到dropbear使用的密钥 if [ -f $host_key_bk ]; then ln -sf $host_key_bk $host_key fi # 当前固件为稳定版时，需要执行下面命令开启SSH channel=`/sbin/uci get /usr/share/xiaoqiang/xiaoqiang_version.version.CHANNEL` if [ "$channel" = "release" ]; then sed -i 's/channel=.*/channel="debug"/g' /etc/init.d/dropbear /etc/init.d/dropbear restart fi # 备份SSH密钥 if [ ! -s $host_key_bk ]; then i=0 while [ $i -le 30 ] do if [ -s $host_key ]; then cp -f $host_key $host_key_bk 2>/dev/null break fi let i++ sleep 1s done fi最后，虽然SSH用密钥连接，但是telnet还开着、可用密码连接，是一个隐患。如果想关闭telnet，笔者的方法是：crontab -e在里面添加一行 * * * * * /etc/init.d/telnet stop ，这样每分钟关闭一次telnet服务，虽然有点笨拙，但至少telnet不会常开了；或者你也可以用iptables关闭telnet使用的23端口。附录：小米路由器修复工具的使用可能由于本文中修改了 Bdata 和 crash 分区，所以无法使用修复工具，这点要注意。对小米路由器来说其实不太担心变砖，可以自由刷机，因为小米路由器修复工具很好用，下面讲讲修复工具的使用。到官网下载小米路由器修复工具，解压后会报毒，选择 允许在设备上 。到官网下载小米路由器的固件，将电脑连接到路由器（连到路由器的LAN口），断开其他所有网线，运行exe文件，开始刷机。刷机过程中也注意不要断电，有UPS的可以用上。上传刷机包 即选择固件，选择刚才下载的固件； 网卡设置 中，注意选择正确的网卡（比如你的电脑上装了虚拟机，可能显示其他网卡，注意选择有线连接到路由器LAN口的那张网卡）； 到 刷机步骤 界面，断开路由器电源，按住reset键后接通路由器电源，直到橙灯闪烁松开reset键（对于路由器Mesh是紫灯长亮时），再等待5-8min，成功修复后路由器灯会变蓝闪烁，下面的文本框中会出现新的提示，这时路由器再断电重启。这样就成功拯救小米路由器了。由于这个工具的存在，其他路由器可能已经变砖，小米路由器却还能救回来。
步骤/目录： 1.DDNS简介 2.获取域名 3.配置路由器 4.配置端口转发 5.配置光猫 6.DDNS的安全性本文首发于个人博客https://lisper517.top/index.php/archives/54/，转载请注明出处。本文的目的是介绍DDNS，并在路由器上使用DDNS，以达到访问内网设备的目的。本文写作日期为2022年9月30日。1.DDNS简介想要从公网访问自己的电脑或其他设备，笔者之前的文章中曾详细介绍过frp内网穿透，它的使用流程大概是：购买一台有公网IP的云服务器，在云服务器上运行frps，在需要从公网访问的机器上运行frpc，运行frpc时是将一个或多个内网机器的端口投射到云服务器的端口上，比如多个内网机器的22端口都需要开放到公网，那云服务器自己占一个22端口，其他机器就用云服务器的122、222、322等端口。frp是中心化的，优点是你可以将多个不同局域网（比如你在两个地方各买了一套房子，或者其他情况下不得不把需要投射的机器放在几个不相连的局域网中）的机器端口投射到同一个公网ip，安全性也有保障（如果你会设置tls、双向认证的话）；缺点是带宽等于云服务器的带宽，而云服务器的带宽常常不够用，可能只有 5 Mbps 这种速度（ 625 KB/s ），想看看家里的监控都有点卡。本文介绍的DDNS则是另一种路线，使用流程为：向网络运营商申请公网ip（这个公网ip在每次拨号时都会变化，不是固定的。另外，据说电信的公网IP最容易申请，联通次之，移动基本不可能），到DDNS服务商处申请一个域名，在需要开放到公网的机器上安装DDNS客户端即可。它的原理是，每次装有DDNS客户端的内网机器联网时，都会要求DDNS服务商把你的域名解析到这台机器的公网ip上（所以虽然每次拨号时公网ip可能变化，但都能通过域名确定公网ip）。在实践中，一般这个开放到公网的机器是路由器，因为它开机时间长、不怕断电，能把连到路由器上的其他内网机器的端口投射到路由器上，而且现在很多路由器都自带DDNS客户端支持。DDNS是以域名为中心的，优点是网速可以很快，只和你的路由器速度、宽带（百兆还是千兆）相关；缺点是域名选择比较少（但是花一些钱，也可以把DDNS的域名设置为自己的域名，比如下文的no-ip是29.95$/年），所有要投射的机器都要在同一个局域网下，而且安全性上需要一点注意（在openwrt等固件上可以设置DDNS用HTTPS加密，但是路由器自带的固件很少有支持这个的）。申请公网ip这一步，你需要找到当初帮你装宽带的师傅，或者你这一片区的网络负责人，可以告诉他家里要装监控，想申请一个公网ip，如果是电信、联通的基本都会帮你开。另外据说尽量不要通过10000或10010找师傅，因为这样算投诉、要扣工资，而是应该留一个师傅的私人电话或微信联系。本文中，选择红米AX6S路由器，它自带支持花生壳（oray.com）、公云（3322.org）、 dyndns.com 和 no-ip.com 这四家DDNS服务商，其中笔者选择 no-ip.com ，很多路由器都支持 no-ip.com 。不过路由器支持最多的还是花生壳，可惜比较贵，而且用花生壳据说需要提交身份证信息。如果你想跟着本文实操，最好买一样的路由器，或者自己去找支持DDNS的路由器（同时还需要支持端口转发，一般支持DDNS的路由器都支持端口转发），查看该路由器支持哪些DDNS服务商；不要买太便宜的路由器了，两三百还是要出的，表示一下起码的尊重，如果一切顺利，你需要花钱的地方只有买路由器这一步，原本的路由器就支持DDNS的话甚至可以不用花钱。2.获取域名进入 https://www.noip.com/ ，右上角用自己的邮箱注册一个免费账号（高级账号有一些额外功能，但是这里就用免费账号演示一下），注册的时候可以先不填域名。登录后自己跳转到 https://my.noip.com/ ，点击左侧的 Dynamic DNS -> No-IP Hostnames ，点击 Create Hostname ，这里需要输入自己的域名、选择后缀域名，最后的样子是 自己的域名.后缀域名1.后缀域名2 ，意思就是比如 www.bing.com ，这3者当中你能随意改动的只有 www ； bing.com 则是固定的，不过还是可以有几种选择。自己的域名最好独特一点，否则路由器下线的时候这个域名可能被别人注册（虽然概率极低）；后缀域名最好先试试能否访问，有些后缀域名因没有备案而无法访问。接下来点击右侧的 Account -> Account Info ，在 Basic Info 里把Username改一下。这个用户名和密码要记住，设置路由器要用。然后在右侧点击 Account -> Security ，有个二维码，下面有这样一段文字：Account: No-IP.com: 你的邮箱 Key: 你的密钥 Type: time-based这个密钥最好记一下，本文中不需要用，但有些时候可能有用。最后提醒，由于这是no-ip的免费DDNS服务，每30天你会收到一封邮件要求再次激活一下，但这个激活不算太麻烦。如果免费DDNS玩熟练后需要额外功能，请升级到no-ip的高级账号（1.99$/月，后缀域名比免费账号的选择更多）或最高级账号（29.95$/年，可以完全用自己的域名）。3.路由器设置以红米AX6S为例，就用原版固件、不用openwrt等其他固件（目前AX6S的openwrt固件用来看视频还行，玩一些在线游戏就会偶有断网，而且笔者对AX6S开SSH、刷其他固件的需求不强），在 高级设置 -> DDNS 里点击 添加服务 ，选择 no-ip ，用户名密码就填刚才的Username和你自己的密码，主机名称为你选的域名，状态检查和强制更新随便，比如填成5分钟和6小时。最后确定，路由器会尝试连接，等待几秒后 状态 变为绿色的 连接正常 ，就行了。如果是红色的 连接失败 ，就检查一下有没有填错的地方，等一会儿再尝试连接一下，直到成功。另外在 常用设置 -> 局域网设置 -> DHCP静态IP分配 里，把连到路由器的所有机器的内网IP都固定一下，这样转发端口的时候不会乱。最后是一些AX6S的其他推荐设置。在 常用设置 -> 上网设置 -> 上网设置 里，选择 手动配置DNS ，填入Google的两个DNS： 8.8.8.8 和 8.8.4.4 ；开启 IPV6网络设置 ，同样 手动配置DNS ，填入Google的IPV6 DNS服务器，即 2001:4860:4860::8888 和 2001:4860:4860::8844 。然后看看配置的效果，访问 http://test-ipv6.com/ （这个网址挺不错，还可以检测自己能否访问ipv6网址），看一下自己的ipv4地址、ipv6地址。最后提一嘴，在现在的主流浏览器上，比如chrome、edge这些，要访问ipv6的IP而不是域名（解析到ipv6地址的域名）的话，用 https://[ipv6地址]:端口号 类似的格式，比如 http://[2001:4860:4860::8888]:80 （这个网址访问不了，只是举个例子）。4.配置端口转发配置到这里，理论上说已经可以通过域名访问自己的路由器及内网机器了（但是可能只在局域网内，这将在后面说明），我们下面来实验一下。首先是路由器的管理页，你访问 选择的域名 （比如 test.ddns.net ），应该就能访问到路由器登录页面，这已经说明成功了。如果需要进一步实验，把内网机器的端口开放到公网，就要配置端口转发，在AX6S的 高级设置 -> 端口转发 里。以树莓派为例，我想用realvnc访问树莓派桌面，realvnc使用的是5900端口，那么可以新建一个端口转发规则，名称可以写成 pi-realvnc ，外部端口比如是56789，内部ip地址为树莓派连到AX6S后的内网ip，内部端口为5900，然后把树莓派的5900端口打开，在realvnc的客户端里，ip填 选择的域名::56789 ，连接即可。再以笔者之前文章提到的samba（一种NAS的搭建方案）为例，它使用的是445端口，那么ip填运行samba的机器的ip，内部端口填445，外部端口也填445（你也可以填其他外部端口，只是用windows连NAS稍微麻烦一点），在samba客户端中，比如windows机器上，访问 //选择的域名/... 即可。其他就不再举例了。总之，搞清楚自己想开放的机器的ip，搞清楚需要开放哪一个或几个端口，就能非常轻松地从外网访问到自己的内网机器。5.配置光猫如果你家里是通过路由器拨号连到外网，那么就可以跳过这一步；如果你家是通过光纤连光猫（EPON、GPON），然后连到下级路由器上，则此时这个域名只能通过内网访问。具体可以查看AX6S的DDNS设置界面，如果 WAN口 IP 是 192.168. 开头的，那就说明只能内网访问；或者可以到 no-ip官网 ，登录自己的账号，可以看到自己的域名， IP / Target 对应的是 192.168. 开头。这是因为虽然你的光猫能获取一个动态的公网ip，但路由器拿到的是内网ip，在路由器上进行DDNS配置也是把域名解析到一个内网ip，自然无法在外网访问。接下来就需要对光猫和路由器再进行一些设置，但下面的步骤笔者并未进行实操。光猫的设置方法比较多变，建议自行搜索（搜索 光猫型号+超级密码 ），这里以TEWA-708E这个型号的光猫为例：首先去光猫背面查看用户名和密码（或者你自行更改过密码，则是自己改的密码），然后访问光猫的ip，比如 192.168.1.1 ，但是这个ip可能只是一些基础设置，对TEWA-708E这个型号需要访问 192.168.1.1:8080 ，用户名密码是一样的。接下来的操作需要你知道自己上网拨号的账号、密码。在 状态 -> 配置向导 里把光猫的工作模式从 自动拨号 改成 桥接模式 ，这里也能看到上网拨号的账号。然后是AX6S路由器，登录管理页面后，在 常用设置 -> 上网设置 -> 上网设置 里，把DHCP改成PPPoE，账号密码就是你上网拨号的账号密码。简单来说，DHCP是由上级的路由器或光猫等给自己分配网络，PPPoE则是自己拨号上网。成功配置后，在上面的上网信息里，应该能看到 IP地址 从 192.168 开头变成一个公网ip，你可以网页访问 https://www.whatismyip.com/ 或 https://www.ip138.com/ 或 http://www.test-ipv6.com/ 看看这三个网站显示的IPv4地址和AX6S路由器上显示的IPv4地址是否相同。在AX6S的DDNS设置里，也能看到 WAN口 IP 变成了这个公网ip。再等待3~10min（如果用的不是no-ip，有时可能要等30min），当DNS服务器里都把你的域名解析到路由器的这个动态公网ip后，就能通过域名访问到自己的内网。最后提一下，很多光猫有一些隐藏功能，比如TEWA-708E这个型号，你还可以访问 http://192.168.1.1:8080/scdmz.html 这个页面来配置DMZ。DMZ功能需要输入一个ip，光猫会把所有端口的流量都转发到这个ip上，相当于转发所有端口。还有开启telnet（ssh），详情参考 这篇文章 。6.DDNS的安全性前面也提到过DDNS的安全性上需要一些注意，下面主要给出4点建议：定期更换域名，或者定期更改路由器的登录密码（设置的复杂一点）。一般情况下，国内的网络会屏蔽80、443端口，大家可能会把路由器的管理页面放到其他端口。虽然概率不大，但路由器长时间开着，总有些人会用一些脚本扫描no-ip的各种域名或ip，万一不巧扫到你的，他又恰好扫描端口到路由器的管理页面，一个一个试你的密码，说不定就给你试出来了。从内网登录路由器后台。不要在公网登录路由器，因为用的http是不加密的。笔者的解决方法是在端口转发规则里常备一个树莓派的22端口、5900端口转发，需要的时候先ssh登录树莓派、临时打开5900端口，用realvnc的客户端登录树莓派的桌面，浏览器访问192.168.31.1登录小米路由器后台，从内网这里操作路由器页面。这也是对上面一条的补充，因为你并没有在外网改端口开放路由器的管理页面，更安全。使用非常规的端口。在转发端口时，内网机器的端口号可以不改，路由器开放的公网端口建议要改一下（前文中的5900改成56789），而且在1~65535这个范围内尽量大一点，这样别人扫到端口的几率会降低，而且就算扫到也猜不到是用来干什么的。最后，你转发端口时也要注意你的这些服务是否加密，比如SSH就是安全的，realvnc也在传输层有加密，samba用的是用户名密码认证，也算是安全的（只是建议定期修改密码）；如果是redis的6379端口，则不应该直接开放到公网，而是要设置一下redis的tls加密才敢放开。有些路由器的DDNS支持在最外层用https加密，也可以打开，但是需要在DDNS服务商处有一些其他设置或收费。
Python，爬虫与深度学习（6）——爬虫详解（二）构建自己的ip池（简易版）
步骤/目录： 1.需求分析 2.mysql建表 3.网页请求分析 4.爬虫编写 （0）安装所需模块 （1）mysql存取 （2）爬虫主体编写 （3）其他爬虫补充 （4）实际运行 （5）检测爬取的ip 5.总结与改进本文首发于个人博客https://lisper517.top/index.php/archives/44/，转载请注明出处。本文的目的是构建自己的ip池。本文写作日期为2022年9月4日。操作的平台为win10，编辑器为VS code。爬虫写的好，牢房进的早。使用爬虫时不要占用服务器太多流量，否则可能惹祸上身。除了python与爬虫，这里实际上还需要了解html格式与mysql命令。可以在 runoob-html 或 runoob-mysql 找到初步的学习资料，或者查看笔者其他的文章。最后，笔者的能力有限，也并非CS从业人员，很多地方难免有纰漏或者不符合代码原则的地方，请在评论中指出。1.需求分析之前说过，在对别人的网页服务器进行爬取时要注意不能占用太多服务器资源。但有时候，我们确实需要在短时间内对某个网页高频访问，又担心自己被封ip，那么此时维护一个ip池就非常有必要，我们需要对网络上的ip（每个ip背后是一个或一群服务器）进行测试，看是否能利用这些ip作为代理服务器访问其他网页。本文先介绍如何爬取ip，后续文章会介绍如何用自己的ip池爬取网页，以及代理失效时更换代理。在爬虫的一些较高级的应用中，一个ipool是非常基础且不可或缺的，所以建议学会scrapy后就应该尽快建立自己的ipool（除非没有高频访问某个网页的需求）。ip池的构建和使用分为两步，第一步是筛选ip。世界上的公网ipv4地址最多有 4,294,967,296 个（256^4。实际上有一些被局域网占用，有一些不会使用），要一个个去验证这些ip耗时太长，何况还需要对已存在的ip进行验证、剔除失效ip。所以，这里使用一些提供免费代理ip的网页缩小ip搜索范围，爬取到的代理ip不做验证，都放入库中（这里使用mysql）。第二步是对mysql中已有的ip进行验证，挑选出对某个网址有效的代理ip。这一步严格来讲和第一步关系不大，它可以在前一个步骤进行时也同步进行，更重要的是这一步是在其他爬虫开始后才进行，下面就讲解一下免费代理ip的使用。免费代理可分为HTTP或HTTPS，高匿名和低匿名。如果爬虫运行时经过了这些免费代理，毕竟还是别人的服务器，你的请求和响应是到了别人那里，所以用代理服务器时不要传递一些私密信息。实在有必要的话，自己买服务器做转发。最后，同一个代理ip，可能你用它代理一个网站可以，代理另一个网站就不行，这也是不确定因素，所以爬到的代理ip在使用时还要对特定的网址进行测试，其实也是比较麻烦的，如果有紧急需求，建议买一些付费的代理ip。2.mysql建表在mysql中存储收集到的ip，需要新建以下列：ip，字符串形式，VARCHAR(50)（为了将来兼容ipv6，稍微长一点）；端口号，数值形式，SMALLINT UNSIGNED（刚好是0~65535）；匿名度，2个bit位，BIT(2)（0表示低匿名，1表示高匿名，3表示未知）；类型（HTTP还是HTTPS），2个bit位，BIT(2)（0表示HTTP，1表示HTTPS，2表示都可，3表示其他）；ip物理位置，字符串，VARCHAR(100)；响应速度，MEDIUMINT，单位是毫秒ms；最后一次验证的时间，日期时间格式。建表的语句在程序中将给出。其实对这种不断更新、添加的数据，redis更加合适，有关redis的内容将在后续的文章中介绍。3.网页请求分析这里使用的提供代理ip的网站为（笔者给出的例子中，只使用了第一个网址）：http://www.ip3366.net/free/ https://www.kuaidaili.com https://ip.jiangxianli.com/ https://www.cool-proxy.net/ https://free-proxy-list.net https://www.ipaddress.com/proxy-list/ http://proxy-list.org/english/ https://www.proxynova.com/proxy-server-list/ http://pubproxy.com/api/proxy?limit=5&format=txt&type=http&level=anonymous&last_check=60&no_country=CN https://www.rmccurdy.com/.scripts/proxy/good.txt http://spys.one/en/anonymous-proxy-list/ https://raw.githubusercontent.com/sunny9577/proxy-scraper/master/proxies.json https://raw.githubusercontent.com/a2u/free-proxy-list/master/free-proxy-list.txt https://raw.githubusercontent.com/clarketm/proxy-list/master/proxy-list.txt https://raw.githubusercontent.com/TheSpeedX/SOCKS-List/master/http.txt这些网页的请求响应都挺简单的，这里就不专门介绍了。由于涉及多个网站，这里将使用多线程的方法，持续对各个ip网站爬取。唯一需要注意的是对每个网站，爬取ip的速度不要太快了，虽然对单个网站爬取速度不快，但毕竟有这么多ip网站，实际上ip入库的速度挺快的。4.爬虫编写（0）安装所需模块需要用到requests（处理网络请求）、bs4（对响应内容进行解析）、pymysql（连接mysql数据库）、lxml（网页解析器）、cryptography（连接mysql时密码加密）库，在cmd中使用 pip install requests bs4 pymysql lxml cryptography 下载（下载失败就一直使用上述命令，直到下载成功）。之后新建一个文件夹，用VS Code打开该文件夹、新建一个py文件，进行下面的操作。（1）mysql存取使用mysql存储ip，其他爬虫需要通过这个类存取ip。自己使用时，记得把mysql连接的地址、端口、密码更改一下。#!/usr/bin/python # -*- coding: UTF-8 -*- import pymysql, time, os, logging from traceback import print_exc if not os.path.isdir(os.path.join(os.getcwd(), 'logs')): os.mkdir(os.path.join(os.getcwd(), 'logs')) class MysqlOperationFailed(Exception): """若更改mysql出现错误则抛出这个异常。""" def __init__(self): message = "Failed to change data in MySQL, please check. " super().__init__(message) class mysql_api(): """通过这个界面实现提交爬取的ip、取出ip的操作。""" conn = pymysql.connect(host='127.0.0.1', port=53306, user='root', passwd='mysqlpasswd', db='mysql', charset='utf8') cur = conn.cursor() def __init__(self, log_name: str): """初始函数，需要提交一个log_name作为进程的log文件名。""" self.cur = mysql_api.conn.cursor() mysql_api.check_table() self.logging = logging.basicConfig( level=logging.INFO, filename=os.path.join( os.getcwd(), 'logs', '{}-log{}.txt'.format(log_name, time.strftime("%Y-%m-%d-%H_%M_%S")))) def __del__(self): self.cur.close() @staticmethod def check_table(): """检查一下mysql中是否已经建好了表，若表不存在则新建表。这里并没有检查：若表存在，表的格式是否正确。""" cur, conn = mysql_api.cur, mysql_api.conn command = ''' CREATE TABLE IF NOT EXISTS ipool ( no BIGINT UNSIGNED AUTO_INCREMENT, ip VARCHAR(50) NOT NULL DEFAULT "0.0.0.0" COMMENT "IP address", port SMALLINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "port", anonymous BIT(2) NOT NULL DEFAULT 3 COMMENT "whether ip is anonymous", type BIT(2) NOT NULL DEFAULT 3 COMMENT "HTTP or HTTPS or both or others", physical_address VARCHAR(100) NOT NULL DEFAULT "unknown" COMMENT "where is the server", response_time MEDIUMINT NOT NULL DEFAULT -1 COMMENT "response_time in microseconds", last_verify DATETIME NOT NULL DEFAULT '1000-01-01 00:00:00' COMMENT "last verify time", created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (no));''' command = command.replace('\n', '') command = command.replace(' ', '') try: cur.execute("CREATE DATABASE IF NOT EXISTS ipool;") cur.execute("USE ipool;") cur.execute(command) conn.commit() except: conn.rollback() raise MysqlOperationFailed def save_ips(self, ips_list: list): """存入多个ip（以字典的列表形式提交）。注意这里只是对不存在的数据添加默认值，没有验证数据类型等。 另外注意，响应时间单位为ms。""" cur, conn = self.cur, mysql_api.conn command = '''INSERT INTO ipool (ip,port,anonymous,type,physical_address,response_time,last_verify) VALUES ''' for ip_dict in ips_list: ip, port, anonymous, type_, physical_address, response_time, last_verify = "0.0.0.0", 0, 3, 3, 'unknown', -1, "1000-01-01 00:00:00" if 'ip' in ip_dict.keys(): ip = ip_dict['ip'] if 'port' in ip_dict.keys(): port = ip_dict['port'] if 'anonymous' in ip_dict.keys(): anonymous = ip_dict['anonymous'] if 'type' in ip_dict.keys(): type_ = ip_dict['type'] if 'physical_address' in ip_dict.keys(): physical_address = ip_dict['physical_address'] if 'response_time' in ip_dict.keys(): response_time = ip_dict['response_time'] if 'last_verify' in ip_dict.keys(): last_verify = ip_dict['last_verify'] command += '("{}",{},{},{},"{}",{},"{}"),'.format( ip, port, anonymous, type_, physical_address, response_time, last_verify) command = command[:-1] + ';' try: cur.execute(command) conn.commit() except: conn.rollback() raise MysqlOperationFailed @staticmethod def check_repeat(): """检查爬到的数据里有没有重复的，只检查ip和port都相同的。如果有重复的，保留最新的一条记录，也就是no最大的。""" cur, conn = mysql_api.cur, mysql_api.conn cur.execute('USE ipool;') cur.execute( 'SELECT ip,port FROM ipool GROUP BY ip,port HAVING COUNT(*)>1;') repeated_items = cur.fetchall() if len(repeated_items) == 0: print('\n数据库中无重复数据。') return else: print('\n数据库中检测到重复数据，有{}种。'.format(len(repeated_items))) command = ''' DELETE FROM ipool WHERE no IN (SELECT no FROM (SELECT no FROM ipool WHERE (ip,port) IN (SELECT ip,port FROM ipool GROUP BY ip,port HAVING COUNT(*)>1) AND no NOT IN (SELECT MAX(no) FROM ipool GROUP BY ip,port HAVING COUNT(*)>1)) AS a);''' command = command.replace('\n', '') command = command.replace(' ', '') try: cur.execute(command) conn.commit() except: conn.rollback() raise MysqlOperationFailed @staticmethod def get_ips(anonymous: list = [1], type_: list = [2], response_time: int = 30000, last_verify_interval: str = '48:00:00', limit: int = 1000) -> tuple: """根据要求返回多个ip，只返回ip和port。 :param last_verify_interval 用于指定上次验证的时间距现在不超过多久。""" cur, conn = mysql_api.cur, mysql_api.conn cur.execute('USE ipool;') anonymous, type_ = tuple(anonymous), tuple(type_) command = ''' SELECT ip,port FROM ipool WHERE anonymous IN {} AND type IN {} AND response_time BETWEEN 0 AND {} AND (NOW()-last_verify)<"{}" ORDER BY response_time LIMIT {}; '''.format(anonymous, type_, response_time, last_verify_interval, limit) command = command.replace('\n', '') command = command.replace(' ', '') print(command) cur.execute(command) return cur.fetchall() if __name__ == "__main__": try: a = mysql_api('kuaidaili') ip_list = [] for i in range(1, 256): ip_dict = { 'ip': '192.168.1.{}'.format(i), 'port': i, 'response_time': i * 10, 'last_verify': '2022-09-05 12:00:00' } ip_list.append(ip_dict) a.save_ips(ip_list) a.check_repeat() print(a.get_ips([0, 1, 2, 3], [0, 1, 2, 3], 30000)) except: print_exc()（2）爬虫主体编写这里稍微把前面mysql存取的部分进行了更改，一并写出。单个ip网站的爬虫使用了递归调用的方法，不太适合页码比较多的情况。这里只是写了对 http://www.ip3366.net/free/ 的爬虫、并用线程的方法开启它，其他爬虫可在后面自行补充。最后，自己使用时，记得把mysql连接的地址、端口、密码更改一下。#!/usr/bin/python # -*- coding: UTF-8 -*- import pymysql, os, time, logging from traceback import print_exc if not os.path.isdir(os.path.join(os.getcwd(), 'logs')): os.mkdir(os.path.join(os.getcwd(), 'logs')) logging.basicConfig(level=logging.INFO, filename=os.path.join( os.getcwd(), 'logs', 'log-{}.txt'.format( time.strftime("%Y-%m-%d-%H_%M_%S")))) class MysqlOperationFailed(Exception): """若更改mysql出现错误则抛出这个异常。""" def __init__(self): message = "Failed to change data in MySQL, please check. " super().__init__(message) class mysql_api(): """通过这个界面实现提交爬取的ip、取出ip的操作。""" conn = pymysql.connect(host='127.0.0.1', port=53306, user='root', passwd='mysqlpasswd', db='mysql', charset='utf8') cur = conn.cursor() def __init__(self): self.cur = mysql_api.conn.cursor() mysql_api.check_table() message = 'one of mysql_api object started' logging.info(message) def __del__(self): self.cur.close() message = 'one of mysql_api object stopped' logging.info(message) @staticmethod def check_table(): """检查一下mysql中是否已经建好了表，若表不存在则新建表。这里并没有检查：若表存在，表的格式是否正确。""" cur, conn = mysql_api.cur, mysql_api.conn command = ''' CREATE TABLE IF NOT EXISTS ipool ( no BIGINT UNSIGNED AUTO_INCREMENT, ip VARCHAR(50) NOT NULL DEFAULT "0.0.0.0" COMMENT "IP address", port SMALLINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "port", anonymous BIT(2) NOT NULL DEFAULT 3 COMMENT "whether ip is anonymous", type BIT(2) NOT NULL DEFAULT 3 COMMENT "HTTP or HTTPS or both or others", physical_address VARCHAR(100) NOT NULL DEFAULT "unknown" COMMENT "where is the server", response_time MEDIUMINT NOT NULL DEFAULT -1 COMMENT "response_time in microseconds", last_verify DATETIME NOT NULL DEFAULT '1000-01-01 00:00:00' COMMENT "last verify time", created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (no));''' command = command.replace('\n', '') command = command.replace(' ', '') try: cur.execute("CREATE DATABASE IF NOT EXISTS ipool;") cur.execute("USE ipool;") cur.execute(command) conn.commit() message = 'database ipool and table ipool checked' logging.info(message) except: conn.rollback() raise MysqlOperationFailed def save_ips(self, ips_list: list): """存入多个ip（以字典的列表形式提交）。注意这里只是对不存在的数据添加默认值，没有验证数据类型等。 另外注意，响应时间单位为ms。""" cur, conn = self.cur, mysql_api.conn command = '''INSERT INTO ipool (ip,port,anonymous,type,physical_address,response_time,last_verify) VALUES ''' for ip_dict in ips_list: ip, port, anonymous, type_, physical_address, response_time, last_verify = "0.0.0.0", 0, 3, 3, 'unknown', -1, "1000-01-01 00:00:00" if 'ip' in ip_dict.keys(): ip = ip_dict['ip'] if 'port' in ip_dict.keys(): port = ip_dict['port'] if 'anonymous' in ip_dict.keys(): anonymous = ip_dict['anonymous'] if 'type' in ip_dict.keys(): type_ = ip_dict['type'] if 'physical_address' in ip_dict.keys(): physical_address = ip_dict['physical_address'] if 'response_time' in ip_dict.keys(): response_time = ip_dict['response_time'] if 'last_verify' in ip_dict.keys(): last_verify = ip_dict['last_verify'] command += '("{}",{},{},{},"{}",{},"{}"),'.format( ip, port, anonymous, type_, physical_address, response_time, last_verify) message = 'trying to save ip:port={}:{}'.format(ip, port) logging.info(message) command = command[:-1] + ';' try: cur.execute(command) conn.commit() message = 'saving ips successfully' logging.info(message) except: conn.rollback() raise MysqlOperationFailed def check_repeat(self): """检查爬到的数据里有没有重复的，只检查ip和port都相同的。如果有重复的，保留最新的一条记录，也就是no最大的。""" cur, conn = mysql_api.cur, mysql_api.conn cur.execute('USE ipool;') cur.execute( 'SELECT ip,port FROM ipool GROUP BY ip,port HAVING COUNT(*)>1;') repeated_items = cur.fetchall() if len(repeated_items) == 0: message = 'no repeated ip:port in ipool' logging.info(message) return else: message = 'found ip:port repeated {} kinds'.format( len(repeated_items)) logging.info(message) command = ''' DELETE FROM ipool WHERE no IN (SELECT no FROM (SELECT no FROM ipool WHERE (ip,port) IN (SELECT ip,port FROM ipool GROUP BY ip,port HAVING COUNT(*)>1) AND no NOT IN (SELECT MAX(no) FROM ipool GROUP BY ip,port HAVING COUNT(*)>1)) AS a);''' command = command.replace('\n', '') command = command.replace(' ', '') try: cur.execute(command) conn.commit() message = 'repeated ip:port deleted successfully. ' logging.info(message) except: conn.rollback() raise MysqlOperationFailed @staticmethod def get_ips(anonymous: list = [1], type_: list = [2], response_time: int = 30000, last_verify_interval: str = '48:00:00', limit: int = 1000) -> tuple: """根据要求返回多个ip，只返回ip和port。 :param last_verify_interval 用于指定上次验证的时间距现在不超过多久。""" cur, conn = mysql_api.cur, mysql_api.conn cur.execute('USE ipool;') anonymous, type_ = tuple(anonymous), tuple(type_) command = ''' SELECT ip,port FROM ipool WHERE anonymous IN {} AND type IN {} AND response_time BETWEEN 0 AND {} AND (NOW()-last_verify)<"{}" ORDER BY response_time LIMIT {}; '''.format(anonymous, type_, response_time, last_verify_interval, limit) command = command.replace('\n', '') command = command.replace(' ', '') print(command) cur.execute(command) ips = cur.fetchall() return ips import threading, requests, re from lxml import etree headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36' } class spider_threads(): """爬虫类，通过多线程的方法同时爬取多个网站。""" websites = [ 'http://www.ip3366.net/free/' ] url_spidered_list = [] for i in range(len(websites)): url_spidered_list.append(set()) def __init__(self): self.threads = len(spider_threads.websites) @staticmethod def get_session(): """返回一个session对象。""" session = requests.Session() session.mount('http://', requests.adapters.HTTPAdapter(max_retries=3)) session.mount('https://', requests.adapters.HTTPAdapter(max_retries=3)) return session class spider_website(): """定义一个网站爬虫的基本操作。""" def __init__(self, no: int): self.session = spider_threads.get_session() self.base_url = spider_threads.websites[no] self.url_spidered = spider_threads.url_spidered_list[no] self.kind = re.search(r'\.{1}.+\.{1}', self.base_url).group().replace('.', '') self.mysql_api = mysql_api() def get_html(self, url): """对一个页面，返回其响应对象。""" global headers session = self.session try: html = session.get(url, timeout=30, headers=headers) except requests.exceptions.TooManyRedirects as e: message = "网页重定向，休息10min（{}）。".format(self.kind) logging.warning(message) print('\r' + message, end='') time.sleep(600) html = session.get(url, timeout=30, headers=headers) html.encoding = html.apparent_encoding return html def get_ip_recursively(self, url): """这个方法用于递归调用爬虫，将在子类中重写。""" pass def start_spider(self): """这个方法用于开启爬虫，将在子类中重写。""" pass class spider_ip3366(spider_website): """ip3366的爬虫类。""" def __init__(self): super().__init__(0) self.start_urls = [ self.base_url, 'http://www.ip3366.net/free/?stype=2' ] def get_ip_recursively(self, url): """这是一个递归调用的爬虫。它将爬取页面上的信息，将爬取过的网页存入字典，对页面上新的网页进行爬取。 重写了父类方法。""" html = self.get_html(url) time.sleep(15) message = '1 page crawled:{}'.format(url) logging.info(message) html = etree.HTML(html.content) ip_nodes = html.xpath(r'//*[@id="list"]/table/tbody/tr') ips_list = [] for ip_node in ip_nodes: ip_dict = {} ip = ip_node.xpath(r'./td[1]/text()')[0].strip() port = int(ip_node.xpath(r'./td[2]/text()')[0].strip()) anonymous = (0, 1)['高匿' in ip_node.xpath(r'./td[3]/text()')[0]] type_ = (0, 1)['HTTPS' in ip_node.xpath(r'./td[4]/text()')[0]] physical_address = ip_node.xpath(r'./td[5]/text()')[0].strip() response_time = 1000 * int( ip_node.xpath(r'./td[6]/text()')[0].strip().replace( '秒', '')) response_time = (response_time, 250)[response_time == 0] last_verify = ip_node.xpath( r'./td[7]/text()')[0].strip().replace('/', '-') ip_dict['ip'] = ip ip_dict['port'] = port ip_dict['anonymous'] = anonymous ip_dict['type'] = type_ ip_dict['physical_address'] = physical_address ip_dict['response_time'] = response_time ip_dict['last_verify'] = last_verify ips_list.append(ip_dict) self.mysql_api.save_ips(ips_list) self.url_spidered.add(url) page_urls = html.xpath(r'//*[@id="listnav"]/ul/a/@href') for i in range(len(page_urls)): page_url = self.base_url + page_urls[i] if page_url not in self.url_spidered: self.get_ip_recursively(page_url) else: return def start_spider(self): """这个方法用于开启爬虫，对父类的同名方法进行重写。""" for start_url in self.start_urls: self.get_ip_recursively(start_url) self.mysql_api.check_repeat() def start_all_spider_process(): process_list = [] process_list.append(crawl_website_ip3366().start_spider) for i in process_list: process_pool.apply_async(i) time.sleep(35) process_pool.close() process_pool.join() if __name__ == "__main__": try: start_all_spider_process() except: traceback.print_exc()spider_ip3366类是一个范例，其他爬虫也照着这样写就行了，所以剩下的只是简单的重复劳动，这里笔者就不进行展示了。如果要继续写的话，注意其他网站的爬虫都和spider_ip3366类处于同一个层级，也在spider_threads内部。添加其他网站的爬虫后，在start_all_spider_threads里添加线程即可。（4）实际运行自行添加其他网站的爬虫后，运行。（5）检测爬取的ip这是相对独立的一个步骤，并且要在使用时同步进行，将在后续的文章中讲解。5.总结与改进（1）这个爬虫使用了递归，在内存小一点的机器上其实不太好运行。有可能的话，可以改用递归外的方法（比如用scrapy），专门买一个更加小巧的机器或云服务器，全天运行这个爬虫和ip检验，以便能及时更新自己的ipool。（2）在上一个例子（ Python，爬虫与深度学习（2）——爬虫详解（一）爬取金融机构成交持仓排名 ）和本例中，都用到了一些重复的方法，比如连接mysql时添加conn、cur，以及log操作。这部分其实可以在自建库里写方法调用，也免得每次都要更改。（3）这里所有爬虫使用的log文件都是同一个，不太容易看清楚。后续可以让每个爬虫使用不同的log文件。（4）除了自己写这种ipool爬虫，其实在github上有其他人做好的成品。笔者在这里推荐两个项目： https://github.com/SpiderClub/haipproxy 和 https://github.com/imWildCat/scylla ，这两个项目都可以用docker安装。更新：docker-scylla可能自带挖矿病毒，详见这个问题： https://github.com/imWildCat/scylla/issues/102 。
步骤/目录： 1.常用DNS 2.更换DNS 3.添加解析记录本文首发于个人博客https://lisper517.top/index.php/archives/37/，转载请注明出处。本文的目的是为树莓派更换DNS。本文实验日期为2022年8月27日。本文使用的树莓派为 4B 8GB 版，系统镜像是Pi OS的64位桌面版，名为 2022-04-04-raspios-bullseye-arm64.img 。1.常用DNSDNS，即 Domain Name Server（域名服务器），是可以将域名与IP进行转换的服务器。ip地址是xxx.xxx.xxx.xxx的格式、由数字组成，而我们访问网页时的域名显然更好记一些，但最终域名必须转换成ip，网络请求才能找到目的地，这就是DNS发挥作用的地方。DNS也需要通过ip访问，下面是一些常用的DNS（排名表示推荐程度）。国外：（1）Cloudflare DNS 1.1.1.1 1.0.0.1 2606:4700:4700::1111 #IPv6的DNS 2606:4700:4700::1001 （2）谷歌 8.8.8.8 8.8.4.4 2001:4860:4860::8888 2001:4860:4860::8844 （3）微软 4.2.2.1 4.2.2.2 （4）Norton ConnectSafe 199.85.126.10 199.85.126.20 199.85.126.30 199.85.127.10 199.85.127.20 199.85.126.30 （5）OpenDNS 208.67.222.222 208.67.220.220 （6）DNS Watch 84.200.69.80 84.200.70.40 （7）Comodo安全DNS 8.26.56.26 8.20.247.20 （8）威瑞信 64.6.64.6 64.6.65.6 （9）OpenNIC 192.95.54.1 192.95.54.3 （10）GreenTeamDNS 81.218.119.11 209.88.198.133国内：（1）114DNS（http://www.114dns.com/） 114.114.114.114 114.114.115.115 （2）腾讯（https://www.dnspod.cn/Products/Public.DNS） 119.29.29.29 182.254.118.118 （3）阿里（http://alidns.com/） 223.5.5.5 223.6.6.6 （4）百度（http://dudns.baidu.com/intro/publicdns/） 180.76.76.76 （5）CNNIC（http://www.sdns.cn/） 1.2.4.8 210.2.4.82.更换DNSDNS其实起到一个指路的作用，当你访问域名时指向对应的ip。但是有时候可能因为一些原因，DNS故意或无意间指错路了，就被称为 DNS污染 、 DNS劫持 ，这会让我们无法访问到正确的服务器，非常的危险。更换DNS为一些靠谱的DNS可以一定程度上规避这个问题。下面就以google的 8.8.8.8 和 8.8.4.4 为例，展示如何更换为这两个DNS。（0）路由器更换DNS登录路由器后台，一般都可以手动设置DNS，这样最方便。（1）windows系统在 设置 - 网络和Internet - 以太网 - （右上角）更改适配器选项 ，这里可以看到自己的网络连接，可能有无线、有线连接，看一下自己目前用的哪个网络，左下角没有红色叉的是正在使用的网络。对正在用的网络单击鼠标右键，点击 属性 ，点击下面的框里的 Internet协议版本 4 (TCP/IPv4) ，点击右下的 属性 - 常规 - 使用下面的DNS服务器地址 ，填入谷歌DNS。这实际上是设置了单个网络连接的DNS。如果同时使用有线、无线连接，最好把两个网络连接的DNS都设置一下；如果到新的地方、连接到新的网络，则也需要同样更改新网络连接的DNS。（2）linux系统（以树莓派为例）linux系统里一般都可以通过更改 /etc/resolv.conf 这个文件来更改DNS，具体的方法是在这个文件末尾加上：nameserver 8.8.8.8 nameserver 8.8.4.4并把原来的 nameserver 192.168.1.1 等之类的行删掉，或者也可以不删。但是这只是临时更改DNS，因为 /etc/resolv.conf 这个文件在每次开机后里面的内容都会由系统根据系统设置重写，所以开机后原来的设置就会丢失。不过一般这个文件前面的注释里都会提示如何永久更改DNS，像树莓派的这个文件，注释只有一行：# Generated by resolvconfresolvconf 是一个DNS相关的命令，用 whereis resolvconf 可以看到它的可执行文件位置是 /usr/sbin/resolvconf ，另外还可以看到有一个 /etc/resolvconf.conf 配置文件，实际上这个配置文件可以更改 resolvconf 这个命令的设置。进行以下操作：nano /etc/resolvconf.conf根据注释的提示，把其中：#name_servers=这一行改为：name_servers="8.8.8.8 8.8.4.4"然后用 reboot 重启树莓派，再次查看 /etc/resolv.conf ，发现前面增加了谷歌的两个nameserver，最后则是 192.168.1.1 （也可能是其他地址），即路由器的局域网地址。其他linux发行版也是大同小异， /etc/resolv.conf 这个文件的注释一般都会有提示，比如在 Docker实践——用于小诊所的开源医疗记录系统 这篇文章中就用到了OpenSUSE发行版，提示把 /etc/sysconfig/network/config 这个文件中的 NETCONFIG_DNS_STATIC_SERVERS="" 改成了 NETCONFIG_DNS_STATIC_SERVERS="8.8.8.8 8.8.4.4" 。实在不会的，可以自行用bing搜索。3.添加解析记录上面的方式可以更换DNS。有时想通过域名访问局域网的机器（比如想把 mycomputer.com 绑定到 192.168.1.10 ），也是可以做到的。（0）路由器设置解析记录有少部分路由器支持。如果刷了openwrt，也可以设置，这里不讲解了。（1）电脑设置如果路由器无法设置，可以找到hosts文件，它的路径：Windows下，在 C:\Windows\System32\drivers\etc\hosts Linux下，在 /etc/hosts按照里面的例子，添加一条解析记录即可。这样，在该电脑上，就能通过域名访问内网机器。