You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

跨网络Web终端与Android TCP Client的TCP服务架构选型咨询

解决方案与架构指导

一、持续监听的TCP Server实现

PHP 实现方案

PHP 可通过原生 socket 扩展实现持续监听的TCP服务,但需注意关闭脚本超时限制,且必须在命令行环境运行(不能依赖Apache/Nginx等Web服务器,否则会被执行时间限制中断)。以下是核心示例:

// 初始化TCP Socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($socket, '0.0.0.0', 9000);
socket_listen($socket);

// 关闭脚本超时限制
set_time_limit(0);

// 存储Android客户端连接,key为设备唯一ID
$androidClients = [];

while (true) {
    $readSockets = array_merge([$socket], $androidClients);
    $writeSockets = null;
    $exceptSockets = null;

    // 多路复用监听所有连接状态
    socket_select($readSockets, $writeSockets, $exceptSockets, null);

    foreach ($readSockets as $conn) {
        // 处理新的客户端连接
        if ($conn === $socket) {
            $newConn = socket_accept($socket);
            // 读取客户端发送的设备ID(需Android端先上报)
            $deviceId = trim(socket_read($newConn, 1024));
            $androidClients[$deviceId] = $newConn;
        } else {
            // 处理客户端发送的数据
            $data = trim(socket_read($conn, 1024));
            if (empty($data)) {
                // 连接断开,移除连接池
                $deviceId = array_search($conn, $androidClients);
                if ($deviceId !== false) {
                    unset($androidClients[$deviceId]);
                }
                socket_close($conn);
                continue;
            }

            // 解析Web终端发送的指令(格式示例:{"targetId":"device123","cmd":"openDoor"})
            $command = json_decode($data, true);
            $targetId = $command['targetId'] ?? '';
            $cmd = $command['cmd'] ?? '';

            // 转发指令到目标Android客户端
            if (isset($androidClients[$targetId])) {
                socket_write($androidClients[$targetId], $cmd, strlen($cmd));
            }
        }
    }
}

Node.js 实现方案

Node.js 天生支持异步非阻塞IO,非常适合长连接场景,且可同时启动HTTP服务处理Web页面需求,完美解决你提到的「结合HTML页面」问题。核心示例:

const net = require('net');
const http = require('http');
const fs = require('fs');

// TCP服务:管理Android客户端长连接
const tcpServer = net.createServer();
const androidClients = new Map(); // key:设备ID, value:Socket连接

tcpServer.on('connection', (socket) => {
    // 首次连接时读取Android端上报的设备ID
    socket.once('data', (idBuffer) => {
        const deviceId = idBuffer.toString().trim();
        androidClients.set(deviceId, socket);
        console.log(`设备 ${deviceId} 已连接`);

        // 处理连接断开
        socket.on('end', () => {
            androidClients.delete(deviceId);
            console.log(`设备 ${deviceId} 已断开`);
        });

        socket.on('error', (err) => {
            androidClients.delete(deviceId);
            console.error(`设备 ${deviceId} 连接异常:`, err);
        });
    });
});

tcpServer.listen(9000, '0.0.0.0', () => {
    console.log('TCP服务监听端口 9000');
});

// HTTP服务:提供Web页面+接收指令提交
const httpServer = http.createServer((req, res) => {
    // 响应Web页面请求
    if (req.url === '/' && req.method === 'GET') {
        fs.readFile('./web.html', (err, data) => {
            if (err) {
                res.writeHead(500, {'Content-Type': 'text/plain'});
                res.end('页面加载失败');
                return;
            }
            res.writeHead(200, {'Content-Type': 'text/html'});
            res.end(data);
        });
    }
    // 处理Web终端的指令提交
    else if (req.url === '/send-cmd' && req.method === 'POST') {
        let body = '';
        req.on('data', (chunk) => body += chunk.toString());
        req.on('end', () => {
            const { targetId, cmd } = JSON.parse(body);
            if (androidClients.has(targetId)) {
                androidClients.get(targetId).write(cmd);
                res.writeHead(200, {'Content-Type': 'application/json'});
                res.end(JSON.stringify({code: 0, msg: '指令已发送'}));
            } else {
                res.writeHead(404, {'Content-Type': 'application/json'});
                res.end(JSON.stringify({code: 1, msg: '目标设备未在线'}));
            }
        });
    }
});

httpServer.listen(8080, () => {
    console.log('HTTP服务监听端口 8080');
});

Web页面只需通过 fetch 或 AJAX 向 /send-cmd 接口POST指令即可,无需直接操作TCP连接。

二、多Web终端Socket连接是否会覆盖?

不会覆盖。每个客户端(Web/Android)与Server建立的TCP连接都是独立的,Server会为每个连接分配唯一的文件描述符(PHP)或Socket对象(Node.js),并通过连接池统一管理,不同连接之间互不干扰。

注意:Web端不建议直接建立原生TCP连接(浏览器无标准API支持),通过HTTP接口提交指令是更简单、兼容的方案。

三、整体架构方向建议

推荐采用**「HTTP服务 + TCP长连接服务」一体化架构**,流程如下:

  1. Android客户端:启动后主动发起TCP长连接,连接成功后上报设备唯一ID(如IMEI、自定义UUID),Server将连接与设备ID绑定存入连接池。
  2. Web终端:通过浏览器访问HTTP服务提供的页面,用户操作后,页面通过HTTP接口向Server提交指令(包含目标设备ID和具体命令)。
  3. Server端:
    • 解析Web指令,从连接池找到对应Android设备的长连接,转发命令;
    • 实现心跳机制:Server定期向Android端发送心跳包,长时间无响应则标记连接失效;Android端若未收到心跳,主动重连;
    • 加入连接异常处理:捕获Android端断开、网络波动等场景,及时清理无效连接。

若坚持使用PHP,推荐使用Swoole扩展,它支持异步非阻塞IO,可同时提供HTTP和TCP服务,性能和稳定性远优于原生socket扩展。

内容的提问来源于stack exchange,提问作者momone50

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 08:05:04