1. 程式人生 > >Java 利用RXTX串列埠工具使用簡訊貓

Java 利用RXTX串列埠工具使用簡訊貓

由於前段時間做的系統需要使用簡訊貓收發簡訊,所以研究了一下在Java下使用簡訊貓,網上很多資料都是使用的smslib的jar包來發送簡訊,但是這種方式只支援32的jdk,而我的系統使用的是linux的64位環境,所以最後採用了用RXTX串列埠通訊工具直接向簡訊貓傳送AT指令的方式實現。

1. smslib.jar收發簡訊

java呼叫簡訊貓傳送簡訊。 這裡的簡訊貓主要使用RS232串列埠與伺服器通訊。smslib.jar 需要用到java串列埠通訊需要用到的comm.jar,win32com.dll和javax.comm.properties。
下載地址:簡訊貓java二次開發包smslib及使用示例


具體的使用可以參考簡訊貓java二次開發包原始碼smslib-3.5.4.jar

2. RXTX串列埠通訊工具和簡訊貓收發簡訊關鍵程式碼

2.1. 簡訊貓串列埠通訊工具 rxtx-2.2pre2-bins

Win64位系統將RXTXcomm.jar拷貝到\jre\lib\ext目錄下,win64資料夾中的rxtxSerial.dll 拷貝到\jre\bin 目錄下。
Linux系統將RXTXcomm.jar拷貝/jre/lib/ext 目錄下,librxtxSerial.so檔案拷貝到 /jre/lib/[machine type] (i386 for instance) 中
若執行時找不到rxtxSerial 類,則拷貝到/usr/lib/jvm/java-6-sun-1.6.0.26(根據情況而定)/jre/lib/[machine type] (i386/amd64)
以上jre目錄都已當前jre執行環境目錄為準

2.2 將傳送的簡訊進行PDU格式編碼

package com.rxtx.util;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.sms.util.Convert;

/***
 * 傳送pdu格式簡訊工具
 */
