JAVA通信编程(一)——串口通讯
歡迎跳轉(zhuǎn)到本文的原文鏈接:https://honeypps.com/network/java-serial-comm/
? 博主結(jié)合實際經(jīng)驗,決定總結(jié)下JAVA通訊編程的一些小知識,希望能給給位讀者有些幫助。這里的JAVA通訊編程主要是指如何應(yīng)用JAVA編寫串口、TCP以及UDP的通訊程序。本片主要講述的是串口通訊。
? ? 本人所知的java串口通訊可以采用兩種方式實現(xiàn):
? ? 1. 采用comm.jar這個包,這個是sun提供的串口包javacomm20-win32.zip,可以搜索去下載。
? ? 2. 采用RXTXComm.jar包。
? ? 這兩個的串口編程方式類似,有許多類名也相似,這里采用的是RXTXComm.jar,具體原因可以去問度娘,我是先用了comm.jar后來換成了rxtxcomm.jar。
? ? 我采用的版本是:rxtx-2.1-7-bins-r2.zip.
通過目錄可以看到RXTXcomm可以幾種各種主流的操作系統(tǒng),(不支持64位的windows系統(tǒng))
不同的操作系統(tǒng)需要導(dǎo)入不同的庫,可以參考里面的INSTALL文件,雖然是英文的,但是不難看懂,比如Windows操作系統(tǒng)下,將RXTXcomm.jar放入jre/lib/ext/下,將window的dll文件rxtxSerial.dll放入jre/bin下(linux的會有差異,可以參考INSTALL文件)。
雖然本文講的是串口通訊,但是為了能夠使我們的程序屏蔽底層差異,即上層應(yīng)用只負責(zé)業(yè)務(wù)處理,底層的通訊不必管,通過一定的配置可以切換串口、tcp、udp進行通訊,這里我們的串口程序?qū)崿F(xiàn)以公用接口:
?
package com.zzh.comm;public interface CommBuff {public byte[] readBuff();public void writeBuff(byte[] message);public void open();public void close();public Object getInfo(); }
readBuff負責(zé)讀數(shù)據(jù),writeBuff負責(zé)寫數(shù)據(jù),open()做一些打開串口之類的事,close()做些資源關(guān)閉的工作,Object getInfo()預(yù)留接口,可以將一些狀態(tài)通過這個接口傳輸出來,這個可以在TCP那篇博文中可以用到,具體是將TCP的Socket對象返送給上層應(yīng)用。上層應(yīng)用讀取這個對象以便做相應(yīng)的處理。
?
下面就是我們的串口通訊程序了:
?
package com.zzh.comm;import gnu.io.CommPortIdentifier; import gnu.io.NoSuchPortException; import gnu.io.PortInUseException; import gnu.io.SerialPort; import gnu.io.SerialPortEvent; import gnu.io.SerialPortEventListener; import gnu.io.UnsupportedCommOperationException;import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Enumeration; import java.util.Map; import java.util.TooManyListenersException;import org.apache.log4j.Logger;public class SerialImpl implements CommBuff, SerialPortEventListener {private CommPortIdentifier portId; private SerialPort serialPort; private InputStream inputStream; private OutputStream outputStream; private boolean isOpen = false;private static byte[] recvBuff = new byte[4096];private static int recvLen = 0;private String appName;private String portName;private int rate;private int dataBit;private int stopBit;private int parityInt;private int timeout;private int delay;private Logger logger = Logger.getLogger(Object.class.getName());private String fileName = "/serial.properties";public SerialImpl(){Map<String,String> map = new ReadProperties().getPropertiesMap(fileName);try{this.appName = map.get("appName");this.portName = map.get("portName");String rates = map.get("rate");this.rate = Integer.parseInt(rates);String dataBits = map.get("dataBit");this.dataBit = Integer.parseInt(dataBits);String stopBits = map.get("stopBit");this.stopBit = Integer.parseInt(stopBits);String parityInts = map.get("parityInt");this.parityInt = Integer.parseInt(parityInts);String timeouts = map.get("timeout");this.timeout = Integer.parseInt(timeouts);String delays = map.get("delay");this.delay = Integer.parseInt(delays);}catch (Exception e){logger.error(e.getMessage());}}public void listPort(){CommPortIdentifier cpid;Enumeration<?> en = CommPortIdentifier.getPortIdentifiers();while(en.hasMoreElements()){cpid = (CommPortIdentifier)en.nextElement();if(cpid.getPortType() == CommPortIdentifier.PORT_SERIAL){logger.info(cpid.getName());}}}public boolean isOpen(){return isOpen;}public void open(){if(isOpen){close();}try{portId = CommPortIdentifier.getPortIdentifier(portName);serialPort = (SerialPort) portId.open(appName,timeout);inputStream = serialPort.getInputStream(); serialPort.addEventListener(this);serialPort.notifyOnDataAvailable(true);serialPort.setSerialPortParams(rate, dataBit, stopBit, parityInt);isOpen = true;}catch (NoSuchPortException e){logger.error(e.getMessage());}catch (PortInUseException e){logger.error(e.getMessage());}catch (IOException e){logger.error(e.getMessage());}catch (TooManyListenersException e){logger.error(e.getMessage());}catch (UnsupportedCommOperationException e){logger.error(e.getMessage());}}public void close(){if(isOpen){try{serialPort.notifyOnDataAvailable(false);serialPort.removeEventListener();inputStream.close();serialPort.close();isOpen = false;}catch(IOException ex){logger.error(ex.getMessage());}}}private void checkPort(){if(portId==null){throw new RuntimeException("no found serial port!");}if(serialPort == null){throw new RuntimeException("serialPort object is failed!");}}@Overridepublic synchronized void writeBuff(byte[] message){checkPort();try{outputStream = new BufferedOutputStream(serialPort.getOutputStream());}catch (IOException e){logger.error(e.getMessage());}try{outputStream.write(message);logger.info("發(fā)送成功: "+CommUtil.bytesToHex(message));}catch (IOException e){throw new RuntimeException("向端口發(fā)送信息出錯: "+e.getMessage());}finally{try{outputStream.close();}catch(Exception e){logger.error(e.getMessage());}}}@Overridepublic synchronized byte[] readBuff(){checkPort();byte[] ans = new byte[0];if(recvLen > 0){ans = new byte[recvLen];System.arraycopy(recvBuff,0,ans,0,recvLen);recvLen = 0;}return ans;}@Overridepublic synchronized void serialEvent(SerialPortEvent event){try{Thread.sleep(delay);}catch (InterruptedException e){logger.error(e.getMessage());}switch(event.getEventType()){case SerialPortEvent.BI:case SerialPortEvent.OE:case SerialPortEvent.FE:case SerialPortEvent.PE:case SerialPortEvent.CD:case SerialPortEvent.CTS:case SerialPortEvent.DSR:case SerialPortEvent.RI:case SerialPortEvent.OUTPUT_BUFFER_EMPTY:break;case SerialPortEvent.DATA_AVAILABLE:byte[] readBuffer = new byte[1024];try{while(inputStream.available()>0){int numBytes = inputStream.read(readBuffer);if(recvLen + numBytes>4096){throw new RuntimeException("接收緩存數(shù)組內(nèi)容溢出");}else{logger.info("串口接收:"+CommUtil.bytesToHexWithLen(readBuffer,numBytes));for(int i=0;i<numBytes;i++){recvBuff[recvLen+i] = readBuffer[i];}recvLen = recvLen + numBytes;}}}catch(IOException e){logger.error(e.getMessage());}break;}}@Overridepublic Object getInfo(){// TODO Auto-generated method stubreturn null;} }
構(gòu)造函數(shù)里面是初始化一些串口參數(shù),這些參數(shù)都在serial.properties中存放,至于如何讀取java的properties文件,封裝成
?
這樣方便的讀取將在下一篇文章中簡要說明。
可以看到這個串口程序?qū)崿F(xiàn)了接口中的功能,并且實現(xiàn)了SerialPortEventListener這個接口的serialEvent方法。
serial.properties文件內(nèi)容如下:
?
#serial para #(隨便配) appName=DIAOZHATIAN #串口名 portName=COM3 #波特率 rate=9600 #數(shù)據(jù)位 dataBit=8 #停止位 stopBit=1 #校驗方式 (0:無校驗 1:奇校驗 2:偶校驗) parityInt=0 #(某些參數(shù),不建議修改) timeout=2000 #(某些參數(shù),不建議修改) delay=1000上層應(yīng)用需要開辟一個線程專門讀取readBuff中的數(shù)據(jù),否則上面串口程序中開辟的4k的recvBuff很快會被塞滿。
舉例:上層應(yīng)用采用多線程雙向隊列進行存儲接收到的數(shù)據(jù)private ConcurrentLinkedDeque<Byte> deque = new ConcurrentLinkedDeque<Byte>();
?
private class readThread implements Runnable{@Overridepublic void run(){while(true){byte[] recvBuff = comm.readBuff();if(recvBuff.length>0){for(int i=0;i<recvBuff.length;i++){deque.add(recvBuff[i]);}}try{TimeUnit.MILLISECONDS.sleep(1000);}catch (InterruptedException e){e.printStackTrace();}}}}然后在初始化的時候開啟這個線程并設(shè)置成后臺線程。
?
ExecutorService pool = Executors.newFixedThreadPool(1);Thread thread1 = new Thread(new readThread());thread1.setDaemon(true);pool.execute(thread1);關(guān)于串口通訊,先寫到這里。再下一篇先插播一下如何讀取java的配置文件properties,然后繼續(xù)講述如何使用java進行tcp通訊。
歡迎跳轉(zhuǎn)到本文的原文鏈接:https://honeypps.com/network/java-serial-comm/
歡迎支持筆者新作:《深入理解Kafka:核心設(shè)計與實踐原理》和《RabbitMQ實戰(zhàn)指南》,同時歡迎關(guān)注筆者的微信公眾號:朱小廝的博客。總結(jié)
以上是生活随笔為你收集整理的JAVA通信编程(一)——串口通讯的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring+Mybatis多数据源配置
- 下一篇: JAVA通信编程(二)——如何读取jav