家里的旧电脑还在吃灰?代码一上线就趴窝?仅需简单3个步骤将闲置旧设备利用起来,搭建博客、测试环境、NAS都不在话下。

演示环境

网络:中国移动宽带+移动烽火路由器

笔记本电脑:2核4线程CPU+16G内存+500G固态硬盘

系统:win10企业版+

虚拟机内系统:VMware+rocky Linux 9.6

域名:阿里云+DNS解析

脚本环境:Python 3.11.9

详细步骤

1. 网络准备

1.1 启用公网ipv6

登录路由器后台启用ipv6,我这里是移动的网络,默认管理地址是192.168.1.1,账户和密码在路由器背面。移动路由器需要admin账号才有权限修改,获取移动路由器admin账号见文末。

登录 -> 网络 -> 带宽设置 -> 网络连接:选择4_INTERNET_R_VID_41勾选ipv6并保存。

注:不同路由器启用IPv6的路径各不相同,比如小米路由器只需要在网络里勾选ipv6即可。

1.2 测试公网地址

在路由器上启用ipv6后只需重启电脑即可获取公网ipv6地址。

重启后通过WIN+R输入cmd并回车打开终端,在终端输入ipconfig并回车即可查看公网ipv6地址。

如图2409开头的IPv6地址就是公网地址,尽量不要使用临时IPv6地址,临时的变更频率比较快。

复制地址后在另一台主机上连接手机热点使用ping ipv6地址 来测试是否能ping通。(ping前确保被访问主机防火墙ICMP服务为放行状态)

注:如无法访问建议关掉路由器ipv6防火墙,有一定风险,自行斟酌。

2. DDNS解析

2.1 域名解析

获取到公网ipv6地址后前往阿里云官网 -> 控制台 -> 云解析DNS -> 点击对应域名的解析设置 -> 添加记录:记录类型AAAA、主机记录根据需求配置,可设置一个二级域名或使用主域名、记录值为你的公网IPv6地址 -> 确定。

注:确保你已经在阿里云购买或已经拥有一个域名。

2.2 新建RAM用户

鼠标放在阿里云右上角头像上会弹出窗口 -> 访问控制 -> 用户 -> 创建用户 -> 填写账户名并勾选 使用永久 AccessKey 访问 -> 确认并保存账户和密码,一定要保存好,否则需要重新创建 -> 找到刚刚新建的用户添加权限 -> 勾选只读访问云解析(DNS)的权限、管理云解析(DNS)的权限 -> 确定。

2.3 配置脚本

在这之前确保你拥有Python环境和阿里云sdk,文末有教程。在桌面新建txt文件,将下方Python脚本复制到txt文件内,修改配置区域的对应配置,将文件保存为ddnsUpData.py,文件名不要紧,后缀一定要是.py。将文件放到在你找得到的地方,方便使用。以上都配置好后就可以双击脚本文件测试是否正常运行。

# -*- coding: utf-8 -*-
import subprocess
import re
import socket
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ClientException
from aliyunsdkalidns.request.v20150109 import DescribeDomainRecordsRequest, UpdateDomainRecordRequest
import json

# ==================== 配置区域 ====================
ACCESS_KEY_ID = 'RAM用户id'
ACCESS_SECRET = 'RAM用户SECRET'

DOMAIN_NAME = '主域名,如example.com'
RECORD_TYPE = 'AAAA'
RECORD_LINE = 'default'
HOST_RECORD = '主机记录(例如设置为www则解析www.example.com)'
# ==================== 配置区域 ====================

def is_public_ipv6(ip):
    """判断是否是公网 IPv6 地址(排除 fe80::/10 和 ::1)"""
    return not ip.startswith("fe80") and not ip.startswith("::1")

