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串列埠工具使用簡訊貓