当前位置: 首页> 最新文章列表> 使用 pack() 结合 stream_socket_client() 发送原始数据

使用 pack() 结合 stream_socket_client() 发送原始数据

M66 2025-06-04

在网络编程中,有时候我们需要发送原始的二进制数据到远程服务器,特别是在实现某些协议时。PHP 提供了非常强大的函数 pack(),可以将数据按照指定格式打包成二进制字符串。结合 stream_socket_client(),我们可以方便地通过 TCP 或 UDP 连接发送这些原始数据。

本文将详细介绍如何利用 PHP 的 pack() 函数来打包数据,并通过 stream_socket_client() 发送给指定服务器。


1. pack() 函数简介

pack() 函数用于把数据转换成二进制字符串。它的第一个参数是格式字符串,后续参数是要打包的数据。格式字符串中包含各种格式代码,比如:

  • C — 无符号字符(1 字节)

  • n — 无符号短整型(2 字节,大端序)

  • N — 无符号长整型(4 字节,大端序)

  • a — NUL 字符填充的字符串

  • A — 空格填充的字符串

例如:

$data = pack('Cnn', 0x01, 300, 400);

这里的 0x01 是一个字节,300400 都是 2 字节无符号整型。


2. stream_socket_client() 简介

stream_socket_client() 用于创建客户端的网络连接,可以支持 TCP、UDP 等协议。常用语法如下:

$socket = stream_socket_client("tcp://m66.net:12345", $errno, $errstr, 30);
  • 第一个参数是地址,格式如 tcp://域名:端口

  • 连接成功返回资源,失败返回 false

  • $errno$errstr 会返回错误码和错误信息

  • 最后一个参数是超时时间(秒)

通过此连接,我们可以使用 fwrite() 发送数据,fread() 接收数据。


3. 示例:用 pack() 打包数据并发送

假设我们需要向服务器发送一个协议数据包,格式如下:

  • 1 字节命令码,固定为 0x10

  • 2 字节的用户 ID,大端序

  • 4 字节的时间戳,大端序

  • 8 字节的字符串(如果不足 8 字节,空格填充)

PHP 代码示例:

<?php

// 目标服务器信息
$host = "m66.net";
$port = 12345;

// 创建 TCP 连接
$socket = stream_socket_client("tcp://$host:$port", $errno, $errstr, 10);
if (!$socket) {
    die("连接失败: $errstr ($errno)\n");
}

// 准备数据
$command = 0x10;
$userId = 1025;
$timestamp = time();
$username = "user123";

// 打包数据
// C - 1字节无符号整数
// n - 2字节无符号短整数(大端)
// N - 4字节无符号长整数(大端)
// A8 - 长度为8的字符串,空格填充
$packedData = pack('CnNA8', $command, $userId, $timestamp, $username);

// 发送数据
fwrite($socket, $packedData);

// 读取服务器响应(假设服务器会返回 4 字节响应码)
$response = fread($socket, 4);
if ($response !== false) {
    $responseCode = unpack('N', $response)[1];
    echo "服务器响应码: $responseCode\n";
} else {
    echo "未接收到服务器响应\n";
}

fclose($socket);

4. 关键点总结

  • 使用 pack() 可以方便地将各种数据类型转换成二进制数据,适合网络传输。

  • stream_socket_client() 用于创建网络连接,支持多种协议。

  • 发送二进制数据时,确保双方对协议格式理解一致,特别是数据长度和字节序。

  • 读取数据时也要用 unpack() 来解析原始二进制数据。