在本篇文章中,我们将介绍一个使用 C# 编写的程序,该程序可以自动登录到路由器并定时重启它。程序利用 HttpClient
和异步编程来执行网络请求,确保高效性。下面将详细分析程序的各个部分及其功能。
其实用telnet来实现更简单,问题是一般的路由器没有提供这个功能。
功能概述 我们将开发一个控制台应用程序,具有以下功能:
自动登录路由器管理界面 定时执行路由器重启命令(默认1小时一次) 程序启动时立即执行一次重启 完整的错误处理和日志记录 技术实现 所需依赖 using System; using System.Net; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; using System.Text.RegularExpressions;
基础配置 首先,我们需要设置路由器的基本信息:
private static string routerAddress = "http://192.168.18.1" ; private static string username = "user" ; // 根据路由器设置修改 private static string password = "yourpassword" ; // 替换为您的路由器密码 private static CookieContainer cookieContainer = new CookieContainer(); private static HttpClientHandler handler; private static HttpClient client;
主程序结构 static async Task Main ( string [] args) { Console.WriteLine( "路由器定时重启程序已启动" ); // 初始化HTTP客户端 InitializeHttpClient(); // 设置定时器 - 24小时执行一次 SetupTimer(); // 初次启动也执行一次 await RebootRouter () ; Console.WriteLine( "按任意键退出..." ); Console.ReadKey(); }
HTTP客户端初始化 private static void InitializeHttpClient () { handler = new HttpClientHandler { CookieContainer = cookieContainer, UseCookies = true }; client = new HttpClient(handler) { BaseAddress = new Uri(routerAddress), Timeout = TimeSpan.FromSeconds( 30 ) }; // 设置默认的User-Agent和其他请求头 client.DefaultRequestHeaders.Add( "User-Agent" , "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36" ); client.DefaultRequestHeaders.Add( "Accept" , "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7" ); client.DefaultRequestHeaders.Add( "Accept-Language" , "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7" ); }
定时器设置 private static void SetupTimer () { var timer = new System.Timers.Timer( 1 * 60 * 60 * 1000 ); // 1小时 timer.Elapsed += async (sender, e) => { try { await RebootRouter(); } catch (Exception ex) { Console.WriteLine($ "[{DateTime.Now}] 定时重启出错: {ex.Message}" ); } }; timer.AutoReset = true ; timer.Start(); }
路由器重启流程 重启路由器的过程分为两个主要步骤:登录路由器和执行重启命令。
private static async Task RebootRouter () { try { Console.WriteLine($ "[{DateTime.Now}] 开始重启路由器..." ); // 第1步:登录到路由器 bool loginSuccess = await LoginToRouter(); if (loginSuccess) { // 第2步:执行重启命令 bool rebootSuccess = await ExecuteReboot(); if (rebootSuccess) { Console.WriteLine($ "[{DateTime.Now}] 路由器重启命令已发送成功!" ); } else { Console.WriteLine($ "[{DateTime.Now}] 路由器重启命令发送失败!" ); } } else { Console.WriteLine($ "[{DateTime.Now}] 登录路由器失败!" ); } } catch (Exception ex) { Console.WriteLine($ "[{DateTime.Now}] 错误: {ex.Message}" ); } }
登录路由器 登录过程模拟了浏览器的行为,包括获取随机计数值、设置Cookie、发送登录请求等步骤:
private static async Task< bool > LoginToRouter() { try { // 首先访问登录页面获取初始Cookie var initialResponse = await client.GetAsync( "/" ); // 获取随机计数值(用于安全验证) var randCountResponse = await client.PostAsync( "/asp/GetRandCount.asp" , null); string randCount = await randCountResponse.Content.ReadAsStringAsync(); Console.WriteLine($ "获取到的随机计数: {randCount}" ); // 设置初始Cookie cookieContainer.Add( new Uri(routerAddress), new Cookie( "Cookie" , "body:Language:chinese:id=-1" )); cookieContainer.Add( new Uri(routerAddress), new Cookie( "MenuJumpIndex" , "0" )); // 准备登录参数 var loginParams = new Dictionary< string , string > { { "UserName" , username }, { "PassWord" , Convert.ToBase64String(Encoding.UTF8.GetBytes(password)) }, { "Language" , "chinese" }, { "x.X_HW_Token" , randCount } }; // 创建表单内容 var loginContent = new FormUrlEncodedContent(loginParams); // 添加引用页头 client.DefaultRequestHeaders.Referrer = new Uri(routerAddress); // 发送登录请求 var loginResponse = await client.PostAsync( "/login.cgi" , loginContent); // 检查登录结果 if (loginResponse.IsSuccessStatusCode) { // 获取登录后的页面内容 string responseContent = await loginResponse.Content.ReadAsStringAsync(); // 登录后应该被重定向到index.asp var indexResponse = await client.GetAsync( "/index.asp" ); if (indexResponse.IsSuccessStatusCode) { Console.WriteLine( "登录成功!" ); return true ; } } return false ; } catch (Exception ex) { Console.WriteLine($ "登录过程中出错: {ex.Message}" ); return false ; } }
执行重启命令 执行重启命令需要先获取页面中的安全令牌(onttoken),然后发送重启请求:
private static async Task< bool > ExecuteReboot() { try { // 重启功能在 resetfactory.asp 页面 Console.WriteLine( "访问重启页面..." ); var resetPageUrl = "/html/ssmp/restore/resetfactory.asp" ; var resetPageResponse = await client.GetAsync(resetPageUrl); if (!resetPageResponse.IsSuccessStatusCode) { Console.WriteLine($ "访问重启页面失败,状态码: {resetPageResponse.StatusCode}" ); return false ; } // 获取页面内容以提取 onttoken var pageContent = await resetPageResponse.Content.ReadAsStringAsync(); // 匹配模式 <input type="hidden" name="onttoken" id="hwonttoken" value="..."> var tokenMatch = Regex.Match(pageContent, @ "<input\s+type=" "hidden" "\s+name=" "onttoken" "\s+id=" "hwonttoken" "\s+value=" "([^" "]+)" "" , RegexOptions.IgnoreCase); if (!tokenMatch.Success) { Console.WriteLine( "无法找到onttoken值" ); return false ; } string tokenValue = tokenMatch.Groups[ 1 ].Value; Console.WriteLine($ "获取到的onttoken值: {tokenValue}" ); // 设置重启请求的引用页 client.DefaultRequestHeaders.Referrer = new Uri($ "{routerAddress}{resetPageUrl}" ); // 准备重启请求参数 var rebootParams = new Dictionary< string , string > { { "x.X_HW_Token" , tokenValue } }; // 创建重启请求内容 var rebootContent = new FormUrlEncodedContent(rebootParams); // 发送重启请求 var rebootUrl = "set.cgi?x=InternetGatewayDevice.X_HW_DEBUG.SMP.DM.ResetBoard&RequestFile=html/ssmp/restore/resetfactory.asp" ; var rebootResponse = await client.PostAsync(rebootUrl, rebootContent); if (rebootResponse.IsSuccessStatusCode) { Console.WriteLine( "重启命令已成功发送" ); return true ; } else { Console.WriteLine($ "重启命令发送失败,状态码: {rebootResponse.StatusCode}" ); var responseContent = await rebootResponse.Content.ReadAsStringAsync(); Console.WriteLine($ "响应内容: {responseContent}" ); return false ; } } catch (Exception ex) { Console.WriteLine($ "执行重启时出错: {ex.Message}" ); return false ; } }
完整代码示例 下面是完整的定时重启路由器程序代码:
using System; using System.Net; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; using System.Text.RegularExpressions; namespace AppRebootRouter { class Program { private static string routerAddress = "http://192.168.18.1" ; private static string username = "user" ; // 根据路由器设置修改 private static string password = "yqv2psyy" ; // 替换为您的路由器密码 private static CookieContainer cookieContainer = new CookieContainer(); private static HttpClientHandler handler; private static HttpClient client; static async Task Main ( string [] args) { Console.WriteLine( "路由器定时重启程序已启动" ); // 创建一个HttpClient实例,这个一直会用的 handler = new HttpClientHandler { CookieContainer = cookieContainer, UseCookies = true }; client = new HttpClient(handler) { BaseAddress = new Uri(routerAddress), Timeout = TimeSpan.FromSeconds( 30 ) }; // 设置默认的User-Agent client.DefaultRequestHeaders.Add( "User-Agent" , "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36" ); client.DefaultRequestHeaders.Add( "Accept" , "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7" ); client.DefaultRequestHeaders.Add( "Accept-Language" , "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7" ); // 设置定时器 - 使用更可靠的方式实现定时功能 var timer = new System.Timers.Timer( 24 * 60 * 60 * 1000 ); // 24小时 timer.Elapsed += async (sender, e) => { try { await RebootRouter(); } catch (Exception ex) { Console.WriteLine($ "[{DateTime.Now}] 定时重启出错: {ex.Message}" ); } }; timer.AutoReset = true ; timer.Start(); // 初次启动也执行一次 await RebootRouter () ; Console.WriteLine( "按任意键退出..." ); Console.ReadKey(); } private static async Task RebootRouter () { try { Console.WriteLine($ "[{DateTime.Now}] 开始重启路由器..." ); // 第1步:登录到路由器 bool loginSuccess = await LoginToRouter(); if (loginSuccess) { // 第2步:执行重启命令 bool rebootSuccess = await ExecuteReboot(); if (rebootSuccess) { Console.WriteLine($ "[{DateTime.Now}] 路由器重启命令已发送成功!" ); } else { Console.WriteLine($ "[{DateTime.Now}] 路由器重启命令发送失败!" ); } } else { Console.WriteLine($ "[{DateTime.Now}] 登录路由器失败!" ); } } catch (Exception ex) { Console.WriteLine($ "[{DateTime.Now}] 错误: {ex.Message}" ); } } private static async Task< bool > LoginToRouter() { try { // 首先访问登录页面获取一些初始Cookie var initialResponse = await client.GetAsync( "/" ); // 获取随机计数值 var randCountResponse = await client.PostAsync( "/asp/GetRandCount.asp" , null); string randCount = await randCountResponse.Content.ReadAsStringAsync(); Console.WriteLine($ "获取到的随机计数: {randCount}" ); // 设置初始Cookie cookieContainer.Add( new Uri(routerAddress), new Cookie( "Cookie" , "body:Language:chinese:id=-1" )); cookieContainer.Add( new Uri(routerAddress), new Cookie( "MenuJumpIndex" , "0" )); // 准备登录参数 var loginParams = new Dictionary< string , string > { { "UserName" , username }, { "PassWord" , Convert.ToBase64String(Encoding.UTF8.GetBytes(password)) }, { "Language" , "chinese" }, { "x.X_HW_Token" , randCount } }; // 创建表单内容 var loginContent = new FormUrlEncodedContent(loginParams); // 添加引用页头 client.DefaultRequestHeaders.Referrer = new Uri(routerAddress); // 发送登录请求 var loginResponse = await client.PostAsync( "/login.cgi" , loginContent); // 检查登录结果 if (loginResponse.IsSuccessStatusCode) { // 获取登录后的页面内容 string responseContent = await loginResponse.Content.ReadAsStringAsync(); // 登录后应该被重定向到index.asp var indexResponse = await client.GetAsync( "/index.asp" ); if (indexResponse.IsSuccessStatusCode) { Console.WriteLine( "登录成功!" ); return true ; } } return false ; } catch (Exception ex) { Console.WriteLine($ "登录过程中出错: {ex.Message}" ); return false ; } } private static async Task< bool > ExecuteReboot() { try { // 重启功能在 resetfactory.asp 页面 Console.WriteLine( "访问重启页面..." ); var resetPageUrl = "/html/ssmp/restore/resetfactory.asp" ; var resetPageResponse = await client.GetAsync(resetPageUrl); if (!resetPageResponse.IsSuccessStatusCode) { Console.WriteLine($ "访问重启页面失败,状态码: {resetPageResponse.StatusCode}" ); return false ; } // 获取页面内容以提取 onttoken var pageContent = await resetPageResponse.Content.ReadAsStringAsync(); // 匹配模式 <input type="hidden" name="onttoken" id="hwonttoken" value="..."> var tokenMatch = Regex.Match(pageContent, @ "<input\s+type=" "hidden" "\s+name=" "onttoken" "\s+id=" "hwonttoken" "\s+value=" "([^" "]+)" "" , RegexOptions.IgnoreCase); if (!tokenMatch.Success) { Console.WriteLine( "无法找到onttoken值" ); return false ; } string tokenValue = tokenMatch.Groups[ 1 ].Value; Console.WriteLine($ "获取到的onttoken值: {tokenValue}" ); // 设置重启请求的引用页 client.DefaultRequestHeaders.Referrer = new Uri($ "{routerAddress}{resetPageUrl}" ); // 准备重启请求参数 var rebootParams = new Dictionary< string , string > { { "x.X_HW_Token" , tokenValue } }; // 创建重启请求内容 var rebootContent = new FormUrlEncodedContent(rebootParams); // 发送重启请求 var rebootUrl = "set.cgi?x=InternetGatewayDevice.X_HW_DEBUG.SMP.DM.ResetBoard&RequestFile=html/ssmp/restore/resetfactory.asp" ; var rebootResponse = await client.PostAsync(rebootUrl, rebootContent); if (rebootResponse.IsSuccessStatusCode) { Console.WriteLine( "重启命令已成功发送" ); return true ; } else { Console.WriteLine($ "重启命令发送失败,状态码: {rebootResponse.StatusCode}" ); var responseContent = await rebootResponse.Content.ReadAsStringAsync(); Console.WriteLine($ "响应内容: {responseContent}" ); return false ; } } catch (Exception ex) { Console.WriteLine($ "执行重启时出错: {ex.Message}" ); return false ; } } } }
实现原理详解 HTTP请求模拟 本程序本质上是通过模拟浏览器行为来访问路由器的管理界面。我们使用 HttpClient
类来发送HTTP请求,并使用 CookieContainer
来管理会话状态。这种方式无需使用浏览器自动化工具(如Selenium),效率更高,资源占用更少。
登录过程分析 登录一个路由器管理界面通常涉及以下几个步骤:
获取初始Cookie 获取安全令牌 :许多路由器会生成一个随机令牌用于防止CSRF攻击 处理凭据 发送POST请求 验证登录成功 在我们的实现中,通过访问 /asp/GetRandCount.asp
获取随机计数值,并使用它作为安全令牌。密码使用Base64编码后发送,这是许多路由器常见的做法。
重启命令执行 执行重启命令同样需要先获取安全令牌,这通常包含在页面的隐藏表单字段中。在这个例子中,我们使用正则表达式从重启页面的HTML内容中提取 onttoken
值:
var tokenMatch = Regex.Match(pageContent, @ "<input\s+type=" "hidden" "\s+name=" "onttoken" "\s+id=" "hwonttoken" "\s+value=" "([^" "]+)" "" , RegexOptions.IgnoreCase);
获取到令牌后,我们将其作为参数发送到路由器的重启API端点:
var rebootUrl = "set.cgi?x=InternetGatewayDevice.X_HW_DEBUG.SMP.DM.ResetBoard&RequestFile=html/ssmp/restore/resetfactory.asp" ;
定时执行机制 我们使用 System.Timers.Timer
类来实现定时功能,设置为每1小时执行一次重启操作:
var timer = new System.Timers.Timer( 1 * 60 * 60 * 1000 ); // 1小时 timer.Elapsed += async (sender, e) => { try { await RebootRouter(); } catch (Exception ex) { Console.WriteLine($ "[{DateTime.Now}] 定时重启出错: {ex.Message}" ); } }; timer.AutoReset = true ; timer.Start();
AutoReset = true
确保定时器会重复触发,而不是只执行一次。
适配不同路由器 本程序专为特定型号的路由器编写,如果要适配其他品牌或型号的路由器,需要根据实际情况修改以下几个方面:
路由器地址和登录凭据 private static string routerAddress = "http://192.168.18.1" ; // 路由器IP地址 private static string username = "user" ; // 用户名 private static string password = "yourpassword" ; // 密码
登录流程 不同路由器有不同的登录API和安全机制。需要分析目标路由器的登录请求,包括:
安全令牌获取方式 密码编码方式(有些路由器可能使用MD5而非Base64) 登录参数的名称和格式 重启命令路径 路由器重启命令的API端点和参数也各不相同:
重启页面的URL(如本例中的 /html/ssmp/restore/resetfactory.asp
) 重启API端点(如本例中的 set.cgi?x=…
) 安全令牌的获取和传递方式 总结 定时重启路由器是一个简单但实用的网络维护工具,通过模拟HTTP请求和自动化交互实现了路由器管理界面的操作自动化。本程序提供了一个完整的实现示例,包括登录验证、指令执行和定时触发功能,可以根据具体路由器型号和需求进行调整和扩展。
阅读原文:原文链接
该文章在 2025/5/14 10:04:54 编辑过