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

Java 整合 SMSLib 與短信貓實現短信發送完整代碼範例

     編輯:Java 相關知識

前言

在企業級應用中,短信通知功能常透過短信貓(GSM Modem)搭配 SMSLib 函式庫實現。本文提供一個完整、可直接參考的 Java 類別範例,涵蓋閘道初始化、單例模式管理、發送中文簡訊、送達狀態回呼、接收簡訊處理與來電通知等核心功能。

核心架構說明

1. 閘道初始化與配置讀取

建構子中從 sms.properties 讀取 COM 埠名稱與鮑率,預設為 COM10、9600。若配置檔存在則覆蓋預設值,隨後建立 SerialModemGateway 實例,指定廠牌與型號(範例為 Wavecom 17254)。

2. 單例模式確保唯一服務

getInstant() 方法採用雙重檢查鎖定(此處簡化版),確保整個應用僅有一個 SmsModem 實例,避免重複開啟序列埠造成資源衝突。

3. 發送簡訊流程

  • 首次呼叫時啟動 Service,啟用序列埠輪詢、設定 SIM PIN、註冊三種回呼通知(發送、接收、來電)。
  • 發送前輸出 Modem 資訊(廠牌、型號、序號、IMSI、訊號強度、電量),利於除錯。
  • 關鍵設定:msg.setEncoding(MessageEncodings.ENCUCS2),確保中文內容正確編碼為 UCS-2,避免亂碼。

4. 回呼通知機制

  • OutboundNotification:發送完成後記錄閘道 ID 與訊息詳情。
  • InboundNotification:區分一般簡訊與狀態報告,印出完整訊息物件;可選擇自動刪除已讀訊息。
  • CallNotification:記錄來電閘道 ID 與主叫號碼。

5. 讀取收件箱範例

readMessage() 示範如何一次性讀取所有類別訊息(MessageClasses.ALL),並格式化輸出「序號、發件人、內容、時間」,同時記錄 Modem 狀態資訊。

完整代碼

import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;

import org.apache.log4j.Logger;
import org.smslib.ICallNotification;
import org.smslib.IInboundMessageNotification;
import org.smslib.IOutboundMessageNotification;
import org.smslib.InboundMessage;
import org.smslib.InboundMessage.MessageClasses;
import org.smslib.Library;
import org.smslib.Message.MessageEncodings;
import org.smslib.Message.MessageTypes;
import org.smslib.OutboundMessage;
import org.smslib.Service;
import org.smslib.modem.SerialModemGateway;

public class SmsModem {
    private SerialModemGateway gateway = null;
    private ResourceBundle rb = null;
    private static SmsModem smsModem = null;
    private final OutboundNotification outboundNotification = new OutboundNotification();
    private static final Logger LOG = Logger.getLogger(SmsModem.class);
    private Service srv;
    private final InboundNotification inboundNotification = new InboundNotification();
    private final CallNotification callNotification = new CallNotification();

    public SmsModem() {
        try {
            rb = ResourceBundle.getBundle("sms");
            String portName = "COM10";
            int baudRate = 9600;
            LOG.info("default portName:" + portName);
            LOG.info("default baudRate:" + baudRate);
            if (rb != null) {
                LOG.info("RB is not null");
                if (rb.getString("smsport") != null && !"".equals(rb.getString("smsport"))) {
                    portName = rb.getString("smsport");
                    LOG.info("portName:" + portName);
                }
                if (rb.getString("smsbaud") != null && !"".equals(rb.getString("smsbaud"))) {
                    baudRate = Integer.valueOf(rb.getString("smsbaud"));
                    LOG.info("baudRate:" + baudRate);
                }
            }
            gateway = new SerialModemGateway("modem." + portName, portName, baudRate, "wavecom", "17254");
        } catch (Exception e) {
            LOG.error("閘道初始化失敗:" + e.getMessage());
            e.printStackTrace();
        }
    }

    public static synchronized SmsModem getInstant() {
        if (smsModem == null) {
            smsModem = new SmsModem();
        }
        return smsModem;
    }

    public SerialModemGateway getGateway() {
        return gateway;
    }

    public void sendMessage(String phone, String content) throws Exception {
        doIt(phone, content);
    }