def get_public_ip(ipv6=True):
    """
    获取公网 IPv6 地址(优先获取固定 IPv6 地址)
    如果没有固定 IPv6,则尝试使用临时 IPv6
    如果都没有,则回退到 IPv4
    """
    if ipv6:
        try:
            result = subprocess.check_output(['ipconfig'], stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL, text=True)
            lines = result.splitlines()

            fixed_ipv6 = None
            temp_ipv6 = None

            for i, line in enumerate(lines):
                # 匹配 IPv6 地址(中英文兼容)
                ipv6_match = re.search(r"(IPv6 Address|IPv6 地址)[\. ]+: ([0-9a-fA-F:]+%?\d*)", line)
                if ipv6_match:
                    ip = ipv6_match.group(2)

                    # 判断是否是公网地址
                    if not is_public_ipv6(ip):
                        continue

                    # 判断当前行是否是 "临时 IPv6 地址"
                    is_temp = re.search(r"(Temporary IPv6 Address|临时 IPv6 地址)", line)

                    if is_temp:
                        if temp_ipv6 is None:
                            temp_ipv6 = ip
                            print(f"[信息] 找到临时IPv6地址: {ip}")
                    else:
                        if fixed_ipv6 is None:
                            fixed_ipv6 = ip
                            print(f"[信息] 找到固定公网IPv6地址: {ip}")

            # 优先返回固定公网IPv6地址
            if fixed_ipv6:
                print(f"[信息] 使用固定公网IPv6地址: {fixed_ipv6}")
                return fixed_ipv6

            # 如果没有固定地址,尝试使用临时地址
            if temp_ipv6:
                print(f"[信息] 使用临时IPv6地址作为备用: {temp_ipv6}")
                return temp_ipv6

            print("[警告] 未找到公网IPv6地址,尝试获取IPv4地址...")
        except Exception as e:
            print(f"[错误] 获取IPv6地址失败({e}),尝试获取IPv4地址...")

    # 回退到 IPv4
    return get_public_ipv4()
def get_public_ipv4():
    """获取公网IPv4地址"""
    try:
        addresses = socket.getaddrinfo(socket.gethostname(), None, socket.AF_INET)
        for addr in addresses:
            ip = addr[4][0]
            if ip != "127.0.0.1":
                print(f"[信息] 检测到IPv4地址: {ip}")
                return ip
        print("[错误] 未找到有效的IPv4地址")
        return None
    except Exception as e:
        print(f"[错误] 获取IPv4地址失败: {e}")
        return None

def get_record_id(client):
    request = DescribeDomainRecordsRequest.DescribeDomainRecordsRequest()
    request.set_DomainName(DOMAIN_NAME)
    request.set_Type(RECORD_TYPE)
    request.set_RRKeyWord(HOST_RECORD)

    try:
        response = client.do_action_with_exception(request)
        result = json.loads(response.decode('utf-8'))

        records = result.get('DomainRecords', {}).get('Record', [])
        for record in records:
            if record['RR'] == HOST_RECORD and record['Type'] == RECORD_TYPE:
                return record['RecordId'], record['Value']

        print(f"[警告] 未找到匹配的DNS记录,请检查配置")
        return None, None
    except Exception as e:
        print(f"[错误] 阿里云API调用失败: {e}")
        return None, None

def update_dns_record(client, record_id, new_ip):
    request = UpdateDomainRecordRequest.UpdateDomainRecordRequest()
    request.set_RecordId(record_id)
    request.set_RR(HOST_RECORD)
    request.set_Type(RECORD_TYPE)
    request.set_Value(new_ip)
    request.set_Line(RECORD_LINE)

    try:
        response = client.do_action_with_exception(request)
        result = json.loads(response.decode('utf-8'))
        if result.get('RecordId') == record_id:
            print(f"[成功] DNS记录已更新为: {new_ip}")
            return True
        return False
    except Exception as e:
        print(f"[错误] 更新DNS记录失败: {e}")
        return False

