301重定向循环研究

 

今天要公布的重要研究报告就是关于上图,也就是重定向循环。重定向也就是在服务器请求页面时,服务器HTTP在报文头部写入新的Location使页面跳转的过程。

如果请求页面A,A重定向到页面B,而页面B又重定向到A就发生了一个重定向循环。幸运的是浏览器在发生重定向循环时不会一直在两个页面间无限跳转,而是在跳转一定次数后显示上图所示的画面。

但是跳转多少次后浏览器会出现上面的页面呢,本实验室经过仔细地尝试,发现各个浏览器在这个问题上的处理上有很大的不同。

我们决定使用三个非常小的脚本程序来测试这个问题。

脚本A

<?php
setcookie("in",3);
header("location: http://localhost/301laboratory/test_4.php");

if ($_COOKIE['first']==15)
header("location: http://www.baidu.com");
?>

用来记录浏览器到过脚本A,并重定向到脚本B
<?php
setcookie("in",4);
$new = $_COOKIE['first']+1;
setcookie("first",$new);
header("location: http://localhost/301laboratory/test_3.php");
?>
最后脚本C
用来方便显示现在的COOKIE
<?php
print_r($_COOKIE['first']);
echo"<br/>";
print_r($_COOKIE['in']);
setcookie("first",0);
setcookie("in",0);
?>



首先是CHROME浏览器上,我们运行脚本A,很遗憾,它没能跳到脚本B15次就显示出包含重定向循环,这时我们查看脚本C,发现显示如下:


说明CHROME跳转去脚本B 10次,在服务器第11次让浏览器跳转到脚本B时,CHROME判定这个页面包含重定向循环,不予以跳转,因为第二值为3,说明最后执行脚本为A。


然后我们开始测试IE,IE上运行脚本A,能够正确地跳转到百度,于是我们改大了判定跳转次数为100,IE不能正确跳转了,查看脚本C,结果为55,3,如下图:

即是IE跳转到B,55次后拒绝继续跳转,但是IE上我们发现了奇怪的特性,如下图:

就是当服务器每第5次让IE跳转到脚本B时,IE会怀疑服务器给的命令有误,拒绝跳转到脚本B,并重新刷新脚本A,当脚本A再次让它跳转到脚本B的时候它才会执行,当这种情况出现到第10次时,IE在拒绝跳转到脚本B的同时也不再刷新脚本A,显示此页面包含重定向循环。


接下来试验safari,在safari上运行脚本A,浏览器没有跳转去百度,显示:

于是去查看脚本C,惊奇地竟然显示为,看起来似乎浏览器完全没有跳转过。
改小判定跳转百度的次数为7,safari可以跳转至百度,再取查看脚本C,显示结果依然为0,0


于是我们猜想是否safari存在沙盒机制判定出包含重定向后,safari不会将COOKIE写入。

只好修改脚本,改用session。session在服务器端,应该不受safari影响,无奈测试结果仍然是0,0.

再次修改脚本,在脚本B中连接数据库,

修改脚本B为
<?php
$dbhost="localhost";
$dbuser="root";
$dbname="301library";
$dbpassword="root";
$conn=mysql_connect($dbhost,$dbuser,$dbpassword) or die("shibai");
mysql_select_db($dbname,$conn) or die("dberror");
mysql_query("SET NAMES 'UTF8'");
session_start();
$_SESSION['in']=4;
$_SESSION['first']++;
$sess=$_SESSION['first'];
$sql="UPDATE test set `value`=$sess where id = 1";
mysql_query($sql);

	header("location: http://localhost/301library/test_3.php");

?>
再次运行脚本A,终于在数据库中看见了结果,
8,3
也就是说safari判定重定向的次数是8次。


至于上面说的safari显示0,0的问题,本实验室还没结果,有兴趣的可以自己试试。

