首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP Socket 通訊實戰:TCP/UDP 連接設備、發送命令、RS232 串口控制全解析

     編輯:PHP 相關知識

PHP 與硬體設備通訊完全指南:TCP 長連接、UDP 無連接、RS232 串口三大模式代碼複用

在工業物聯網、智能硬體、POS 機對接等場景中,PHP 作為後端服務常需與設備進行 Socket 級通訊。本文系統整理三種主流通訊模式的完整封裝代碼:TCP 阻塞/非阻塞連接、UDP 無連接收發、RS232 串口讀寫,並附帶 16 進制指令轉換工具函數,可直接用於生產環境。

一、TCP 通訊:可靠有序的字節流

1. 僅發送命令(無需回覆)

適用於下發控制指令、心跳包等場景。採用非阻塞連接 + 超時輪詢,避免設備離線導致 PHP 進程卡死。

/**
 * TCP 發送命令(無返回值)
 * @param int    $port     目標端口
 * @param string $address  目標 IP
 * @param string $cmd      發送內容(原始字節/字符串)
 */
function tcp_send_only($port, $address, $cmd) {
    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    if (!$socket) { die("Create socket failed: " . socket_strerror(socket_last_error())); }

    $timeout = 2;  // 秒
    $start = time();
    socket_set_nonblock($socket);

    // 非阻塞連接輪詢
    while (!@socket_connect($socket, $address, $port)) {
        $err = socket_last_error($socket);
        if ($err === 10056 || $err === 115) { // 已連接 / EINPROGRESS
            break;
        }
        if (time() - $start >= $timeout) {
            socket_close($socket);
            throw new Exception("TCP 連接超時: $address:$port");
        }
        usleep(250000); // 250ms
    }

    socket_set_block($socket);
    socket_write($socket, $cmd, strlen($cmd));
    socket_close($socket);
}

2. 發送命令並接收回覆(請求-響應模式)

適用於查詢設備狀態、讀取傳感器數據等雙向通訊場景。

/**
 * TCP 發送命令並讀取響應
 * @return string|false 設備返回數據,失敗拋異常
 */
function tcp_send_receive($port, $address, $cmd) {
    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    if (!$socket) { throw new Exception("Socket create failed"); }

    $timeout = 2;
    $start = time();
    socket_set_nonblock($socket);

    while (!@socket_connect($socket, $address, $port)) {
        $err = socket_last_error($socket);
        if ($err === 10056 || $err === 115) { break; }
        if (time() - $start >= $timeout) {
            socket_close($socket);
            throw new Exception("TCP 連接超時");
        }
        usleep(250000);
    }

    socket_set_block($socket);
    socket_write($socket, $cmd, strlen($cmd));
    
    // 讀取響應(根據協議調整長度/模式)
    $buffer = socket_read($socket, 1024, PHP_NORMAL_READ);
    socket_close($socket);
    
    if ($buffer === false || $buffer === '') {
        throw new Exception("設備無響應或讀取失敗");
    }
    return $buffer;
}

二、UDP 通訊:輕量無連接,適合廣播/組播

UDP 無需建立連接,延遲極低,但不保證送達與順序。適用於局域網設備發現、實時遙測等場景。

/**
 * UDP 發送命令並可選接收響應
 * @param bool $need_response 是否等待響應
 */
function udp_communicate($port, $address, $cmd, $need_response = true) {
    $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
    if (!$socket) { throw new Exception("UDP socket create failed"); }

    // 設置接收超時 2 秒
    socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 2, 'usec' => 0]);

    // 發送
    $sent = @socket_sendto($socket, $cmd, strlen($cmd), 0, $address, $port);
    if ($sent === false) {
        socket_close($socket);
        throw new Exception("UDP 發送失敗");
    }

    if (!$need_response) {
        socket_close($socket);
        return true;
    }

    // 接收
    $buffer = '';
    $from = '';
    $port_recv = 0;
    $result = @socket_recvfrom($socket, $buffer, 1024, MSG_WAITALL, $from, $port_recv);
    socket_close($socket);

    if ($result === false || $buffer === '') {
        throw new Exception("UDP 無響應或接收失敗");
    }
    return $buffer;
}

三、RS232 串口通訊:Windows 下直接操作 COM 口

PHP 在 Windows 下可通過 fopen('COM1:', 'w+') 直接讀寫串口,配合 mode 命令設置波特率、校驗位等參數。

/**
 * RS232 串口發送命令
 * @param string $com      串口號,如 'COM1'、'COM3'
 * @param int    $baudrate 波特率,如 9600、115200
 * @param string $cmd      發送的原始字節字符串
 */
function rs232_send($com, $baudrate, $cmd) {
    @set_time_limit(10);
    // 設置串口參數:波特率、無校驗、8數據位、1停止位、關閉硬件流控
    $mode_cmd = "mode {$com} BAUD={$baudrate} PARITY=n DATA=8 STOP=1 odsr=off";
    exec($mode_cmd, $output, $return_var);
    if ($return_var !== 0) {
        throw new Exception("串口參數設置失敗: $mode_cmd");
    }

    // 打開串口(需管理員權限)
    $fp = @fopen($com, 'w+');
    if (!$fp) {
        throw new Exception("無法打開串口 $com,請檢查權限或是否被佔用");
    }

    // 發送數據
    fwrite($fp, $cmd);
    fclose($fp);
}

四、輔助工具:16 進制字符串轉二進制字節

設備協議常用 16 進制指令(如 "AA 55 01 02 FF"),需轉為二進制字符串發送。

/**
 * 16 進制字符串轉二進制字節字符串
 * @param string $hex_str 格式:"AA 55 01 02 FF" 或 "AA550102FF"
 * @return string 二進制字符串
 */
function hex_to_binary($hex_str) {
    // 移除空格
    $hex_str = str_replace(' ', '', $hex_str);
    // 校驗長度為偶數
    if (strlen($hex_str) % 2 !== 0) {
        throw new Exception("16進制字符串長度必須為偶數");
    }
    $binary = '';
    for ($i = 0; $i < strlen($hex_str); $i += 2) {
        $binary .= chr(hexdec(substr($hex_str, $i, 2)));
    }
    return $binary;
}

// 使用示例
$cmd_hex = "AA 55 01 02 FF";
$cmd_bin = hex_to_binary($cmd_hex); // 返回 5 字節二進制字符串
tcp_send_receive(502, '192.168.1.100', $cmd_bin);

五、生產環境部署關鍵點

  1. 權限問題:Linux 下訪問串口需將 PHP-FPM 運行用戶加入 dialout 組;Windows 下 Apache/PHP 需以管理員身份運行。
  2. 超時與重試:網絡/串口通訊必須設置連接超時、讀寫超時,並實現指數退避重試機制。
  3. 協議解析:TCP 為流式協議,需處理粘包/拆包(定長、分隔符、長度前綴);UDP 單包處理較簡單。
  4. 異步非阻塞:高併發場景建議使用 Swoole / Workerman / ReactPHP 等異步框架,避免阻塞 PHP-FPM 進程。
  5. 安全加固:設備指令嚴格白名單校驗,防止命令注入;通訊鏈路建議加密(TLS/DTLS)或 VPN 隔離。
  6. 日誌審計:記錄每條指令的發送時間、內容、響應、耗時,便於故障排查與合規審計。

將上述函數封裝為 DeviceCommunicator 類,配合依賴注入容器,即可在 Laravel/ThinkPHP/Symfony 等框架中優雅地調用:$device->tcpSendReceive($cmd)。掌握這三種通訊模式,讓 PHP 輕松駕馭硬體世界。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved