同一PHP文件内Ajax与Socket调用共存报10048错误解决方案
问题原因
你碰到的10048端口占用报错,核心原因是把HTTP短请求处理逻辑(Ajax对接的部分)和常驻内存的TCP Socket服务逻辑写在了同一个PHP文件里,二者运行模型完全不兼容:
- 不管是直接打开页面还是发Ajax请求,只要通过Web服务器(Apache/Nginx)访问这个PHP文件,PHP就会从头执行一遍全部代码,每次执行都会尝试绑定4324端口启动Socket服务
- 第一次请求执行到Socket监听的无限循环后,进程会常驻后台占住4324端口,后续所有访问这个文件的请求(包括你发的Ajax)执行到
socket_bind的时候,端口已经被之前启动的进程占用,直接抛错 - 就算解决了端口问题,你的逻辑也跑不通:Ajax请求里初始化的
$rooms变量是当前请求进程的临时变量,和常驻的Socket进程内存完全隔离,Socket部分根本拿不到前端传过来的卡牌数据 - 代码里还有几个显性bug:
- Ajax用POST方法发起请求,后端却读
$_GET["q"]取参数 - JS里
var rCards = 0;初始值是数字,后面当数组按下标赋值会有隐式类型错误 $rooms变量定义在if(isset($_POST["q"]))的分支里,Socket逻辑里直接global调用,参数没传的时候会报未定义变量错误
- Ajax用POST方法发起请求,后端却读
可行解决方案
不要试图在同一个通过Web访问的PHP文件里同时跑HTTP接口和Socket服务,按职责拆分逻辑即可:
1. 拆分文件
- 新建单独的
socket_server.php文件,把所有Socket创建、绑定、监听、循环处理连接的代码全部挪到这个文件里,这个文件只能通过命令行(CLI模式)启动,绝对不要通过Web地址访问 - 原来的
server.php只保留页面渲染、Ajax请求处理的逻辑,删掉所有Socket相关代码
2. 解决数据互通问题
HTTP请求是短生命周期的,和常驻的Socket进程内存不共享,你之前想通过Ajax把卡牌数据传给Socket服务的思路走不通,选任意一种方式实现配置传递:
- 最省事的方案:把卡牌初始化、房间洗牌的逻辑直接写在
socket_server.php里,启动Socket服务的时候直接生成所有房间数据,不需要前端传 - 如果确实需要前端动态传配置:前端把卡牌数据通过Ajax发给
server.php后,server.php把数据存到本地配置文件、Redis或者数据库里,Socket服务收到对应请求的时候直接读存储里的配置即可
3. 修复代码细节问题
- 在
socket_server.php里,socket_create之后、socket_bind之前加端口复用配置,避免服务重启时短暂端口占用:
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
- 修正Ajax参数读取逻辑:如果把参数拼在URL上传,后端就读
$_GET;如果走POST传参,就把参数放到xhttp.send()里,后端读$_POST,不要混用 - JS里
var rCards = 0;改成var rCards = [];,初始化数组再赋值
4. 正确启动流程
- 先打开命令行,执行
php socket_server.php启动Socket服务,保持进程运行不要关闭 - 再正常访问Web页面,前端普通业务交互走Ajax请求
server.php,游戏长连接逻辑直接连127.0.0.1:4324的Socket服务即可
为什么同文件跑不通
PHP的Web运行模式是请求级隔离的:每次请求进来执行完代码就会回收所有资源,而Socket服务需要进程一直常驻、持续占用端口监听连接,二者模型天生冲突:
- 如果保留Socket无限循环,代码执行到循环处就会卡住,请求永远不会返回响应,Ajax拿不到结果
- 如果每次请求都尝试启动Socket,第二次请求就会因为端口被占直接报错
- 就算加判断跳过已启动的Socket绑定逻辑,不同PHP请求进程之间内存不互通,你存在当前请求里的房间数据,别的请求或者Socket进程根本读不到,业务逻辑完全跑不通
内容的提问来源于stack exchange,提问作者mohammadhosseinborhani
相关产品推荐
相关产品推荐