public class SendUtil {
    static String regEx = "[\u4e00-\u9fa5]";//中文
    static Pattern pat = Pattern.compile(regEx);
    public static
void main(String[] args) throws Exception { String text =encodeHex("test"); System.out.print(text); } /** * 使用PDU方式傳送訊息 * @param phone * @param msg * @return AT指令 */ public static final String PDUEncoder(String phone,String msg){ String sendcontent =""; String len=""; if(isContainsChinese(msg)){ //簡訊包含中文 sendcontent= "0011000D91" + reverserNumber(phone) + "000801" + PDUUSC2ContentEncoder(msg) ; }else{ sendcontent= "0011000D91" + reverserNumber(phone) + "000001" + PDU7BitContentEncoder(msg) ; } len = (sendcontent.length() - Integer.parseInt(sendcontent.substring(0, 2), 16) * 2 - 2) / 2 +""; //計算長度 if(len.length()<2) len="0"+len; // return "AT+CMGS=" + len + "\r" + sendcontent + String.valueOf(26); //26 Ctrl+Z ascii碼 return len+","+sendcontent; } /** * 獲取簡訊內容的位元組數 * @param txt * @return */ private static String getLength(String txt) { int i = 0; String s = ""; i = txt.length() * 2; i += 15; s = i+""; return s; } /** * 將手機號碼轉換為記憶體編碼 * @param phone * @return */ private static String reverserNumber(String phone) { String str = ""; //檢查手機號碼是否按照標準格式寫,如果不是則補上 if (phone.substring(0, 2) != "86") { phone = "86"+phone; } char[] c = getChar(phone); for (int i = 0; i <= c.length - 2; i += 2) { str += c[i + 1]+"" + c[i]; } return str; } private static char[] getChar(String phone) { if (phone.length() % 2 == 0) { return phone.toCharArray(); } else { return (phone + "F").toCharArray(); } } private static boolean isContainsChinese(String str) { Matcher matcher = pat.matcher(str); boolean flg = false; if (matcher.find()) { flg = true; } return flg; } /** * 使用Sms 的 SendSms()方法傳送簡訊時,呼叫此方法將其簡訊內容轉換成十六進位制 * @param msg 簡訊內容 * @return 轉換後的十六進位制簡訊 */ public static final String encodeHex(String msg) { byte[] bytes = null; try { bytes = msg.getBytes("GBK"); } catch (java.io.UnsupportedEncodingException e) { e.printStackTrace(); } StringBuffer buff = new StringBuffer(bytes.length * 4); String b = ""; char a; int n = 0; int m = 0; for (int i = 0; i < bytes.length; i++) { try{b = Integer.toHexString(bytes[i]);}catch (Exception e) {} if (bytes[i] > 0) { buff.append("00"); buff.append(b); n = n + 1; } else { a = msg.charAt((i - n) / 2 + n); m = a; try{b = Integer.toHexString(m);}catch (Exception e) {} buff.append(b.substring(0, 4)); i = i + 1; } } return buff.toString(); } /** * 中文簡訊內容USC2編碼 * @param userData * @return */ private static String PDUUSC2ContentEncoder(String userData){ String contentEncoding = encodeHex(userData); String length = Integer.toHexString(contentEncoding.length() / 2);//把value的值除以2並轉化為十六進位制字串 while(length.length()<2) length="0"+length; return length+contentEncoding.toUpperCase(); } /** * 英文簡訊內容7Bit編碼 * @param userData */ public static String PDU7BitContentEncoder(String userData) { String result = ""; String length = Integer.toHexString(userData.length()); while(length.length()<2) length="0"+length;//7bit編碼 使用者資料長度:源字串長度 byte[] Bytes = userData.getBytes(); String temp = ""; //儲存中間字串 二進位制串 String tmp; for (int i = userData.length(); i > 0; i--) { tmp = Convert.conver2HexStr(Bytes[i-1]); while (tmp.length() < 7) { tmp = "0" + tmp; } temp += tmp; } for (int i = temp.length() ; i > 0; i -= 8) { //每8位取位一個字元 即完成編碼 同時高位取為低位 if (i > 8) { String aa = Integer.toHexString(Integer.parseInt(temp.substring(i-8, i), 2)); while(aa.length()<2) aa="0"+aa; result += aa; } else { String aa = Integer.toHexString(Integer.parseInt(temp.substring(0, i), 2)); while(aa.length()<2) aa="0"+aa; result += aa; } } return length+result.toUpperCase(); } }

2.3 以text形式傳送簡訊時簡訊編碼成16進位制

/**
     * 將其簡訊內容轉換成十六進位制
     * @param msg 簡訊內容
     * @return 轉換後的十六進位制簡訊
     */
    public static final String encodeHex(String msg) {
        byte[] bytes = null;
        try {
            bytes = msg.getBytes("GBK");
        } catch (java.io.UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        StringBuffer buff = new StringBuffer(bytes.length * 4);
        String b = "";
        char a;
        int n = 0;
        int m = 0;
        for (int i = 0; i < bytes.length; i++) {
            try{b = Integer.toHexString(bytes[i]);}catch (Exception e) {}
            if (bytes[i] > 0) {
                buff.append("00");
                buff.append(b);
                n = n + 1;
            } else {
                a = msg.charAt((i - n) / 2 + n);
                m = a;
                try{b = Integer.toHexString(m);}catch (Exception e) {}
                buff.append(b.substring(0, 4));

                i = i + 1;
            }
        }
        return buff.toString();
    }

2.4 接收簡訊時解析接收的字串

/**
     * 解析MODEM返回來的字串
     * 根據MODEM返回的字串,解析成一個CommonSms的集合物件
     * @param str MODEM返回的字串
     * @return
     */
    public static List<CommonSms> analyseArraySMS(String str) {
        List<CommonSms> mesList = new ArrayList<CommonSms>();
        CommonSms cs;
        String[] messages;
        String temp;
        String[] t;
        if (str.indexOf("CMGL: ") == -1)
            return null;
        str = str.substring(0, str.indexOf("OK")).trim();
        messages = str.split("\n");
        if (messages.length < 2)
            return null;
        for (int i = 0; i < messages.length; i++) {
            cs = new CommonSms();
            if(messages[i].length()>=messages[i].indexOf("CMGL: ")+6){
                messages[i] = messages[i]
                                    .substring(messages[i].indexOf("CMGL: ") + 6);
            }           
            t = messages[i].split(",");
            //CT5150
            if (t.length > 5) {
                t[0] = t[0].replaceAll(":", "");
                cs.setId(Integer.parseInt(t[0].trim()));
                temp = t[1].substring(t[1].indexOf('"') + 1,
                        t[1].lastIndexOf('"')).trim();
                if (temp.equals("REC READ")) {
                    cs.setState("已讀");
                } else {
                    cs.setState("未讀");
                }
                cs.setSender((t[2].substring(t[2].indexOf('"') + 1, t[2]
                        .lastIndexOf('"')).trim()));
                //CT5150
                SimpleDateFormat df = new SimpleDateFormat("yy/MM/dd hh:mm:ss");
                String datestring = t[4].substring(t[4].indexOf('"') + 1) + " "
                        + t[5].substring(0, t[5].indexOf('"'));// 簡訊貓時間格式09/09/09
                                                                // 20:18:01+32
                Date date = null;
                try {
                    date = df.parse(datestring);
                } catch (Exception ex) {
                    System.out.println(ex.getMessage());
                }
                cs.setDate(date);
                i++;
                cs.setSmstext(analyseStr(messages[i].trim()));
                mesList.add(cs);
            }
        }
        return mesList;
    }

/**
     * 將編碼的十六進位制字串 如“4F60597DFF01” 轉換成unicode "\u4F60\u597D\uFF01"
     * @param str 要轉化的字串
     * @return 轉換後的十六進位制字串
     */
    public static String analyseStr(String str) {
        StringBuffer sb = new StringBuffer();
        if (!(str.length() % 4 == 0))
            return str;
        for (int i = 0; i < str.length(); i++) {
            if (i == 0 || i % 4 == 0) {
                sb.append("\\u");
            }
            sb.append(str.charAt(i));
        }
        return Unicode2GBK(sb.toString());
    }

/**
     * 將unicode編碼 "\u4F60\u597D\uFF01" 轉換成中文 "你好!"
     * @param dataStr 要轉化的字串
     * @return 轉換後的中文字串
     */
    public static String Unicode2GBK(String dataStr) {
        int index = 0;
        StringBuffer buffer = new StringBuffer();
        while (index < dataStr.length()) {
            if (!"\\u".equals(dataStr.substring(index, index + 2))) {
                buffer.append(dataStr.charAt(index));
                index++;
                continue;
            }
            String charStr = "";
            charStr = dataStr.substring(index + 2, index + 6);
            char letter = 0;
            try{letter = (char) Integer.parseInt(charStr, 16);}catch (Exception e) {}
            buffer.append(letter);
            index += 6;
        }
        return buffer.toString();
    }

2.5 傳送和接收簡訊

這裡傳送簡訊的設定是,傳送英文簡訊時使用PDU編碼,傳送中文時使用Text模式傳送,這是因為英文簡訊只有使用PDU編碼才會在接收時被識別為英文(這是開發時實踐得出的並不知道是為什麼)。

private CommonSms commonsms;
    private static char symbol1 = 13;
    private static String strReturn = "", atCommand = "";

/**
     * 號碼,內容,傳送簡訊息
     * @param phone
     * @param countstring
     * @throws Exception
     */
    public static void sendmsn(String phone,String countstring){
         Sms s = new Sms();
          // 傳送測試       
          CommonSms cs=new CommonSms();
          cs.setRecver(phone);
          cs.setSmstext(countstring);
          s.setCommonsms(cs);
          Port myort=new Port("COM4");
          System.out.println(myort.isIsused()+"     "+myort.getCOMname());
         s.SendSms(myort,0);    
         s.setMessageMode(myort,1);
          List<CommonSms> recvlist = s.RecvSmsList(myort);
          if(recvlist!=null){
              for(CommonSms sms:recvlist){
                  System.out.println("傳送人:"+sms.getSender()+"  時間:"+sms.getDate()+"  狀態:"+sms.getState());
                  System.out.println("內容:"+sms.getSmstext());
              }
          }
          myort.close();
    }

    public boolean SendSms(Port myport,int op) {
        if(!myport.isIsused())
        {       
        System.out.println("COM通訊埠未正常開啟!");        
        return false;
        }
        setMessageMode(myport,op);
        // 空格
        char symbol2 = 34;
        // ctrl~z 傳送指令
        char symbol3 = 26;
        try {
            if(op==1){
                atCommand = "AT+CSMP=17,169,0,08" + String.valueOf(symbol1);
                strReturn = myport.sendAT(atCommand);
                System.out.println(strReturn);
                if (strReturn.indexOf("OK", 0) != -1) {
                    atCommand = "AT+CMGS=" + commonsms.getRecver()
                            + String.valueOf(symbol1);
                    strReturn = myport.sendAT(atCommand);
                    atCommand = StringUtil.encodeHex(commonsms.getSmstext().trim())
                            + String.valueOf(symbol3) + String.valueOf(symbol1);
                    strReturn = myport.sendAT(atCommand);
                    if (strReturn.indexOf("OK") != -1
                            && strReturn.indexOf("+CMGS") != -1) {
                        System.out.println("簡訊傳送成功...");
                        return true;
                    }
                }   
            }else if(op==0){
                String[] txt = SendUtil.PDUEncoder(commonsms.getRecver(), commonsms.getSmstext().trim()).split(",");
                atCommand = "AT+CMGS=" + txt[0] + String.valueOf(symbol1);
                strReturn = myport.sendAT(atCommand);
                strReturn = myport.sendAT(txt[1]+ String.valueOf(symbol3) + String.valueOf(symbol1));
                if (strReturn.indexOf("OK") != -1
                        && strReturn.indexOf("+CMGS") != -1) {
                    System.out.println("簡訊傳送成功...");
                    return true;
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();           
            System.out.println("簡訊傳送失敗...");        
            return false;
        }   
        System.out.println("簡訊傳送失敗...");    
        return false;
    }
    /**
     * 設定訊息模式 
     * @param op
     * 0-pdu 1-text(預設1 文字方式 )
     * @return
     */
    public boolean setMessageMode(Port myport,int op) {
        try {
            String atCommand = "AT+CMGF=" + String.valueOf(op)
                    + String.valueOf(symbol1);
            String  strReturn = myport.sendAT(atCommand);
            if (strReturn.indexOf("OK", 0) != -1) {
                System.out.println("*************訊息模式 設定成功************");
                return true;
            }
            return false;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }   

    /**
    * 讀取所有簡訊 
    * @return CommonSms集合
    */
    public List<CommonSms> RecvSmsList(Port myport) {
        if(!myport.isIsused())
        {
            System.out.println("System Message:  COM通訊埠未正常開啟!");       
            return null;
        }
        List<CommonSms> listMes = new ArrayList<CommonSms>();
        try {
            atCommand = "AT+CMGL=\"REC UNREAD\"";
//          atCommand = "AT+CMGL=\"ALL\"";
//          AT+CMGL="REC UNREAD"代表顯示未讀簡訊清單
//                     AT+CMGL= "REC READ"代表顯示已讀簡訊清單
//                     AT+CMGL= "STO SENT"代表顯示已傳送的儲存簡訊清單
//                     AT+CMGL= "STO UNSENT"代表顯示未傳送的儲存簡訊清單
//                     AT+CMGL= "ALL"代表顯示所有簡訊清單
            strReturn = myport.sendAT(atCommand);
            if(strReturn.contains("ERROR"))
            System.out.println("System Message:  讀取簡訊出錯!");
            listMes = StringUtil.analyseArraySMS(strReturn);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return listMes;
    }

3. 簡訊貓串列埠相關問題

在使用簡訊貓時要填寫簡訊貓的串列埠號,windows下很方便檢視,直接填寫COM4或者其他的就行,在linux下就有一點麻煩,下面介紹一下我在部署程式時檢視串列埠號的方法,給大家一個參考。

(1)簡訊貓使用串列埠轉usb連線伺服器,那麼埠可能是ttyUSB0
直接 ls /dev 檢視裝置資訊,找到ttyUSB*,進而判斷簡訊貓的串列埠
(2)tail -10 /var/log/messages 可以檢視硬體裝置插拔變化,檢視簡訊貓是哪個裝置
(3)簡訊貓直接通過串列埠連線伺服器的話
# dmesg | grep tty 或者 setserial -g /dev/ttyS* 顯示檢測到的系統串列埠支援

一個簡單示例:簡訊貓用rxtx收發簡訊Java示例
本文示例:Java 利用RXTX串列埠工具使用簡訊貓