    public void doIt(String phone, String content) throws Exception {
        OutboundMessage msg;
        LOG.info("發送範例:從序列 GSM Modem 發送簡訊。");
        LOG.info(Library.getLibraryDescription());
        LOG.info("版本: " + Library.getLibraryVersion());
        if (srv == null) {
            srv = new Service();
            srv.S.SERIAL_POLLING = true;
            gateway.setInbound(true);
            gateway.setOutbound(true);
            gateway.setSimPin("0000");
            gateway.setOutboundNotification(outboundNotification);
            gateway.setInboundNotification(inboundNotification);
            gateway.setCallNotification(callNotification);
            srv.addGateway(gateway);
            srv.startService();
        }
        if (gateway != null) {
            LOG.info("Modem 資訊:");
            LOG.info(" 製造商: " + gateway.getManufacturer());
            LOG.info(" 型號: " + gateway.getModel());
            LOG.info(" 序號: " + gateway.getSerialNo());
            LOG.info(" SIM IMSI: " + gateway.getImsi());
            LOG.info(" 訊號強度: " + gateway.getSignalLevel() + "%");
            LOG.info(" 電池電量: " + gateway.getBatteryLevel() + "%");
        }
        msg = new OutboundMessage(phone, content);
        msg.setEncoding(MessageEncodings.ENCUCS2); // 中文必須設定 UCS-2 編碼
        srv.sendMessage(msg);
    }

    public class OutboundNotification implements IOutboundMessageNotification {
        public void process(String gatewayId, OutboundMessage msg) {
            LOG.info("發送回呼來自閘道: " + gatewayId);
            LOG.info(msg);
        }
    }

    public String readMessage() {
        StringBuffer sb = new StringBuffer("");
        List msgList;
        try {
            System.out.println("讀取範例:從序列 GSM Modem 讀取簡訊。");
            System.out.println(Library.getLibraryDescription());
            System.out.println("版本: " + Library.getLibraryVersion());
            if (srv == null) {
                srv = new Service();
                srv.S.SERIAL_POLLING = true;
                gateway.setInbound(true);
                gateway.setOutbound(true);
                gateway.setSimPin("0000");
                gateway.setOutboundNotification(outboundNotification);
                gateway.setInboundNotification(inboundNotification);
                gateway.setCallNotification(callNotification);
                srv.addGateway(gateway);
                srv.startService();
            }
            LOG.info("讀取 Modem 資訊:");
            LOG.info(" 製造商: " + gateway.getManufacturer());
            LOG.info(" 型號: " + gateway.getModel());
            LOG.info(" 序號: " + gateway.getSerialNo());
            LOG.info(" SIM IMSI: " + gateway.getImsi());
            LOG.info(" 訊號強度: " + gateway.getSignalLevel() + "%");
            LOG.info(" 電池電量: " + gateway.getBatteryLevel() + "%");
            msgList = new ArrayList();
            this.srv.readMessages(msgList, MessageClasses.ALL);
            int num = 1;
            for (InboundMessage msg : msgList) {
                sb.append("第" + num + "條;發件人:" + msg.getOriginator() + "; 內容:" + msg.getText() + "\n");
                LOG.info("第" + num + "條;發件人:" + msg.getOriginator() + "; 內容:" + msg.getText() + "\n");
                num++;
                LOG.info(msg);
            }
        } catch (Exception e) {
            sb.append(e.getMessage());
            e.printStackTrace();
        } finally {
            // this.srv.stopService(); // 若需持續監聽請勿關閉
        }
        return sb.toString();
    }

    public class InboundNotification implements IInboundMessageNotification {
        public void process(String gatewayId, MessageTypes msgType, InboundMessage msg) {
            if (msgType == MessageTypes.INBOUND)
                System.out.println(">> 新收到簡訊來自閘道: " + gatewayId);
            else if (msgType == MessageTypes.STATUSREPORT)
                System.out.println(">> 新收到狀態報告來自閘道: " + gatewayId);
            System.out.println(msg);
            try {
                // srv.deleteMessage(msg); // 如需自動刪除請取消註解
            } catch (Exception e) {
                System.out.println("刪除訊息失敗...");
                e.printStackTrace();
            }
        }
    }

    public class CallNotification implements ICallNotification {
        public void process(String gatewayId, String callerId) {
            System.out.println(">> 新來電偵測來自閘道: " + gatewayId + " : " + callerId);
        }
    }
}

部署注意事項

  1. 需在 classpath 下放置 sms.properties,內容範例:
    smsport=COM3
    smsbaud=115200
  2. 確認 Modem 驅動程式已正確安裝,裝置管理員中可見對應 COM 埠。
  3. 若使用 Maven,請加入 org.smslib:smslib:3.5.4 相依套件。
  4. 生產環境建議將 Service 啟動與關閉納入 Spring 生命週期管理(@PostConstruct / @PreDestroy)。

常見問題排查

  • 訊號強度 0%:檢查天線連接、SIM 卡是否欠費或被鎖定。
  • CommPortIdentifier 找不到埠:確認 javax.comm 或 rxtx 原生庫與 JVM 位元數一致(32/64-bit)。
  • 中文亂碼:務必在 OutboundMessage 上呼叫 setEncoding(ENCUCS2)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved