在物聯網、工業監控、金融通知等場景,通過短信貓(GSM/GPRS Modem)直發短信具有「不依賴互聯網、到達率高、成本可控」的優勢。SmsLib v3.x 是 Java 生態中最成熟的短信貓開發庫。本文詳述 Maven 依賴管理、原生庫加載、核心 API 調用、編碼處理及高頻故障排查。
SmsLib 依賴 JavaComm (javax.comm) 訪問串口,涉及 JNI 原生庫,部署極易出錯。
<!-- 建議使用維護分支 fork 版本 -->
<dependency>
<groupId>org.smslib</groupId>
<artifactId>smslib</artifactId>
<version>3.5.4</version> <!-- 或 3.3.0 歷史版本 -->
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- JavaComm 官方無 Maven 坐標,需 system scope 引用本地 jar -->
<dependency>
<groupId>javax.comm</groupId>
<artifactId>comm</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/comm.jar</systemPath>
</dependency>
| 文件 | 目標目錄 | 說明 |
|---|---|---|
javax.comm.properties | %JAVA_HOME%/jre/lib/ | 指定驅動類 com.sun.comm.Win32Driver |
win32com.dll | %JAVA_HOME%/jre/bin/ | Windows 串口 JNI 庫(32/64 位必須與 JDK 一致) |
comm.jar | %JAVA_HOME%/jre/lib/ext/ | 擴展類加載器加載,確保 javax.comm 可見 |
⚠️ 關鍵驗證:啟動 JDK 自帶 java -jar comm.jar 或編寫測試代碼 CommPortIdentifier.getPortIdentifiers() 能枚舉到 COM 口,才說明環境就緒。
jre/lib/ext 機制,需用 -Djava.ext.dirs 或模塊化方案(極其麻煩)。package com.alonely.notecat;
import org.smslib.*;
import org.smslib.modem.SerialModemGateway;
public class SendMessage {
// 異步發送結果回調
private static class OutboundNotification implements IOutboundMessageNotification {
@Override
public void process(String gatewayId, OutboundMessage msg) {
System.out.println("【發送回調】Gateway: " + gatewayId + " | Msg: " + msg);
}
}
@SuppressWarnings("deprecation")
public void sendSMS(String mobilePhones, String content) {
Service srv = null;
try {
srv = Service.getInstance();
// 配置串口網關
SerialModemGateway gateway = new SerialModemGateway(
"modem.com3", // 網關 ID
"COM3", // 串口號 (Windows: COM1~COM9, Linux: /dev/ttyUSB0)
9600, // 波特率 (短信貓默認 9600/115200)
"wavecom", // 製造商 (可為空)
"" // 型號 (可為空)
);
gateway.setInbound(true); // 接收短信
gateway.setOutbound(true); // 發送短信
gateway.setSimPin("0000"); // SIM 卡 PIN 碼 (無則空字符串)
gateway.setOutboundNotification(new OutboundNotification());
srv.addGateway(gateway);
System.out.println("初始化成功,啟動服務...");
srv.startService();
System.out.println("服務啟動成功");
// 批量發送
String[] phones = mobilePhones.split(",");
for (String phone : phones) {
OutboundMessage msg = new OutboundMessage(phone.trim(), content);
msg.setEncoding(Message.MessageEncodings.ENCUCS2); // 中文必須 UCS2 (Unicode)
srv.sendMessage(msg);
System.out.println("已發送至: " + phone);
Thread.sleep(500); // 避免發送過快導致模組緩衝區溢出
}
} catch (Exception e) {
System.err.println("發送異常: " + e.getMessage());
e.printStackTrace();
} finally {
if (srv != null) {
try { srv.stopService(); } catch (Exception ignored) {}
}
}
}
public static void main(String[] args) {
new SendMessage().sendSMS("13800138000,13900139000", "【測試】Java SmsLib 短信發送成功!");
}
}
| 編碼模式 | SmsLib 常量 | 適用場景 | 單條字符數限制 |
|---|---|---|---|
| GSM 7-bit 默認字母表 | ENC7BIT | 純英文/數字/少量符號 | 160 字符 |
| 8-bit 數據 | ENC8BIT | 二進制數據、WAP Push | 140 字節 |
| UCS-2 (UTF-16BE) | ENCUCS2 | 中文/多語言/Emoji | 70 字符 (超過自動拆分長短信) |
長短信拆分:SmsLib 內部自動處理 Concatenated SMS (UDH 頭部),發送端無感知,接收端手機自動重組。
| 錯誤現象 | 根因分析 | 解決方案 |
|---|---|---|
NoClassDefFoundError: org/apache/log4j/Logger | 缺少 log4j JAR | 加入 log4j-1.2.17.jar 到 classpath |
NoSuchPortException / PortInUseException | 1. 串口號錯誤 2. 被其他程序佔用 3. JDK 位數不匹配 (64位 JDK 載入 32位 dll) | 1. 設備管理器確認 COM 口號 2. 關閉串口調試助手/其他占用程序 3. 強制使用 32位 JDK |
GatewayException: Comm library exception | javax.comm 原生庫加載失敗 | 檢查 win32com.dll 是否在 jre/bin,comm.jar 是否在 jre/lib/ext,javax.comm.properties 配置正確 |
| 發送成功但手機收不到 | 1. SIM 卡欠費/無信號 2. 短信中心號碼 (SMSC) 未設置 3. 運行營商攔截 | 1. 手機測試 SIM 卡 2. AT 指令設置 SMSC: AT+CSCA="+8613800755500" 3. 換卡/換號段測試 |
| 中文顯示亂碼/問號 | 未設置 UCS2 編碼 | msg.setEncoding(MessageEncodings.ENCUCS2) |
| Eclipse 運行正常,打包 JAR 失效 | 原生庫未隨 JAR 發布,系統路徑依賴 | 打包時排除 comm.jar/win32com.dll,部署腳本手動佈署到目標機器 JRE 目錄 |
SmsLib + JavaComm 技術棧已停維護超 10 年,jSerialComm 是純 Java 實現、無原生庫依賴、支持 JDK 8~21、跨平台(Win/Linux/macOS/ARM)的現代串口庫。
<dependency>
<groupId>com.fazecast</groupId>
<artifactId>jSerialComm</artifactId>
<version>2.10.4</version>
</dependency>
// 簡單發送 AT 指令示例
SerialPort port = SerialPort.getCommPort("COM3");
port.setBaudRate(9600);
port.openPort();
try (OutputStream out = port.getOutputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(port.getInputStream()))) {
// 設置 PDU 模式
out.write("AT+CMGF=0\r\n".getBytes()); out.flush(); Thread.sleep(500);
// 發送 PDU 封裝後的短信 (需自行實現 PDU 編碼或引入 smslib 核心編碼邏輯)
// ...
} finally { port.closePort(); }
短信貓直發雖然技術棧老舊,但在特定場景下不可替代。掌握 JavaComm 環境配置核心邏輯、PDU 編碼原理、以及向 jSerialComm 遷移的路徑,能讓你在維護舊系統與構建新系統時游刃有餘。