由于前段时间做的系统需要使用短信猫收发短信,所以研究了一下在Java下使用短信猫,网上很多资料都是使用的smslib的jar包来发送短信,但是这种方式只支持32的jdk,而我的系统使用的是linux的64位环境,所以最后采用了用RXTX串口通讯工具直接向短信猫发送AT指令的方式实现。
java调用短信猫发送短信。 这里的短信猫主要使用RS232串口与服务器通信。smslib.jar 需要用到java串口通信需要用到的comm.jar,win32com.dll和javax.comm.properties。
下载地址:短信猫java二次开发包smslib及使用示例
具体的使用可以参考短信猫java二次开发包源代码smslib-3.5.4.jar
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运行环境目录为准
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();
}
}
/**
* 将其短信内容转换成十六进制
* @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();
}
/**
* 解析MODEM返回来的字符串
* 根据MODEM返回的字符串,解析成一个CommonSms的集合对象
* @param str MODEM返回的字符串
* @return
*/
public static List analyseArraySMS(String str) {
List mesList = new ArrayList();
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();
}
这里发送短信的设置是,发送英文短信时使用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 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 RecvSmsList(Port myport) {
if(!myport.isIsused())
{
System.out.println("System Message: COM通讯端口未正常打开!");
return null;
}
List listMes = new ArrayList();
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;
}
在使用短信猫时要填写短信猫的串口号,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串口工具使用短信猫