短信猫开发记录
准备
GSM MODEM
天线(没有天线,可能没有信号)
java相关开发环境(jar包,驱动,配置文件)
超级终端(用于检测设备是否正常)
SIM卡
整
-
配置环境:
把RXTXcomm.jar文件放在\jdk\jre\lib\ext目录下
把rxtxSerial.dll和rxtxParallel.dll文件放在\jdk\jre\bin目录下 -
搭建项目
maven
依赖
<dependency>
<groupId>org.smslib</groupId>
<artifactId>smslib</artifactId>
<version>3.5.3</version>
</dependency>
-
开发需求:监听数据库某表的变化,有数据插入,发送短信到某表保存的联系人信息。
思路:项目初始化时保存总量和最大时间到redis中,定时任务每分钟去查询一次和redis中数据比较,总量不同则对联系人发送短信。 -
短信客户端配置
@Configuration
@Slf4j
public class SmsServerConfig {
@Value("${sms.portName}")
private String portName;
@Value("${sms.baud}")
private int baudIndex;
@Value("${sms.pin:0000}")
private String pin;
@Value("${sms.puer.ip:}")
private String prIp;
@Value("${sms.puer.username:}")
private String prUserName;
@Value("${sms.puer.password:}")
private String prPassword;
@Value("${sms.type:}")
private String type;
/**
* 签名编码
*/
@Value("${sms.mas.sign:}")
private String sign;
/**
* url
*/
@Value("${sms.mas.url:}")
private String url;
/**
* 账号
*/
@Value("${sms.mas.userAccount:}")
private String userAccount;
/**
* 密码
*/
@Value("${sms.mas.password:}")
private String password;
/**
* 企业名称
*/
@Value("${sms.mas.ecname:}")
private String ecname;
@Bean
public SmsExecuter smsServer(){
return new SmsServer(portName,baudIndex,pin);
}
}
短信服务的方法,包括配置,初始化,启动服务,发送短信等方法。
/**
* 短信服务
*/
@Slf4j
public class SmsServer implements SmsExecuter {
private static Service service;
static int bauds[] = {9600,14400,19200,28800,33600,38400,56000,57600,115200};
private boolean init=false;
private boolean start=false;
public SmsServer(String portName,int baudIndex,String pin){
//波特率
int baud=bauds[baudIndex];
SerialModemGateway gateway = new SerialModemGateway("modem."+portName,portName,baud,"SIEMENS","null");
//设置收件箱绑定
gateway.setInbound(true);
//设置发件箱绑定
gateway.setOutbound(true);
//获取实例
service = Service.getInstance();
//加入轮询 支持linux
service.S.SERIAL_POLLING=true;
try {
service.addGateway(gateway);
init=true;
log.info("初始化成功");
} catch (GatewayException e) {
log.error("初始化失败",e);
e.printStackTrace();
}
}
/**
* 消息发送
* @param msg 消息对象
* @return 是否发送成功
*/
private boolean sendSMS(OutboundMessage msg){
msg.setEncoding(Message.MessageEncodings.ENCUCS2);
try {
return service.sendMessage(msg);
} catch (Exception e) {
log.error("消息发送失败",e);
}
return false;
}
/**
* 开启服务
*/
@Override
public void start(){
if(!init){
throw new RuntimeException("初始化出错,无法启动服务");
}
try {
service.startService();
start=true;
} catch (Exception e) {
log.error("启动短信服务失败",e);
}
}
/**
* 停止短信服务
*/
public void stop(){
if(start){
try {
service.stopService();
start=false;
} catch (Exception e) {
log.error("停止短信服务失败",e);
}
}
}
@Override
public boolean send(String phone, String content) {
OutboundMessage outboundMessage=new OutboundMessage(phone,content);
return this.sendSMS(outboundMessage);
}
@Override
public boolean send(List<String> phones, String content) throws SendErrorException {
//TODO:本来通过OutboundMessage[]群发短信,发现中文没有展示,时间有限,测试短信花的自己的钱,就省事用这种方式发送群发短信
int res = 0;
for (String phone : phones) {
boolean send = send(phone, content);
if (send){
res++;
}
}
return res==phones.size();
}
}
短信方法的执行者
/**
* 短信执行者
*/
public interface SmsExecuter {
/**
* 短信发送
* @param phone
* @param content
* @return
*/
boolean send(String phone, String content) throws SendErrorException;
/**
* 短信群发
* @param phones
* @param content
* @return
* @throws SendErrorException
*/
boolean send(List<String> phones, String content) throws SendErrorException;
void start();
}
具体过程:
/**
* @author Mingwentao
* @date 2022年03月02日 14:39
* <p>
* 启动短信服务初始化
* <p>
* 初始化向redis中缓存数据
*/
@Component
@Slf4j
@Order(0)
public class InitRedisDataTask implements CommandLineRunner {
@Autowired
RedisUtil redisUtil;
@Autowired
SmsExecuter smsExecuter;
@Resource
MethodMapper methodMapper;
@Override
public void run(String... strings) {
//启动短信服务
smsExecuter.start();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//1. 获取当前告警信息表的总量
int tCount = methodMapper.selectCount(null);
//2. 获取当前告警信息表的最大时间
LambdaQueryWrapper<AlertInfo> wapper = Wrappers.lambdaQuery();
wapper.orderByDesc(AlertInfo::getFCjsj);
List<AlertInfo> alertInfos = methodMapper.selectList(wapper);
//3. 存入redis
if (alertInfos != null && alertInfos.size() > 0 && tCount != 0) {
redisUtil.set(MAX_COUNT_KEY, String.valueOf(tCount));
redisUtil.set(MAX_GJ_TIME_KEY, sdf.format(alertInfos.get(0).getFCjsj()));
}
}
/**
* redisUtil.set("t_gjxx_max_time","2022-03-11 02:18:21");
* redisUtil.set("t_gjxx_count","8");
*/
}
/**
* @author Mingwentao
* @date 2022年03月02日 15:29
*/
@Component
@EnableScheduling
@Slf4j
public class ScheduleTask {
@Autowired
RedisUtil redisUtil;
@Resource
MethodMapper methodMapper;
@Autowired
SmsExecuter smsExecuter;
@Resource
PhoneMapper phoneMapper;
@Scheduled(cron = "${taskTime}")
public void sendSmsInfo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//1. 获取redis中的count量
int rCount = Integer.parseInt(redisUtil.get(MAX_COUNT_KEY));
//2. 获取表中的count量
int tCount = methodMapper.selectCount(null);
//3. 有数据变动才发送短信
if (rCount != tCount) {
String time = redisUtil.get(MAX_GJ_TIME_KEY);
LambdaQueryWrapper<AlertInfo> wapper = Wrappers.lambdaQuery();
wapper.gt(AlertInfo::getFCjsj, time);
wapper.orderByDesc(AlertInfo::getFCjsj);
//告警信息
List<AlertInfo> alertInfos = methodMapper.selectList(wapper);
if (alertInfos != null && alertInfos.size() > 0) {
QueryWrapper<TelePhoneInfo> query = new QueryWrapper<>();
query.select("phone");
List<Object> phoneLists = phoneMapper.selectObjs(query);
if (phoneLists != null && phoneLists.size() > 0) {
//TODO:群发短信
sendSms(phoneLists, alertInfos);
}
//更新redis中数据
redisUtil.set(MAX_COUNT_KEY, String.valueOf(tCount));
redisUtil.set(MAX_GJ_TIME_KEY, sdf.format(alertInfos.get(0).getFCjsj()));
}
}
}
/**
* 调用短信服务发送短信
*
* @param phone
* @param alertInfos
*/
private void sendSms(List<Object> phone, List<AlertInfo> alertInfos) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
List<String> strs = new ArrayList<>();
for (Object o : phone) {
strs.add(String.valueOf(o));
}
StringBuilder sb = new StringBuilder();
for (AlertInfo alertInfo : alertInfos) {
String content = sb.append(CONTENT_PREFIX).append(alertInfo.getFXw()).append(" ").append(sdf.format(alertInfo.getFCjsj())).toString();
try {
smsExecuter.send(strs, content);
} catch (SendErrorException e) {
log.error("发送短信失败");
}
}
}
}
踩坑
- 短信猫驱动建议找厂商直接要,网上下的驱动很多不适用
- 一定要先用终端软件测试设备,用at指令测下设备功能,大部分原因可能是设备问题,比如我中间一直无法发送短信,就是因为设备是2g信号的,已经过时了,更换4g信号的设备后,就正常使用了。