<think>嗯,用户这次问的是在领芯微环境中实现printf重定向。从引用材料看,用户已经研究了STM32和中颖51的实现方案,但领芯微是另一家国产MCU厂商,需要针对性适配。用户可能正在调试领芯微开发板,急需打通串口打印功能。ta引用四篇技术文档却仍找不到解决方案,说明现有资料不直接适用。深层需求应该是:如何在缺乏官方文档时快速实现基础调试功能。需要抓住重定向的核心原理:无论什么平台,printf最终都依赖fputc这个底层输出函数。领芯微虽然没有现成示例,但串口驱动应该具备发送单字节的能力。关键是要找到:1)串口状态寄存器地址2)数据寄存器地址3)有效的状态检测方式。用户引用的STM32示例很有参考价值:野火的代码用USART->DR寄存器发送,中颖用SFRBUF。推测领芯微也会有类似的UART数据寄存器,可能叫UART_TXDATA或UART_TXR。比较棘手的是状态检测。用户展示的三种实现中:STM32查SR寄存器状态位,中颖可能用中断标志,野火用USART_GetFlagStatus封装函数。建议优先查找领芯微的头文件里是否有类似UART_STS_TXRDY的定义。安全方面要提醒三点:避免在中断里使用printf,注意浮点数处理可能占用大量资源,记得关闭编译器半主机模式——这点用户引用的STM32教程提到过,但51平台通常不需要。最后生成的三个问题都围绕优化展开:复用uart_write避免重复开发,处理多串口场景,解决传输阻塞问题。特别是第三个,用户实际调试时可能会遇到程序卡在while循环的情况,需要提前预警。</think> 在领芯微(LSC)单片机环境中实现`printf`函数重定向的核心原理是**重写底层`fputc`函数**,将标准库的输出重定向到串口。以下是具体实现步骤: --- ### **一、实现步骤** #### 1. **添加头文件** ```c #include <stdio.h> // 标准输入输出库 #include "lsc_uart.h" // 领芯微串口驱动头文件(具体名称需根据型号调整) ``` #### 2. **重写`fputc`函数** 在代码中实现以下函数: ```c int fputc(int ch, FILE *stream) { UART_SendByte(LSC_UART1, (uint8_t)ch); // 发送单个字节 while (UART_GetStatus(LSC_UART1) & UART_BUSY); // 等待发送完成(阻塞式) return ch; } ``` - **关键点**: - `UART_SendByte`: 领芯微串口发送函数(参考官方库函数名)。 - `UART_GetStatus`: 获取串口状态寄存器值。 - **状态标志位**:需查找领芯微数据手册中“发送完成”标志(例如 `TX_COMPLETE` 或 `UART_FLAG_TXE`)。 #### 3. **开启编译器优化(可选)** 若未使用半主机模式(常见于ARM),需在IDE中设置: ``` --specs=nosys.specs // 关闭半主机模式(GCC编译器) ``` #### 4. **初始化串口** 在`main()`中配置串口参数: ```c void UART_Init(void) { UART_Config_t uart_cfg = { .baud_rate = 115200, .data_bits = 8, .stop_bits = 1, .parity = UART_PARITY_NONE }; UART_Init(LSC_UART1, &uart_cfg); } ``` --- ### **二、完整示例代码** ```c #include "lsc_mcu.h" // 领芯微MCU头文件 #include <stdio.h> // 重定向fputc int fputc(int ch, FILE *f) { UART_SendData(LSC_UART0, (uint8_t)ch); // 发送数据 while (!(UART_GetStatus(LSC_UART0) & UART_FLAG_TX_EMPTY)); // 等待发送完成 return ch; } int main() { UART_Init(LSC_UART0, 115200); // 初始化串口0 printf("Hello LSC Micro!\n"); // 测试输出 while(1); return 0; } ``` --- ### **三、注意事项** 1. **串口寄存器名称** 需替换 `UART_SendData`、`UART_GetStatus` 等为领芯微实际函数名,寄存器标志位请查阅 **数据手册**(如TX完成标志可能是 `TXE`、`TC` 或 `BUSY`)。 2. **阻塞式发送优化** 若需非阻塞发送,可结合中断实现环形缓冲区(参考野火开发板的DMA方案[^4])。 3. **浮点数支持** 若需输出`float`,需在编译器选项中启用 `-u _printf_float`(GCC)。 --- ### **四、调试建议** 1. 检查串口引脚配置(如TX/RX是否映射正确)。 2. 用逻辑分析仪捕捉串口波形,确认波特率匹配。 3. 若输出乱码,检查时钟源精度(尤其是内部RC振荡器需校准)。 --- ### 相关问题 1. **如何实现重定向到多个串口?** > 可通过自定义输出函数(如 `my_printf(UARTx, format, ...)`),在函数内动态选择串口号发送。 2. **如何在中断中安全使用`printf`?** > 需避免直接调用(因阻塞等待可能导致中断丢失)。推荐使用**队列+后台线程**:中断内将数据存入队列,主循环中从队列取出并通过`printf`发送。 3. **如何优化`printf`的内存占用?** > ① 使用简化库(如 `tinyprintf`); > ② 避免浮点数转换; > ③ 静态分配发送缓冲区(替代动态内存分配)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值