
本文详解如何基于 alexandr-mironov/php-smpp 库实现多条短信的批量发送,通过复用连接、优化会话生命周期和正确管理地址对象,避免重复建连开销,提升发送效率与稳定性。
本文详解如何基于 alexandr-mironov/php-smpp 库实现多条短信的批量发送,通过复用连接、优化会话生命周期和正确管理地址对象,避免重复建连开销,提升发送效率与稳定性。
在使用 alexandr-mironov/php-smpp 发送短信时,原始示例中每次调用 sendMessage() 都会执行一次完整的 SMPP 会话流程:open() → bindTransceiver() → sendSMS() → close()。这种“一消息一连接”模式不仅性能低下(TCP 握手、认证、断连开销大),还容易触发 SMSC 的频控限制,甚至导致连接被拒绝。
要真正实现高效批量发送,关键在于 复用 SMPP 会话连接,即:
✅ 建立一次连接(bindTransceiver)
✅ 循环调用 sendSMS() 发送多条消息
✅ 所有发送完成后统一 close()
以下是优化后的 SmsBuilder 改写方案,支持单次连接下发多条短信:
use smpp\{Address, SMPP, Client as SmppClient, transport\Socket};
class SmsBuilder
{
const DEFAULT_SENDER = 'github_example';
protected $transport;
protected $smppClient;
protected $debug = false;
protected $login;
protected $password;
protected $isBound = false; // 标记是否已绑定
public function __construct(
string $address,
int $port,
string $login,
string $password,
int $timeout = 10000,
bool $debug = false
) {
$this->transport = new Socket([$address], $port);
$this->transport->setRecvTimeout($timeout);
$this->smppClient = new SmppClient($this->transport);
$this->smppClient->debug = $debug;
$this->transport->debug = $debug;
$this->login = $login;
$this->password = $password;
$this->debug = $debug;
}
/**
* 初始化并绑定到 SMSC(仅首次调用生效)
*/
public function connect(): void
{
if ($this->isBound) {
return;
}
$this->transport->open();
$this->smppClient->bindTransceiver($this->login, $this->password);
$this->isBound = true;
}
/**
* 安全关闭连接(仅当已绑定时执行)
*/
public function disconnect(): void
{
if ($this->isBound) {
$this->smppClient->close();
$this->isBound = false;
}
}
/**
* 发送单条短信(不打开/关闭连接)
* @param Address $from 发送方地址
* @param Address $to 接收方地址
* @param string $message 短信内容
* @return bool 是否成功
*/
public function sendSingle(Address $from, Address $to, string $message): bool
{
try {
$this->smppClient->sendSMS($from, $to, $message, null, SMPP::DATA_CODING_UCS2);
return true;
} catch (\Exception $e) {
if ($this->debug) {
echo "发送失败 [{$to->address}]: {$e->getMessage()}\n";
}
return false;
}
}
/**
* 批量发送短信(推荐方式)
* @param array $messages [['from' => Address, 'to' => Address, 'message' => '...'], ...]
* @return array 每条发送结果的布尔值数组
*/
public function sendBatch(array $messages): array
{
$results = [];
$this->connect(); // 复用连接
foreach ($messages as $item) {
$from = $item['from'] ?? new Address(self::DEFAULT_SENDER, SMPP::TON_ALPHANUMERIC);
$to = $item['to'] ?? throw new \InvalidArgumentException('Missing "to" address');
$msg = $item['message'] ?? '';
$results[] = $this->sendSingle($from, $to, $msg);
}
return $results;
}
}
✅ 使用示例(批量发送两条短信)
// 构建 SMS 实例
$sms = new SmsBuilder('192.168.1.1', 2776, 'your_login', 'your_password', 10000, true);
// 准备批量数据(支持不同发件人/收件人/内容)
$batch = [
[
'from' => new Address('Nendersno', SMPP::TON_ALPHANUMERIC),
'to' => new Address('+8613800138000', SMPP::TON_INTERNATIONAL, SMPP::NPI_E164),
'message' => 'Hello, this is message #1'
],
[
'from' => new Address('AlertBot', SMPP::TON_ALPHANUMERIC),
'to' => new Address('+8613900139000', SMPP::TON_INTERNATIONAL, SMPP::NPI_E164),
'message' => 'Hello, this is message #2'
]
];
// 一次性发送全部
$results = $sms->sendBatch($batch);
print_r($results); // [true, true]
// 显式断开(可选,析构函数中也可自动处理)
$sms->disconnect();
⚠️ 注意事项与最佳实践
- 不要在循环内反复 open() / bindTransceiver():SMPP 协议设计为长连接,频繁重绑将显著降低吞吐量,并可能被 SMSC 拒绝。
- 务必处理异常:网络波动、SMSC 拒绝、PDU 错误等均可能导致 sendSMS() 抛出异常,建议包裹 try-catch 并记录失败详情。
- 编码统一推荐 SMPP::DATA_CODING_UCS2:确保中文、emoji 等 Unicode 字符正确传输;若只发纯英文数字,可改用 SMPP::DATA_CODING_DEFAULT 提升长度上限(最多 160 字)。
- 国际号码格式需规范:使用 TON_INTERNATIONAL + NPI_E164,号码须含国家码(如 +86138...),避免前导 0 或空格。
- 生产环境建议增加重试机制与队列:对失败消息加入指数退避重试,或接入 Redis/RabbitMQ 异步队列,保障最终送达。
? 补充说明:虽然 Plivo、Twilio 等云通信平台提供更易用的 REST 批量接口(如 Plivo Bulk Messaging),但若项目已深度集成 SMPP 协议(如对接自建 SMSC、运营商网关),则优化原生库使用方式仍是首选——它更可控、延迟更低、成本更优。
通过以上改造,你可轻松将 QPS(每秒短信数)从个位数提升至数十条,同时大幅增强系统健壮性与可维护性。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