def main():
    client = AcsClient(ACCESS_KEY_ID, ACCESS_SECRET, 'cn-hangzhou')

    current_ip = get_public_ip(ipv6=True)
    if not current_ip:
        print("[错误] 无法获取公网IP地址")
        return

    # 可选:去掉 IPv6 地址中的 %11 网卡标识符
    current_ip = current_ip.split("%")[0]

    print(f"[信息] 当前公网IP地址: {current_ip}")

    record_id, old_ip = get_record_id(client)
    if not record_id:
        print("[错误] 无法获取DNS记录ID")
        return

    print(f"[信息] 当前DNS记录值: {old_ip}")

    if current_ip == old_ip:
        print("[信息] IP地址未发生变化,无需更新")
        return

    if update_dns_record(client, record_id, current_ip):
        print("[完成] DNS更新成功")
    else:
        print("[失败] DNS更新失败")

if __name__ == '__main__':
    main()

2.4 定期启动脚本更新ip地址

如果脚本正常运行则使用“任务计划程序”进行定期执行:windows搜索并打开“任务计划程序” -> 创建任务 -> 勾选使用最高权限运行和不管用户是否登录运行 -> 触发器勾选重复任务间隔30分钟,持续期限无限期,勾选启用并确认 ->点击操作,选择启动程序,选择刚刚脚本所在位置并确定 ->设置好后点击 确定 -> 点击刚刚创建的任务,选择运行,查看脚本是否运行成功。可以在历史记录里看每是否运行。

到这里,你已经拥有了一个公网地址,只需要通过域名放即可,远程桌面等也可以直接使用域名。(记得防火墙放行)

3. 准备服务器环境(可选)

3.1 安装虚拟机系统

根据个人爱好选择vm或vb虚拟机都可以,具体安装步骤和系统的选择这里就不讲了,图形化界面很简单,不会的百度很多教程。

3.2 设置ipv6地址端口转发

由于vm或vb虚拟机目前都不支持ipv6的nat地址转换,同时虚拟机的桥接模式无法获取路由器下发的ipv6前缀,所以这里使用cmd命令设置,将本机的公网ipv6地址端口映射到虚拟机ipv4局域网地址的对应端口。

netsh interface portproxy add v6tov4 listenaddress=:: listenport=主机的ipv6地址的端口 connectaddress=虚拟机局域网地址 connectport=虚拟机端口

# 查看现以设置的所有端口代理规则
netsh interface portproxy show all

# 删除指定规则
netsh interface portproxy delete v6tov4 listenaddress=:: listenport=要删除规则的主机的ipv6地址的端口

注:由于大部分家庭宽带的80、443、22、8080等端口都处于封禁状态,建议避开这几个端口,如果有需求,可以使用公网服务器的反向代理进行访问。

4.3 访问测试

基本完成,可自行安装web服务器进行访问测试。

其他问题解答

1. 获取移动路由器admin账号

由于移动路由器必须使用admin才能修改ipv6设置,这里讲解如何获取admin账户和密码。

首先查看路由器背面的mac地址如:AA-BB-CC-DD-EE-FF,将其“-”去掉获得:AABBCCDDEEFF。

浏览器输入http:/192.168.1.1/cgi-bin/telnetenable.cgi?telnetenable=1&key=自行替换为去掉-的mac地址 并回车,这时会看到telnet开启等字样,现在打开cmd终端输入telnet 192.168.1.1并回车,按提示输入admin账户,密码为Fh@mac地址后6位,如Fh@DDEEFF。

先输入load_cli factory

其次输入show admin_name 获取账户名

再输入show admin_pwd 获取密码,这个密码是临时的。

使用获取的账户名和密码登录并进行配置修改。

2. Python脚本环境准备

去官网下载Python官网下载python并安装https://www.python.org/downloads/windows/

使用cmd终端执行以下命令安装aliyunSDK

pip install aliyun-python-sdk-core

pip install aliyun-python-sdk-alidns

缺一不可。

3. windows防火墙放行

点击跳转教程

4. Windows远程桌面

点击跳转教程