多个XMLHttpRequest请求无法向后端发送数据如何排查解决
问题排查与修复方案
核心错误点
- 发送条件逻辑错误:原代码仅当W按键缓存有值时才会触发发送逻辑,用户仅按下A/S/D按键时不会执行任何请求
- 多请求逻辑混乱:同时发起4个XHR请求,每个请求仅携带单个按键的参数,每个请求结束都会提前解锁,锁机制完全失效;且单个请求到达PHP后,另外三个按键参数不存在,会直接清空对应文件的内容
- 数据格式错误:
FormData.append()传入数组时会自动转为逗号分隔的字符串,PHP侧直接json_encode后存储格式不符合预期 - 文件写入模式错误:PHP用
w模式打开文件,每次写入都会清空之前的所有记录,无法累计保存按键数据
修复后的代码
JavaScript代码
var keylog = { // (A) 设置与属性 cacheW: [], // 按键临时存储 cacheA: [], cacheD: [], cacheS: [], delay: 500, // 向服务端发送数据的间隔(毫秒) sending: false, // 同一时间仅允许一个上传请求 // (B) 初始化 init: function() { // (B1) 捕获按键动作 window.addEventListener("keydown", function(evt) { switch(evt.key) { case "w": keylog.cacheW.push(evt.key); break; case "a": keylog.cacheA.push(evt.key); break; case "d": keylog.cacheD.push(evt.key); break; case "s": keylog.cacheS.push(evt.key); break; } }); window.addEventListener("keyup", function(evt) { switch(evt.key) { case "w": keylog.cacheW.push("!" + evt.key); break; case "a": keylog.cacheA.push("!" + evt.key); break; case "d": keylog.cacheD.push("!" + evt.key); break; case "s": keylog.cacheS.push("!" + evt.key); break; } }); // (B2) 定时向服务端发送按键数据 window.setInterval(keylog.send, keylog.delay); }, // (C) AJAX发送按键数据 send: function() { // 只要任意一个缓存有数据就发送 const hasData = keylog.cacheW.length || keylog.cacheA.length || keylog.cacheD.length || keylog.cacheS.length; if (!keylog.sending && hasData) { // (C1) 加锁,直到本次请求完成再解锁 keylog.sending = true; // (C2) 所有数据整合到同一个FormData里单次发送 var data = new FormData(); // 数组转JSON字符串再上传,避免自动转成逗号分隔的字符串 data.append("keysW", JSON.stringify(keylog.cacheW)); data.append("keysA", JSON.stringify(keylog.cacheA)); data.append("keysD", JSON.stringify(keylog.cacheD)); data.append("keysS", JSON.stringify(keylog.cacheS)); // 清空缓存 keylog.cacheW = []; keylog.cacheA = []; keylog.cacheD = []; keylog.cacheS = []; // (C3) 单个AJAX请求发送所有数据 var xhr = new XMLHttpRequest(); xhr.open("POST", "index4.php"); xhr.onload = function() { keylog.sending = false; // 请求完成解锁 // console.log(this.response); }; xhr.send(data); } } }; window.addEventListener("DOMContentLoaded", keylog.init);
PHP代码
<?php // 先判断参数是否存在,不存在就不处理对应文件,避免报错 if (isset($_POST['keysW'])) { $keysW = $_POST['keysW']; $keylogW = 'player2KeylogW.txt'; // 用a模式追加写入,不会清空之前的内容,需要覆盖则换回w $writeKeylogW = fopen($keylogW, "a") or die("Unable to open W file!"); // 直接写入拿到的JSON字符串即可,不需要再json_encode fwrite($writeKeylogW, $keysW . PHP_EOL); fclose($writeKeylogW); chmod($keylogW , 0755); } if (isset($_POST['keysA'])) { $keysA = $_POST['keysA']; $keylogA = 'player2KeylogA.txt'; $writeKeylogA = fopen($keylogA, "a") or die("Unable to open A file!"); fwrite($writeKeylogA, $keysA . PHP_EOL); fclose($writeKeylogA); chmod($keylogA , 0755); } if (isset($_POST['keysD'])) { $keysD = $_POST['keysD']; $keylogD = 'player2KeylogD.txt'; $writeKeylogD = fopen($keylogD, "a") or die("Unable to open D file!"); fwrite($writeKeylogD, $keysD . PHP_EOL); fclose($writeKeylogD); chmod($keylogD , 0755); } if (isset($_POST['keysS'])) { $keysS = $_POST['keysS']; $keylogS = 'player2KeylogS.txt'; $writeKeylogS = fopen($keylogS, "a") or die("Unable to open S file!"); fwrite($writeKeylogS, $keysS . PHP_EOL); fclose($writeKeylogS); chmod($keylogS , 0755); } ?>
补充说明
浏览器本身允许向同一个服务端地址发送多个请求,你遇到的问题完全是业务逻辑错误导致的。如果修复后仍有异常,可以打开浏览器控制台的「网络」标签,查看XHR请求的状态、请求参数、返回值快速定位问题。
内容的提问来源于stack exchange,提问作者seriously
相关产品推荐
相关产品推荐

