主要代码如下:
Level_Final_Serial.java
package com.serial; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import javax.comm.CommPortIdentifier; import javax.comm.PortInUseException; import javax.comm.SerialPort; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 作用: 串口读写数据(底层) * ①串口文件位置放置: rxtxSerial.dll 放在%java_home%\bin下<br> * ②类使用介绍: 初始化:getInstance->setPortName->initialize * 打开串口:openPort * 读写数据:writePort->readPackData * 关闭串口:ClosePort * 枚举全部串口名称:getAllComPorts * 注意事项: 无 */ public class Level_Final_Serial { /** * 数据包长度 */ public static final int PACKET_LENGTH = 500; private Logger log = LoggerFactory.getLogger(Level_Final_Serial.class); public static Level_Final_Serial final_Level; SerialPort serialPort; CommPortIdentifier identifier; String PortName; // 串口名.如:COM3 OutputStream out; // 串口输出流 InputStream in; // 串口输入流 String appname = "SerialBean"; // 程序名 int timeOut; // 延迟时间(毫秒数) int baudrate; // 波特率 int dataBits; // 数据位 int stopBits; // 停止位 int parity; // 奇偶检验 /** * @describe: 获取SerialBeanl类单例 * @date:2009-11-5 */ public static Level_Final_Serial getInstance() { if (final_Level == null) { final_Level = new Level_Final_Serial(); } return final_Level; } /** * 构造函数 */ public Level_Final_Serial() { } /** * @describe: 设置 串口程序名 * @date:2010-3-2 */ public void setAppname(String appname) { this.appname = appname; } /** * @describe: 初始化类 * @param timeOut 等待时间 * @param baudrate 波特率 * @param dataBits 数据位 * @param stopBits 停止位 * @param parity 奇偶检验 * @date:2009-11-5 */ public void initialize(int timeOut, int baudrate, int dataBits, int stopBits, int parity) { this.timeOut = timeOut; this.baudrate = baudrate; this.dataBits = dataBits; this.stopBits = stopBits; this.parity = parity; } /** * @describe: 初始化串口详细信息 * @return true : 初始化串口成功 false: 初始化串口失败 * @date:2009-11-5 */ public boolean openPort(String portName) { boolean rsBool = false; this.PortName = portName; try { //获取串口 identifier = getCommPort(); if (identifier == null) { // null } else { if (identifier.isCurrentlyOwned()) { log.info(PortName + ": 串口已经被" + identifier.getCurrentOwner() + "占用..."); } else { // open方法打开通讯端口 serialPort = (SerialPort) identifier.open(appname, timeOut); // 获取端口的输入,输出流对象 in = serialPort.getInputStream(); out = serialPort.getOutputStream(); // 设置串口初始化参数,依次是波特率,数据位,停止位和校验 serialPort.setSerialPortParams(baudrate, dataBits, stopBits, parity); serialPort.setDTR(true); serialPort.setRTS(true); rsBool = true; } } } catch (PortInUseException e) { log.info(PortName + ": 串口已经被" + identifier.getCurrentOwner() + "占用..."); } catch (Exception e) { log.info(PortName + "--初始化串口出错:" + e.toString()); } return rsBool; } /** * @describe: 列举并得到需要用串口 * @date:2009-11-5 */ public CommPortIdentifier getCommPort() throws Exception { CommPortIdentifier portIdRs = null; portIdRs = CommPortIdentifier.getPortIdentifier(PortName); return portIdRs; } /** * @describe: 读取串口数据 * @date:2009-11-5 */ public char[] readPackData() throws Exception { byte[] readBuffer = new byte[PACKET_LENGTH]; char[] msgPack = null; int numBytes = 0; while (in.available() > 0) { numBytes = in.read(readBuffer); msgPack = null; msgPack = new char[numBytes]; for (int i = 0; i < numBytes; i++) { msgPack[i] = (char) (readBuffer[i] & 0xFF); } } return msgPack; } /** * @describe: 向串口写数据 char[] bytes * @date:2009-11-5 */ public void writePort(char[] bytes) throws IOException { for (char b : bytes) { writePort(b); } } /** * @describe: 向串口写数据 char bytes * @date:2009-11-5 */ public void writePort(char b) throws IOException { out.write(b); out.flush(); } /** * @describe: 关闭串口,释放资源 * @date:2009-11-5 */ public void closePort() { if (out != null) { try { out.close(); in.close(); out = null; in = null; } catch (IOException e) { log.info("关闭串口时出错:" + e.toString()); } } if (serialPort != null) { serialPort.close(); serialPort = null; } } /** * @describe: 列举全部串口名称 * @date:2009-11-22 */ @SuppressWarnings("unchecked") public static List<String> getAllComPorts() { List<String> comList = new ArrayList<String>(); Enumeration en = CommPortIdentifier.getPortIdentifiers(); CommPortIdentifier portIdRs = null; while (en.hasMoreElements()) { portIdRs = (CommPortIdentifier) en.nextElement(); if (portIdRs.getPortType() == CommPortIdentifier.PORT_SERIAL) { comList.add(portIdRs.getName()); } } return comList; } }Serial_For_Smslib.java
package com.serial; import javax.comm.SerialPort; /** * 作用 : 串口操作:封装底层 * 类使用介绍: * 获取实例:getInstance * 打开串口:openPort * 读写数据:writeByte->readByte * 关闭串口:closePort * 注意事项 : 无 */ public class Serial_For_Smslib { public static Serial_For_Smslib sms_serial; public static Level_Final_Serial final_Level; //需要设置的参数 int portId; // 串口号:如:com1,则portId为1 int baudrate; // 波特率 // 不一定要设定的参数(有默认值) int timeOut; // 延迟时间(毫秒数) int dataBits; // 数据位 int stopBits; // 停止位 int parity; // 奇偶检验 int funCode; // 功能码 int dataLen; // 数据长度 int appendMillsec; // 计算发送间隔用---附加毫秒数 int bytes; // 计算发送间隔用---发送字节数 // 自动计算--发送间隔 int frameInterval; // 根据波特率,数据倍率和数据量,自动设置发送间隔 // 构造方法 public Serial_For_Smslib() { final_Level = new Level_Final_Serial(); timeOut = 60; // 延迟时间(毫秒数) dataBits = SerialPort.DATABITS_8; // 数据位 stopBits = SerialPort.STOPBITS_1; // 停止位 parity = SerialPort.PARITY_NONE; // 奇偶检验 funCode = 3; // 读取当前寄存器内一个或多个二进制值 dataLen = 4; // 假设 需要获取4个数据 appendMillsec = 38; // 附加毫秒数(需要自己测试调整) bytes = 20; // 发送是字节数 } /** * @describe: 获取程序单例 * @date:2009-11-5 */ public static Serial_For_Smslib getInstance() { if (sms_serial == null) { sms_serial = new Serial_For_Smslib(); } return sms_serial; } /** * @describe: 打开串口 * @param portStr 串口号. 如: COM3 * @param baudrate 波特率 * @param appName 串口占用程序的命名 * @return: true:打开串口正常 false:打开串口异常 */ public boolean openPort(String portStr, int baudrate, String appName) { boolean rsBool = false; // 初始化串口 final_Level.initialize(timeOut, baudrate, dataBits, stopBits, parity); final_Level.setAppname(appName.toUpperCase()); // 打开串口 if (final_Level.openPort(portStr)) { rsBool = true; // 设置帧之间的发送间隔 this.frameInterval = getFrameInterval (appendMillsec, bytes, baudrate); } return rsBool; } /** * @describe: 写串口命令 - 发送AT这个指令 * @param rs 发送的数据 */ public void writeByte(char[] rs) throws Exception { final_Level.writePort(rs); // 打印发送的串口数据-16进制显示 // System.out.println(bytesToHexString(rs)); //等待一段时间, 以保证数据,有足够的时间发送和接收 //Thread.sleep(frameInterval); Thread.sleep(frameInterval); } /** * @describe: .读串口命令 - 对发送AT这个指令,返回OK就是成功 * @return: true:成功 false:失败 */ public boolean readByte(String portStr) throws Exception { boolean rsbool = false; String rsStr = ""; // 读取数据 char[] rsByte = final_Level.readPackData(); if (rsByte != null) { // 打印收到的串口数据-16进制显示 for (char c : rsByte) { rsStr += c; } if (rsStr.indexOf("OK") > 0) { System.out.println("找到" + portStr + ":短信模块串口"); rsbool = true; } } else { System.out.println(portStr + ":不是短信模块串口"); } // 处理收到的数据 return rsbool; } /** * @describe: 关闭串口,释放资源 * @date:2009-11-5 */ public void closePort() { final_Level.closePort(); } //---------------工具方法---------------// /** * @describe: 获取需要帧之间需要间隔的时间(毫秒) 功能公式(1*12( 位)*数据长度*1000/波特率 + 附加毫秒数)--根据自己的程序动态调整 * @param appendMillsec 附加毫秒数 * @param dataLen 数据区数据长度 * @param baudrate 波特率 * @return 得到合适的帧发送,间隔毫秒数 * @date:2009-11-5 */ public static int getFrameInterval(int appendMillsec, int dataLen, int baudrate) { int rsInt = (int) Math.ceil(1 * 12 * (dataLen + 4) * 1000 / (float) baudrate) + appendMillsec; return rsInt; } /** * @describe: 把char类型转换成16进制字符串 * @param bArray char类型数组 * @date:2009-11-7 */ public static final String bytesToHexString(char[] bArray) { StringBuffer sb = new StringBuffer(bArray.length); String sTemp; for (int i = 0; i < bArray.length; i++) { sTemp = Integer.toHexString(0xFF & bArray[i]); if (sTemp.length() < 2) { sb.append(0); } sb.append(sTemp.toUpperCase() + " "); } return sb.toString(); } }SmsSendJob.java
package com.serial; import java.util.ArrayList; import java.util.List; /** * 作用 : smslib 发送短信,自动匹配串口 <br> * 类使用介绍: 获取实例:<code>getInstance</code> * 设置波特率: <code>setBaudrate</code><br> * 启动服务: <code>startService</code>已经 * * 启动短信接收事件<br> * 结束服务: <code>stopService</code><br> * 打印网络信息: <code>smsInfo</code><br> * * 发送短信: <code>sendMessage</code><br> * 注意事项 : 无 */ public class SmsSendJob { private static SmsSendJob smsSendJob = new SmsSendJob(); // 本类单例 /** * 用于动态测试短信串口号 */ private Serial_For_Smslib smslib_test = new Serial_For_Smslib (); /** * 发送短信的服务 */ private SmsService smsService = new SmsService(); /** * 短信发送模块专用的端口名 */ private static String SMSAPPNAME = "sms_port"; /** * 波特率 */ private int baudrate = 9600; /** * 串口字符(如:COM1) */ private String comStr = "com1"; /*---------------------------测试合适的串口号-------------------------------*/ /** * @describe: 获取SerialBeanl类单例 * @date:2009-11-5 */ public static SmsSendJob getInstance() { return smsSendJob; } /** * @describe: 设置波特率 和 串口字符 * @param baudrate: * 波特率 * @param comStr: * 串口字符 * @date:2010-3-2 */ public void initial(int baudrate, String comStr) { this.baudrate = baudrate; this.comStr = comStr; } /** * @describe: 动态检测适合短信模块的-串口字符(如:COM1) * @date:2009-11-22 */ public String getRightComStr() { String rsCom = null; // 获取final_Serial实例--扫描端口数量,并逐个测试 List<String> portList = Level_Final_Serial.getAllComPorts(); if (portList.size() <= 0) { // 没有发现任何串口 } else { // 逐个扫描测试连通性 for (String portStr : portList) { // 测试串口的是否适合短信模块 if (testSms(portStr)) { rsCom = portStr; break; } } } return rsCom; } /** * @describe: 测试串口的是否适合短信模块 * @param portStr: * 串口号. 如:COM3 * @return: null:失败 其他:成功 */ public boolean testSms(String portStr) { boolean rsBool = false; try { // ① 打开端口 rsBool = smslib_test.openPort(portStr, baudrate, SMSAPPNAME); // ② 串口写 String atCommand = "AT\r"; // 发送AT指令(加换行符号\r) char[] atOrder = atCommand.toCharArray(); if (rsBool) smslib_test.writeByte(atOrder); if (rsBool) { // ③ 串口读(根据得到的数据,判断返回数据的连通性{返回字符包含OK表示成功}) rsBool = smslib_test.readByte(portStr); // ④ 关闭串口 smslib_test.closePort(); } } catch (Exception e) { e.printStackTrace(); } return rsBool; } /*---------------------------发送短信-------------------------------*/ public char[] strToCharArrary(String str) { char[] rsChar = str.toCharArray(); return rsChar; } /** * @describe: 为发送和接收短信做好准备 * @return: true:成功 false:失败 * @date:2010-3-2 */ public boolean readyToSendMsg() { boolean rsbool = false; rsbool = smsService.initial(comStr, baudrate, "0000"); if (rsbool) rsbool = smsService.startService(); return rsbool; } /** * @describe: 给指定的一组手机号码,发送短信 * @param phoneList * 手机号码列表 * @param message * 信息内容 * @return: true:成功 false:失败 * @date:2010-3-2 */ public boolean sendMessage(List<String> phoneList, String message) { boolean rsbool = smsService.sendMessage(phoneList, message); return rsbool; } /** * @describe: 打印sms信息 * @date:2010-3-2 */ public void printSmsInof() throws Exception { smsService.smsInfo(); } /** * @describe: 停止服务 * @return: true:成功 false:失败 * @date:2010-3-1 */ public void stopService() { smsService.stopService(); } public static void main(String[] args) throws Exception { SmsSendJob smsSendJob = SmsSendJob.getInstance(); // 运行实例 String comName = smsSendJob.getRightComStr(); // 获取合适短信模块的 串口字符 if (comName != null) { smsSendJob.initial(9600, comName); // 设置波特率和串口字符 if (smsSendJob.readyToSendMsg()) { // 准备 - ok // smsSendJob.printSmsInof(); // 打印sms信息 List<String> phoneList = new ArrayList<String>(); phoneList.add("18631129982"); String message = "我是一条测试短信!"; // 给10086发一条查询余额的短信 smsSendJob.sendMessage(phoneList, message); Thread.sleep(60 * 1000); // 一分钟后,关闭短信服务 // 接收短信在SmsService中已经注册,InboundNotification中process会处理 // 收短信后,默认会删除收到的短信,也可以通过setRec_msg_remove(boolean)修改 } smsSendJob.stopService(); } else { System.out.println("没有找到合适的串口号"); } } }SmsService.java
package com.serial; import java.util.List; import org.smslib.AGateway; import org.smslib.GatewayException; import org.smslib.ICallNotification; import org.smslib.IGatewayStatusNotification; import org.smslib.IInboundMessageNotification; import org.smslib.IOrphanedMessageNotification; import org.smslib.IOutboundMessageNotification; import org.smslib.InboundMessage; import org.smslib.Library; import org.smslib.OutboundMessage; import org.smslib.Service; import org.smslib.AGateway.GatewayStatuses; import org.smslib.AGateway.Protocols; import org.smslib.Message.MessageEncodings; import org.smslib.Message.MessageTypes; import org.smslib.modem.SerialModemGateway; /** * @ SmsService.java 作用 : smslib 应用- 发送短信(可以群发) 服务<br> * 类使用介绍: 初始化: <code>initial</code><br> * 启动服务:<code>startService</code>已经 * * 启动短信接收事件<br> * 结束服务:<code>stopService</code><br> * 打印网络信息:<code>smsInfo</code><br> * * 发送短信: <code>sendMessage</code><br> * 注意事项 : 无 VERSION DATE BY CHANGE/COMMENT 1.0 2010-3-1 YANGZHONLI create */ public class SmsService { private static String SEND_SMS_GROUP = "smsgruop"; // 用于发送短信的组名 private Service srv; // 短信服务对象 private SerialModemGateway gateway; // 网关 private boolean rec_msg_remove = true; // 收到,处理短信之后,是否删除当前短信.默认删除 InboundNotification inbound = new InboundNotification(); // 接收短信的监听 OrphanedMessageNotification Orphaned = new OrphanedMessageNotification(); // 中断短信处理(短信字数较多时,会用2条以上是短信,这时候就用到这个了)-好像有点问题,还需要测试 /** * @describe: 设置:收到,处理短信之后,是否删除当前短信.默认删除 * @param rec_msg_flag: * true:删除 false:不删除 * @date:2010-3-2 */ public void setRec_msg_remove(boolean rec_msg_remove) { this.rec_msg_remove = rec_msg_remove; } /** * @describe: 初始化短信模块 * @param com * 串口号 * @param baudRate * 波特率 * @param pin * pin值 * @return: true:成功 false:失败 * @date:2010-3-1 */ public boolean initial(String com, int baudRate, String pin) { boolean rsbool = true; this.srv = Service.getInstance(); this.gateway = new SerialModemGateway("SMSLINK", com, baudRate, "", ""); this.gateway.setOutbound(true); this.gateway.setInbound(true); this.gateway.setProtocol(Protocols.PDU); this.gateway.setSimPin(pin); try { this.srv.addGateway(gateway); } catch (GatewayException e) { rsbool = false; e.printStackTrace(); } return rsbool; } /** * @describe: ①启动服务 ②创建一个用于发送短信的组(名为: smsgruop) ③创建事件监听(接收和中断短信处理) * @return: true:成功 false:失败 * @date:2010-3-1 */ public boolean startService() { boolean rsbool = true; try { this.srv.startService(); this.srv.createGroup(SEND_SMS_GROUP); // 注册启动短信接收事件 -- 小短信 this.srv.setInboundMessageNotification (inbound); // 注册启动短信接收事件 -- 大短信 this.srv.setOrphanedMessageNotification (Orphaned); // ... 还可以注册其他事件 } catch (Exception e) { rsbool = false; e.printStackTrace(); } return rsbool; } /** * @describe: 停止服务 * @return: true:成功 false:失败 * @date:2010-3-1 */ public boolean stopService() { boolean rsbool = true; try { this.srv.stopService(); } catch (Exception e) { rsbool = false; e.printStackTrace(); } return rsbool; } /** * @describe: 打印sms信息 * @date:2010-3-1 */ public void smsInfo() throws Exception { System.out.println(); System.out.println("smslib Version: " + Library.getLibraryVersion()); System.out.println("Modem Information:"); System.out.println(" Manufacturer: " + gateway.getManufacturer()); System.out.println(" Model: " + gateway.getModel()); System.out.println(" Serial No: " + gateway.getSerialNo()); System.out.println(" SIM IMSI: " + gateway.getImsi()); System.out.println(" Signal Level: " + gateway.getSignalLevel() + "%"); System.out.println(" Battery Level: " + gateway.getBatteryLevel() + "%"); System.out.println(" SmscNumber: " + gateway.getSmscNumber()); System.out.println(); } /** * @describe: 给指定的一组手机号码,发送短信 * @param phoneList * 手机号码列表 * @param message * 信息内容 * @return: true:成功 false:失败 * @date:2010-3-1 */ public boolean sendMessage(List<String> phoneList, String message) { boolean rsbool = true; // 把手机号码逐个加入到短信发送组中 for (String phone : phoneList) { this.srv.addToGroup(SEND_SMS_GROUP, phone); } OutboundMessage msg = new OutboundMessage (SEND_SMS_GROUP, message); msg.setEncoding(MessageEncodings.ENCUCS2); try { this.srv.sendMessage(msg); // 发送完短信,把手机号码逐个从短信发送组中移除 for (String phone : phoneList) { this.srv.removeFromGroup (SEND_SMS_GROUP, phone); } } catch (Exception e) { rsbool = false; e.printStackTrace(); } return rsbool; } /** * 作用 : 收短信的监听,并删除收到的短信 */ public class InboundNotification implements IInboundMessageNotification { public void process(String gatewayId, MessageTypes msgType, InboundMessage msg) { if (msgType == MessageTypes.INBOUND) System.out .println(">>>收到短信① New Inbound message detected from Gateway: " + gatewayId); else if (msgType == MessageTypes.STATUSREPORT) System.out .println(">>>收到短信② New Inbound Status Report message detected from Gateway: " + gatewayId); System.out.println(msg); // System.out.println("时间:" + msg.getDate().toLocaleString()); // System.out.println("短信内容:" + msg.getText()); // System.out.println("发件号码:" + msg.getOriginator()); try { if (rec_msg_remove) { // 删除收到的短信 srv.deleteMessage(msg); } } catch (Exception e) { e.printStackTrace(); } } @Override public void process(AGateway arg0, MessageTypes arg1, InboundMessage arg2) { // TODO Auto-generated method stub } } /** * 作用 : 当有人向您发送一个大消息,这个消息来的部分。有时,网 * * 络的原因不明,<br> * 一个大消息的某些部分没有到达你的,所以这个问题的消息是 * * 完全没有收到。<br> * 这些孤儿的消息部分留在您的手机,消耗内存。 <br> * 如果您收到太多的“不完整”的消息部分,这些都可能需要您的调 * * 制解调器的内存 <br> * 有效地禁用接收它的任何其他消息。<br> * * 具体通知方法需要返回<code>true或false</code> :如果您返回 * * true ,该邮件的部分将被删除 ,以夺回失去的调制解调器内存<br> */ public class OrphanedMessageNotification implements IOrphanedMessageNotification { public boolean process(String gatewayId, InboundMessage msg) { System.out.println(">>> 大消息接收 Orphaned message part detected from " + gatewayId); System.out.println(msg); // Since we are just testing, return FALSE and keep the orphaned // message part. return false; } @Override public boolean process(AGateway arg0, InboundMessage arg1) { // TODO Auto-generated method stub return false; } } /** * 作用 : 发短信的监测 */ public class OutboundNotification implements IOutboundMessageNotification { public void process(String gatewayId, OutboundMessage msg) { System.out.println("Outbound handler called from Gateway: " + gatewayId); System.out.println(msg); } @Override public void process(AGateway arg0, OutboundMessage arg1) { // TODO Auto-generated method stub } } /** * 作用 : 接到电话的监听 */ public class CallNotification implements ICallNotification { public void process(String gatewayId, String callerId) { System.out.println(">>> New call detected from Gateway: " + gatewayId + " : " + callerId); } @Override public void process(AGateway arg0, String arg1) { // TODO Auto-generated method stub } } /** * 作用 : 网关变动的监听 */ public class GatewayStatusNotification implements IGatewayStatusNotification { public void process(String gatewayId, GatewayStatuses oldStatus, GatewayStatuses newStatus) { System.out.println(">>> Gateway Status change for " + gatewayId + ", OLD: " + oldStatus + " -> NEW: " + newStatus); } @Override public void process(AGateway arg0, GatewayStatuses arg1, GatewayStatuses arg2) { // TODO Auto-generated method stub } } }