通常,串口通信应用程序有两种模式,一种是实现SerialPortEventListener接口,监听各种串口事件并作相应处理;另一种是建立一个独立的接收线程专门负责数据的接收。但这两种方法在某些情况下存在很严重的问题。

事件监听模型

事件监听模型的运作方式:

(1)首先需要在端口控制类(如SerialManager)加上“implements SerialPortEventListener

(2)在初始化时加入如下代码:

try {
	SerialPort sPort.addEventListener(SerialManager);
} 
catch (TooManyListenersException e) {
	throw new SerialConnectionException("too many listeners added");
	sPort.close();
}
sPort.notifyOnDataAvailable(true);

(3)覆写public void serialEvent(SerialPortEvent e)方法,在其中对事件进行判断。

BI -通讯中断.

CD -载波检测.

CTS -清除发送.

DATA_AVAILABLE -有数据到达.

DSR -数据设备准备好.

FE -帧错误.

OE -溢位错误.

OUTPUT_BUFFER_EMPTY -输出缓冲区已清空.

PE -奇偶校验错.

RI - 振铃指示.

一般最常用的就是DATA_AVAILABLE--串口有数据到达事件。也就是说当串口有数据到达时,可以在serialEvent中接收并处理所收到的数据。模式存在的问题:数据有丢失

串口读数据的线程模型

这个模型顾名思义,就是将接收数据的操作写成一个线程的形式。将收到的数据打包放到一个缓存中,然后启动另一个线程从缓存中获取并处理数据。两个线程以生产者—消费者模式协同工作,数据的流向如下图所示:


java 监听某个端口获取输入输出数据 java监听串口_Java串口通信模式


此模式存在的问题:传感器节点已经停止传送数据,但是串口线程依然在执行读串口的操作。原因是传感器节点发送数据过快,而接收线程处理不过来,所以InputStream就先把已经到达却还没处理的字节缓存起来,于是就导致了明明传感器节点已经不再发数据了,而控制台却还能看见数据不断打印的现象。简言之,线程模型导致了对于发送端数据发送速率过快的情况下的数据接收延迟

申明一点,对于数据发送速率不是很快的情况下,前两种模型应该还是好用的,只是特殊情况应该特殊处理。

第三种方法(根源入手)

解决上述问题的方法其实很简单,就是从根源入手。根源就是接收线程导致的,那干脆取消接收线程和作为中介的共享缓存,而直接在处理线程中调用串口读数据的方法来解决问题(为什么不把处理线程也一并取消?----都取消应用程序界面会锁死,所以必须保留)于是程序变成了这样:

public byte[] getPack(){
    while (true) {
    	// PacketLength为数据包长度
    	byte[] msgPack = new byte[PacketLength];
    	if( (newData = is.read()) != -1){
    		for(int i = 0; i < PacketLength; i++){
    			msgPack[i] = (byte) newData;
    		}
    		System.out.println(msgPack[i]);
    	}
    }
    return msgPack;
}

在处理线程中调用这个方法返回所需要的数据序列并处理之,这样不但没有丢失数据的现象出现,也没有数据接收延迟了。这里唯一需要注意的就是当串口停止发送数据或没有数据的时候is.read()一直都返回-1,如果一旦在开始接收数据的时候发现-1就不要理它,继续接收,直到收到真正的数据为止。