I'm trying to read card UID / HCE android device UID from my pcsc java code.
But I'm getting the following error
javax.smartcardio.CardException: sun.security.smartcardio.PCSCException: Unknown error 0x1f
Although I'm able to get ATS bit when I try to get UID I get this error.
I have refered my code from here
CODE
import java.io.Console;
import java.util.List;
import java.util.ListIterator;
import java.util.Scanner;
import javax.smartcardio.ATR;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.smartcardio.TerminalFactory;
public class PCSC {
private byte[] atr = null;
private String protocol = null;
private byte[] historical = null;
public CardTerminal selectCardTerminal() {
try {
// show the list of available terminals
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
ListIterator<CardTerminal> terminalsIterator = terminals
.listIterator();
CardTerminal terminal = null;
CardTerminal defaultTerminal = null;
if (terminals.size() > 1) {
System.out
.println("Please choose one of these card terminals (1-"
+ terminals.size() + "):");
int i = 1;
while (terminalsIterator.hasNext()) {
terminal = terminalsIterator.next();
System.out.print("[" + i + "] - " + terminal
+ ", card present: " + terminal.isCardPresent());
if (i == 1) {
defaultTerminal = terminal;
System.out.println(" [default terminal]");
} else {
System.out.println();
}
i++;
}
Scanner in = new Scanner(System.in);
try {
int option = in.nextInt();
terminal = terminals.get(option - 1);
} catch (Exception e2) {
// System.err.println("Wrong value, selecting default terminal!");
terminal = defaultTerminal;
}
System.out.println("Selected: " + terminal.getName());
// Console console = System.console();
return terminal;
}
} catch (Exception e) {
System.err.println("Error occured:");
e.printStackTrace();
}
return null;
}
public String byteArrayToHexString(byte[] b) {
StringBuffer sb = new StringBuffer(b.length * 2);
for (int i = 0; i < b.length; i++) {
int v = b[i] & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString().toUpperCase();
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character
.digit(s.charAt(i + 1), 16));
}
return data;
}
public Card establishConnection(CardTerminal ct) {
this.atr = null;
this.historical = null;
this.protocol = null;
System.out
.println("To establish connection, please choose one of these protocols (1-4):");
System.out.println("[1] - T=0");
System.out.println("[2] - T=1");
System.out.println("[3] - T=CL");
System.out.println("[4] - * [default]");
String p = "*";
Scanner in = new Scanner(System.in);
try {
int option = in.nextInt();
if (option == 1)
p = "T=0";
if (option == 2)
p = "T=1";
if (option == 3)
p = "T=CL";
if (option == 4)
p = "*";
} catch (Exception e) {
// System.err.println("Wrong value, selecting default protocol!");
p = "*";
}
System.out.println("Selected: " + p);
Card card = null;
try {
card = ct.connect(p);
} catch (CardException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
ATR atr = card.getATR();
System.out.println("Connected:");
System.out.println(" - ATR: " + byteArrayToHexString(atr.getBytes()));
System.out.println(" - Historical: "
+ byteArrayToHexString(atr.getHistoricalBytes()));
System.out.println(" - Protocol: " + card.getProtocol());
this.atr = atr.getBytes();
this.historical = atr.getHistoricalBytes();
this.protocol = card.getProtocol();
return card;
}
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
PCSC pcsc = new PCSC();
CardTerminal ct = pcsc.selectCardTerminal();
Card c = null;
if (ct != null) {
c = pcsc.establishConnection(ct);
CardChannel cc = c.getBasicChannel();
try {
ResponseAPDU answer = cc.transmit(new CommandAPDU(0xFF, 0xCA, 0x00, 0x00, 0x00));
System.out.println("answer: " + answer.toString());
} catch (CardException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
OUTPUT
Please choose one of these card terminals (1-3):
[1] - PC/SC terminal ACS ACR1281 1S Dual Reader ICC 0, card present: false [default terminal]
[2] - PC/SC terminal ACS ACR1281 1S Dual Reader PICC 0, card present: true
[3] - PC/SC terminal ACS ACR1281 1S Dual Reader SAM 0, card present: false
2
Selected: ACS ACR1281 1S Dual Reader PICC 0
To establish connection, please choose one of these protocols (1-4):
[1] - T=0
[2] - T=1
[3] - T=CL
[4] - * [default]
4
Selected: *
Connected:
- ATR: 3B8F8001804F0CA0000003060300030000000068
- Historical: 804F0CA00000030603000300000000
- Protocol: T=1
javax.smartcardio.CardException: sun.security.smartcardio.PCSCException: Unknown error 0x1f
at sun.security.smartcardio.ChannelImpl.doTransmit(Unknown Source)
at sun.security.smartcardio.ChannelImpl.transmit(Unknown Source)
at myPcscGoogleCode.PCSC.main(PCSC.java:186)
Caused by: sun.security.smartcardio.PCSCException: Unknown error 0x1f
at sun.security.smartcardio.PCSC.SCardTransmit(Native Method)
... 3 more
Already referred the following
Get UID of Mifare Ultralight with SCL010
Please help !
Thanks
I was able to find the solution and get the UID by changing the command APDU.
Then I converted the result byte[] to hex UID.
try {
//get UID cmd apdu
ResponseAPDU answer = cc.transmit( new CommandAPDU(new byte[] { (byte)0xFF, (byte)0xCA, (byte)0x00, (byte)0x00, (byte)0x00 } ));
System.out.println("answer: " + answer.toString());
byte[] reponseBytesArr=answer.getBytes();
System.out.println("answer byte[]");
StringBuilder sb = new StringBuilder();
for(int i=0;i<reponseBytesArr.length;i++){
//print arr
byte b =reponseBytesArr[i];
System.out.println(b);
if(i<=reponseBytesArr.length-3){
// append uid
sb.append(String.format("%02X ", b));
}
}
System.out.println("UID: "+sb.toString());
Output:
Please choose one of these card terminals (1-3):
[1] - PC/SC terminal ACS ACR1281 1S Dual Reader ICC 0, card present: false [default terminal]
[2] - PC/SC terminal ACS ACR1281 1S Dual Reader PICC 0, card present: true
[3] - PC/SC terminal ACS ACR1281 1S Dual Reader SAM 0, card present: false
2
Selected: ACS ACR1281 1S Dual Reader PICC 0
To establish connection, please choose one of these protocols (1-4):
[1] - T=0
[2] - T=1
[3] - T=CL
[4] - * [default]
4
Selected: *
Connected:
-Card Class class sun.security.smartcardio.CardImpl
- ATR: 3B8F8001804F0CA0000003060300030000000068
- Historical: 804F0CA00000030603000300000000
- Protocol: T=1
answer: ResponseAPDU: 9 bytes, SW=9000
answer byte[]
4
-28
-66
42
0
41
-128
-112
0
UID: 04 E4 BE 2A 00 29 80
Related
Background:
We have been using the Schneider M251 PLC with the TM3AI8, TM3DQ8R and TM3DI8 modules for some time now and I have noticed that we are getting unexpected values being sent over Modbus tcp.
Most of the time, the signal we receive over the Modbus protocol is in the 4mA range at the lower portion of our expected signal. However, we are seeing an intermittent 0 that is infrequently sent to our java software over modbus tcp using the easy modbus tcp library
Library: http://easymodbustcp.net/en/
Specifically, when we call:
val = modbusClient.ReadHoldingRegisters(3005, 1)[0];
We are seeing an intermittent infrequent val = 0 appearing but only infrequently. When I check the PLC software called soMachine, it does not appear to ever show a zero value. Has anyone encountered this kind of issue before? I set up a POU using structure text to count the number of times the IO reading is 0 and apparently it never is. So I think it has something to do with the modbus library.
I have provided a system.out.println(val) below showing the issue:
VAL IS THIS NOW: 3995
VAL IS THIS NOW: 3989
VAL IS THIS NOW: 3995
VAL IS THIS NOW: 3995
VAL IS THIS NOW: 3986
VAL IS THIS NOW: 3998
VAL IS THIS NOW: 3992
VAL IS THIS NOW: 3989
VAL IS THIS NOW: 3998
VAL IS THIS NOW: 3995
VAL IS THIS NOW: 0 <- Random intermittent 0
VAL IS THIS NOW: 3986
VAL IS THIS NOW: 3998
VAL IS THIS NOW: 3992
VAL IS THIS NOW: 3989
VAL IS THIS NOW: 3995
VAL IS THIS NOW: 3989
VAL IS THIS NOW: 3989
The I/O config on the plc is 0-20mA extended and the sensor is A 4-20mA sensor. A zero reading on our unit scale represents -2500units which poses an issue for our system given it represents a large discrepancy in our measurement. We are using the diagnostic bits which are available on the Schneider card as well as well to detect sensor faults. However, the sensor does not appear to be faulty and is working fine.
As you can see, the signal off the IO card is a bit noisy as well. A multimeter does not fluctuate when reading the voltage for this sensor and it is really stable on our multimeters at the 3.989mV range.
When we see the intermittent 0's printed to the console, we do not see the 0's in soMachine. Further, the method polling for the values is being called every 250ms.
We have resorted to the following "filter" but it is not an ideal solution to the issue.
static int pt05ErrorCount = 0, pt05lastVal;
private static double readRegister3005() throws IOException{
try {
int val;
if(PLC.isConnected()) {
val = modbusClient.ReadHoldingRegisters(3005, 1)[0];
if(val > 0) {
pt05ErrorCount = 0;
pt05lastVal = val;
}
}else {
val = -1000;
}
System.out.println("VAL IS THIS NOW: "+val);
if(val > 0) {
return (double) (10000-0)/(20-4)*((double) val/1000-4)+0; //convert mA to psig
}else {
if(pt05ErrorCount > maxErrorCount) {
pt05ErrorCount++;
return (double) (10000-0)/(20-4)*((double) val/1000-4)+0;
}else {
return (double) (10000-0)/(20-4)*((double) pt05lastVal/1000-4)+0;
}
}
} catch(Exception e) {
System.out.println(e);
throw new IOException(Globals.getSystemCalendarDateandTime() + " PLC.java > readRegister3005(): ERROR reading PLC address 3005.");
}
}
Please let me know if you require more information about the issue.
The ReadHoldingRegisters method is as follows:
/**
* Read Holding Registers from Server
* #param startingAddress Fist Address to read; Shifted by -1
* #param quantity Number of Inputs to read
* #return Holding Registers from Server
* #throws de.re.easymodbus.exceptions.ModbusException
* #throws UnknownHostException
* #throws SocketException
* #throws SerialPortTimeoutException
* #throws SerialPortException
*/
public int[] ReadHoldingRegisters(int startingAddress, int quantity) throws de.re.easymodbus.exceptions.ModbusException,
UnknownHostException, SocketException, IOException, SerialPortException, SerialPortTimeoutException
{
if (tcpClientSocket == null)
throw new de.re.easymodbus.exceptions.ConnectionException("connection Error");
if (startingAddress > 65535 | quantity > 125)
throw new IllegalArgumentException("Starting adress must be 0 - 65535; quantity must be 0 - 125");
int[] response = new int[quantity];
this.transactionIdentifier = toByteArray(0x0001);
this.protocolIdentifier = toByteArray(0x0000);
this.length = toByteArray(0x0006);
//serialdata = this.unitIdentifier;
this.functionCode = 0x03;
this.startingAddress = toByteArray(startingAddress);
this.quantity = toByteArray(quantity);
byte[] data = new byte[]
{
this.transactionIdentifier[1],
this.transactionIdentifier[0],
this.protocolIdentifier[1],
this.protocolIdentifier[0],
this.length[1],
this.length[0],
this.unitIdentifier,
this.functionCode,
this.startingAddress[1],
this.startingAddress[0],
this.quantity[1],
this.quantity[0],
this.crc[0],
this.crc[1]
};
if (this.serialflag)
{
crc = calculateCRC(data, 6, 6);
data[data.length -2] = crc[0];
data[data.length -1] = crc[1];
}
byte[] serialdata = null;
if (serialflag)
{
serialdata = new byte[8];
java.lang.System.arraycopy(data, 6,serialdata,0,8);
serialPort.purgePort(SerialPort.PURGE_RXCLEAR);
serialPort.writeBytes(serialdata);
if (debug) StoreLogData.getInstance().Store("Send Serial-Data: "+ Arrays.toString(serialdata));
long dateTimeSend = DateTime.getDateTimeTicks();
byte receivedUnitIdentifier = (byte)0xFF;
serialdata = new byte[256];
int expectedlength = 5+2*quantity;
while (receivedUnitIdentifier != this.unitIdentifier & !((DateTime.getDateTimeTicks() - dateTimeSend) > 10000 * this.connectTimeout))
{
serialdata = serialPort.readBytes(expectedlength, this.connectTimeout);
receivedUnitIdentifier = serialdata[0];
}
if (receivedUnitIdentifier != this.unitIdentifier)
{
data = new byte[256];
}
if (serialdata != null)
{
data = new byte[262];
System.arraycopy(serialdata, 0, data, 6, serialdata.length);
if (debug) StoreLogData.getInstance().Store("Receive ModbusRTU-Data: " + Arrays.toString(data));
}
for (int i = 0; i < quantity; i++)
{
byte[] bytes = new byte[2];
bytes[0] = data[3+i*2];
bytes[1] = data[3+i*2+1];
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
response[i] = byteBuffer.getShort();
}
}
if (tcpClientSocket.isConnected() | udpFlag)
{
if (udpFlag)
{
InetAddress ipAddress = InetAddress.getByName(this.ipAddress);
DatagramPacket sendPacket = new DatagramPacket(data, data.length, ipAddress, this.port);
DatagramSocket clientSocket = new DatagramSocket();
clientSocket.setSoTimeout(500);
clientSocket.send(sendPacket);
data = new byte[2100];
DatagramPacket receivePacket = new DatagramPacket(data, data.length);
clientSocket.receive(receivePacket);
clientSocket.close();
data = receivePacket.getData();
}
else
{
outStream.write(data, 0, data.length-2);
if (debug) StoreLogData.getInstance().Store("Send ModbusTCP-Data: "+Arrays.toString(data));
if (sendDataChangedListener.size() > 0)
{
sendData = new byte[data.length-2];
System.arraycopy(data, 0, sendData, 0, data.length-2);
for (SendDataChangedListener hl : sendDataChangedListener)
hl.SendDataChanged();
}
data = new byte[2100];
int numberOfBytes = inStream.read(data, 0, data.length);
if (receiveDataChangedListener.size() > 0)
{
receiveData = new byte[numberOfBytes];
System.arraycopy(data, 0, receiveData, 0, numberOfBytes);
for (ReceiveDataChangedListener hl : receiveDataChangedListener)
hl.ReceiveDataChanged();
if (debug) StoreLogData.getInstance().Store("Receive ModbusTCP-Data: " + Arrays.toString(data));
}
}
}
if (((int) data[7]) == 0x83 & ((int) data[8]) == 0x01)
{
if (debug) StoreLogData.getInstance().Store("FunctionCodeNotSupportedException Throwed");
throw new de.re.easymodbus.exceptions.FunctionCodeNotSupportedException("Function code not supported by master");
}
if (((int) data[7]) == 0x83 & ((int) data[8]) == 0x02)
{
if (debug) StoreLogData.getInstance().Store("Starting adress invalid or starting adress + quantity invalid");
throw new de.re.easymodbus.exceptions.StartingAddressInvalidException("Starting adress invalid or starting adress + quantity invalid");
}
if (((int) data[7]) == 0x83 & ((int) data[8]) == 0x03)
{
if (debug) StoreLogData.getInstance().Store("Quantity invalid");
throw new de.re.easymodbus.exceptions.QuantityInvalidException("Quantity invalid");
}
if (((int) data[7]) == 0x83 & ((int) data[8]) == 0x04)
{
if (debug) StoreLogData.getInstance().Store("Error reading");
throw new de.re.easymodbus.exceptions.ModbusException("Error reading");
}
for (int i = 0; i < quantity; i++)
{
byte[] bytes = new byte[2];
bytes[0] = data[9+i*2];
bytes[1] = data[9+i*2+1];
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
response[i] = byteBuffer.getShort();
}
return (response);
}
Regards,
I'm using Raspbian on a Raspberry Pi 3. I'm learning how to code in Java (SE runtime version 1.8.0_65), and I need to communicate raw data with a USB connected Bill Acceptor. According to the manufacturer's documentation, the USB unit mimics a serial-type interface. When I plug the device in, it appears in /dev/serial/by-id. I wrote C code 20+ years ago using modems on SCO Unix setups. If memory serves, I basically treated the modems /dev/ttyxx as a file for reading and writing. I wrote a C++ program 10+ years ago on Windows XP using a similar Bill Acceptor unit (same manufacturer and model) connected to a serial port instead of USB. The documentation shows both USB and serial units use the same data strings for input and output, so I know the SendString data should be correct for triggering a valid response. However, I'm not getting a reply from the unit, so I'm guessing I'm not connecting properly to it.
Here is the code...
public static void main(String[] args) {
try
{
System.out.println("*****************************");
System.out.println("***** Starting Program *****");
System.out.println("*****************************");
String strUsbDeviceDir = "/dev/serial/by-id";
File myUsbDeviceDir = new File(strUsbDeviceDir);
if(myUsbDeviceDir.exists())
{
String[] myUsbDevices = myUsbDeviceDir.list();
for(int i=0; i<myUsbDevices.length; i++)
{
if(myUsbDevices[i].contains("EBDS_over_USB"))
{
System.out.println("Connecting to " + myUsbDevices[i]);
funcBillAcceptor(strUsbDeviceDir + "/" + myUsbDevices[i]);
}
else
{
System.out.println("Not using " + myUsbDevices[i]);
}
}
}
}
catch (Exception ex)
{
System.err.println(ex.toString());
}
}
public static void funcBillAcceptor(String strBillAcceptor)
{
boolean bOddCount = false;
byte[] SendString = new byte[8];
byte[] RecvString = new byte[10];
byte CheckDigit;
int iSendStringCount, iRecvStringCount, iRecvEmptyCount;
try
{
File fBillAcceptor = new File(strBillAcceptor);
if(!fBillAcceptor.canRead())
{
System.out.println("No Read Permission for " + strBillAcceptor);
return;
}
if(!fBillAcceptor.canWrite())
{
System.out.println("No Write Permission for " + strBillAcceptor);
return;
}
RandomAccessFile rafBillAcceptor = new RandomAccessFile(strBillAcceptor, "rwd");
if(rafBillAcceptor != null)
{
System.out.println("Successfully opened " + strBillAcceptor);
}
while(fBillAcceptor.exists())
{
SendString[0] = (byte) 0x02; //STX
SendString[1] = (byte) 0x08;
if(bOddCount)
{
SendString[2] = (byte) 0x10;
bOddCount = false;
}
else
{
SendString[2] = (byte) 0x11;
bOddCount = true;
}
SendString[3] = (byte) 0x1F;
SendString[4] = (byte) 0x0C;
SendString[5] = (byte) 0x00;
SendString[6] = (byte) 0x03; //ETX
//CheckDigit skips STX (byte 0) with byte 1 as seed/initial value
//To calculate the check digit, start with next byte (2)
CheckDigit = SendString[1];
iSendStringCount = 2;
while(SendString[iSendStringCount] != 0x03)
{
CheckDigit = (byte) (SendString[iSendStringCount]^CheckDigit); //XOR current CheckDigit value with next byte
iSendStringCount++;
}
iSendStringCount++; //advance one more so we don't overwrite ETX
SendString[iSendStringCount] = (byte) CheckDigit;
try
{
rafBillAcceptor.write(SendString);
System.out.println("Sent: " + DatatypeConverter.printHexBinary(SendString));
}
catch (Exception ex)
{
System.err.println("Write exception: " + ex.toString());
}
System.out.println("Reading...");
iRecvStringCount = iRecvEmptyCount = 0;
try
{
do
{
iRecvStringCount = rafBillAcceptor.read(RecvString);
System.out.println("Read " + iRecvStringCount + " bytes.");
if(iRecvStringCount < 0)
{
iRecvEmptyCount++;
Thread.sleep(5);
}
} while (iRecvStringCount < 0 && iRecvEmptyCount < 100);
if(iRecvStringCount > 0)
{
System.out.println("Received: " + DatatypeConverter.printHexBinary(RecvString));
}
}
catch (Exception ex)
{
System.err.println("Read exception: " + ex.toString());
}
}
}
catch (Exception ex)
{
System.err.println(ex.toString());
}
}
Here is the output I'm getting...
*****************************
***** Starting Program *****
*****************************
Connecting to usb-Silicon_Labs_Series_2000_Bill_Acceptor__EBDS_over_USB__46580120748-if00-port0
Successfully opened /dev/serial/by-id/usb-Silicon_Labs_Series_2000_Bill_Acceptor__EBDS_over_USB__46580120748-if00-port0
Sent: 0208111F0C00030A
Reading...
Am I missing something obvious, or just going about this all wrong? Thanks for any suggestions!
I was able to successfully communicate with the Bill Acceptor from a Raspberry Pi 3 (running Raspbian) using the RXTXComm library. When communicating with the device from an old Windows XP computer using a 9-pin serial/RS232 harness, I had to use 9600/7/E/1. If I don't use those values for setSerialPortParams (in my sample code), I'm not able to correctly send/receive data. So, you need to know what the device is expecting in order to use this approach with other "serial" hardware connected via USB. Below is code with the basic functionality. I started with one of the sample programs that come with the RXTXComm library and built out from there. Since this is a sample program, I haven't yet added logic to verify the checksum digit for data coming from the device.
You may notice at the bottom of the code that I'm able to determine the /dev/ttyUSBx using the canonical name of the device that I pull from the /dev/serial/by-id directory/folder. It finds the correct device name regardless of which USB port the unit is plugged in to or what order the USB devices get initialized by the system. I have a card dispenser that is also a USB serial device, so I was able to test and confirm this functionality.
public TwoWaySerialComm()
{
super();
}
static boolean bReadyToSend = false;
void connect ( String portName ) throws Exception
{
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
if ( portIdentifier.isCurrentlyOwned() )
{
System.out.println("Error: " + portName + " is currently in use.");
}
else
{
CommPort commPort = portIdentifier.open(this.getClass().getName(), 2000);
if ( commPort instanceof SerialPort )
{
SerialPort serialPort = (SerialPort) commPort;
serialPort.setSerialPortParams(9600, SerialPort.DATABITS_7, SerialPort.STOPBITS_1, SerialPort.PARITY_EVEN);
InputStream in = serialPort.getInputStream();
OutputStream out = serialPort.getOutputStream();
// Thread handling logic
// https://www.javaspecialists.eu/archive/Issue056.html
Thread readThread = new Thread(new SerialReader(in));
readThread.start();
Thread writeThread = new Thread(new SerialWriter(out));
writeThread.start();
// Running threads for 10 seconds then stopping to ensure threads are shutting down
long threadCurrTime = System.currentTimeMillis();
long threadStartTime = System.currentTimeMillis();
while( (threadCurrTime - threadStartTime) < 10000)
{
try
{
Thread.sleep(100);
}
catch (InterruptedException ex)
{
Logger.getLogger(TwoWaySerialComm.class.getName()).log(Level.SEVERE, null, ex);
}
threadCurrTime = System.currentTimeMillis();
}
if(writeThread.isAlive())
{
//System.out.println("Sending interrupt to SerialWriter thread...");
writeThread.interrupt();
writeThread.join();
//System.out.println("SerialWriter thread is shut down.");
}
//else
//{
// System.out.println("SerialWriter thread is already shut down.");
//}
if(readThread.isAlive())
{
//System.out.println("Sending interrupt to SerialReader thread...");
readThread.interrupt();
readThread.join();
//System.out.println("SerialReader thread is shut down.");
}
//else
//{
// System.out.println("SerialReader thread is already shut down.");
//}
commPort.close();
}
else
{
System.out.println("Error: " + portName + " is not recognized as a valid serial device.");
}
}
}
/* SerialReader thread logic */
public static class SerialReader implements Runnable
{
InputStream in;
boolean bShuttingDown = false;
public SerialReader ( InputStream in )
{
this.in = in;
}
public void run ()
{
byte[] RecvString = new byte[12];
String strResponse;
int len = -1;
try
{
while (!bShuttingDown)
{
len = this.in.read(RecvString);
if( len > -1 )
{
strResponse = "";
if(RecvString[0] == 0x02 && RecvString[9] == 0x03)
{
if(RecvString[3] == 0x00 && RecvString[4] == 0x00 && RecvString[5] == 0x00 && RecvString[6] == 0x00)
{
strResponse = "Device not ready.";
}
else
{
//-- RecvString[3]------------------
if(RecvString[3] == 0x01)
strResponse = " - Idling";
else
if(RecvString[3] == 0x02)
strResponse = " - Accepting";
else
if(RecvString[3] == 0x04)
{
strResponse = " - Escrowed";
if(RecvString[5] == 0x08)
strResponse += " $1";
else
if(RecvString[5] == 0x10)
strResponse += " $2";
else
if(RecvString[5] == 0x18)
strResponse += " $5";
else
if(RecvString[5] == 0x20)
strResponse += " $10";
else
if(RecvString[5] == 0x28)
strResponse += " $20";
else
strResponse += " unrecognized bill inserted";
}
else
if(RecvString[3] == 0x08)
strResponse = " - Stacking";
else
if(RecvString[3] == 0x10)
strResponse = " - Stacked";
else
if(RecvString[3] == 0x11)
strResponse = " - Returning";
else
if(RecvString[3] == 0x12)
strResponse = " - Returned";
//-- RecvString[4]------------------
if(RecvString[4] == 0x01)
strResponse += " - Cheated";
else
if(RecvString[4] == 0x02)
strResponse += " - Rejected";
else
if(RecvString[4] == 0x04)
strResponse += " - Jammed";
else
if(RecvString[4] == 0x08)
strResponse += " - Bill Stacker Full";
else
if(RecvString[4] == 0x10)
strResponse += " - Removable Cassette Installed";
else
if(RecvString[4] == 0x11)
strResponse += " - Reserved";
else
if(RecvString[4] == 0x12)
strResponse += " - Calibration mode";
//-- RecvString[5]------------------
if(RecvString[5] == 0x01)
strResponse += " - Power Up Reset";
else
if(RecvString[5] == 0x02)
strResponse += " - Invalid Command";
else
if(RecvString[5] == 0x04)
strResponse += " - Non-recoverable fault";
}
if(!strResponse.contains("Idling"))
System.out.println("Recv: " + DatatypeConverter.printHexBinary(RecvString) + strResponse);
}
else
{
System.out.println("Recv (invalid): " + DatatypeConverter.printHexBinary(RecvString));
}
try
{
Thread.sleep(100); // need this delay before we send next polling message, otherwise the data doesn't come in correctly
bReadyToSend = true;
}
catch (InterruptedException ex)
{
Thread.currentThread().interrupt();
//System.out.println("Shut down SerialReader thread.");
bShuttingDown = true;
break;
}
}
}
}
catch ( IOException ex )
{
System.out.println("Recv exception: " + ex.toString());
}
}
}
/* SerialWriter thread logic */
public static class SerialWriter implements Runnable
{
OutputStream out;
long lastSendTime = System.currentTimeMillis() - 1001;
long currSendTime;
byte[] SendString = new byte[8];
byte CheckDigit;
int iSendStringCount;
boolean bOddCount = true;
boolean bShuttingDown = false;
public SerialWriter ( OutputStream out )
{
this.out = out;
}
public void run ()
{
while(!bShuttingDown)
{
currSendTime = System.currentTimeMillis();
if(currSendTime - lastSendTime > 1000) // if it's been more than a second, send query string
bReadyToSend = true;
if(bReadyToSend)
{
SendString[0] = (byte) 0x02; //STX
SendString[1] = (byte) 0x08;
if(bOddCount)
{
SendString[2] = (byte) 0x10;
bOddCount = false;
}
else
{
SendString[2] = (byte) 0x11;
bOddCount = true;
}
SendString[3] = (byte) 0x7F;
SendString[4] = (byte) 0x1C;
SendString[5] = (byte) 0x00;
SendString[6] = (byte) 0x03; //ETX
//CheckDigit skips STX (byte 0) with byte 1 as seed/initial value
//To calculate the check digit, start with next byte (2)
CheckDigit = SendString[1];
iSendStringCount = 2;
while(SendString[iSendStringCount] != 0x03)
{
CheckDigit = (byte) (SendString[iSendStringCount]^CheckDigit); //XOR current CheckDigit value with next byte
iSendStringCount++;
}
iSendStringCount++; //advance one more so we don't overwrite ETX
SendString[iSendStringCount] = (byte) CheckDigit;
try
{
lastSendTime = System.currentTimeMillis();
this.out.write(SendString);
//System.out.println("Sent: " + DatatypeConverter.printHexBinary(SendString));
}
catch ( IOException ex )
{
System.out.println("Send exception: " + ex.toString());
}
try
{
Thread.sleep(1); // this is hear simply to catch an external interrupt
}
catch (InterruptedException ex)
{
Thread.currentThread().interrupt();
//System.out.println("Shut down SerialWriter thread.");
bShuttingDown = true;
break;
}
bReadyToSend = false;
}
}
}
}
public static void main ( String[] args )
{
try
{
System.out.println("*****************************");
System.out.println("***** Starting Program *****");
System.out.println("*****************************");
String strUsbDeviceDir = "/dev/serial/by-id";
File myUsbDeviceDir = new File(strUsbDeviceDir);
if(myUsbDeviceDir.exists())
{
String[] myUsbDevices = myUsbDeviceDir.list();
for(int i=0; i<myUsbDevices.length; i++)
{
if(myUsbDevices[i].contains("EBDS_over_USB"))
{
File tempFile = new File(strUsbDeviceDir + "/" + myUsbDevices[i]);
String usbCanonicalName = tempFile.getCanonicalFile().toString(); //gives me /dev/ttyUSBx where 'x' is the USB device number
System.out.println("Connecting to " + usbCanonicalName + " (" + myUsbDevices[i] + ")");
(new TwoWaySerialComm()).connect(usbCanonicalName);
}
else
{
System.out.println("Not using " + myUsbDevices[i]);
}
}
}
}
catch ( Exception ex )
{
System.out.println("Connect exception: " + ex.toString());
}
System.out.println("*****************************");
System.out.println("***** Program Finished ******");
System.out.println("*****************************");
}
Output when I put a $1 bill in (I'm developing/compiling from NetBeans IDE 8.2 on Windows 10 Pro and running using remote debugging on the RPi3. I'm guessing that's the source of the RXTX Version mismatch warning):
*****************************
***** Starting Program *****
*****************************
Connecting to /dev/ttyUSB0 (usb-Silicon_Labs_Series_2000_Bill_Acceptor__EBDS_over_USB__46580120748-if00-port0)
Stable Library
=========================================
Native lib Version = RXTX-2.2pre2
Java lib Version = RXTX-2.1-7
WARNING: RXTX Version mismatch
Jar version = RXTX-2.1-7
native lib Version = RXTX-2.2pre2
Recv (invalid): 020000000000000000000000
Recv (invalid): 0B2001100800503603540000
Recv: 020B20041008005036035100 - Escrowed $1 - Removable Cassette Installed
*****************************
***** Program Finished ******
*****************************
I hope this description and sample code can help someone else.
I want to take real-time data using modbus tcp/ip simulator for filling of a tank that uses port no 502.
How can I write a code in java to get the holding register value from the simulator and also I want to control the values of it?
If you use a Modbus library like this one most of the work is already done for you.
ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder("localhost").build();
ModbusTcpMaster master = new ModbusTcpMaster(config);
CompletableFuture<ReadHoldingRegistersResponse> future =
master.sendRequest(new ReadHoldingRegistersRequest(0, 10), 0);
future.thenAccept(response -> {
System.out.println("Response: " + ByteBufUtil.hexDump(response.getRegisters()));
ReferenceCountUtil.release(response);
});
import java.io.* ;
import java.net.* ;
import java.util.*;
public class Modcli {
public static void main(String argv[]) {
if (argv.length < 4) {
System.out.println("usage: java test3 dns_name unit reg_no num_regs");
System.out.println("eg java test3 aswales8.modicon.com 5 0 10");
return;
}
try {
String ip_adrs = argv[0];
int unit = Integer.parseInt(argv[1]);
int reg_no = Integer.parseInt(argv[2]);
int num_regs = Integer.parseInt(argv[3]);
System.out.println("ip_adrs = "+ip_adrs+" unit = "+unit+" reg_no = "+
reg_no+" num_regs = "+num_regs);
// set up socket
Socket es = new Socket(ip_adrs,502);
OutputStream os= es.getOutputStream();
FilterInputStream is = new BufferedInputStream(es.getInputStream());
byte obuf[] = new byte[261];
byte ibuf[] = new byte[261];
int c = 0;
int i;
// build request of form 0 0 0 0 0 6 ui 3 rr rr nn nn
for (i=0;i<5;i++) obuf[i] = 0;
obuf[5] = 6;
obuf[6] = (byte)unit;
obuf[7] = 3;
obuf[8] = (byte)(reg_no >> 8);
obuf[9] = (byte)(reg_no & 0xff);
obuf[10] = (byte)(num_regs >> 8);
obuf[11] = (byte)(num_regs & 0xff);
// send request
os.write(obuf, 0, 12);
// read response
i = is.read(ibuf, 0, 261);
if (i<9) {
if (i==0) {
System.out.println("unexpected close of connection at remote end");
} else {
System.out.println("response was too short - "+i+" chars");
}
} else if (0 != (ibuf[7] & 0x80)) {
System.out.println("MODBUS exception response - type "+ibuf[8]);
} else if (i != (9+2*num_regs)) {
System.out.println("incorrect response size is "+i+
" expected"+(9+2*num_regs));
} else {
for (i=0;i<=2;i++) {
int w = (ibuf[0+i+i]<<8) + ibuf[1+i+i];
System.out.println("word "+i+" = "+w);
}
for (i=3;i<=5;i++) {
int w = (ibuf[i+3]) ;
System.out.println("word "+i+" = "+w);
}
for (i=0;i<num_regs;i++) {
int w = (ibuf[9+i+i]<<8) + ibuf[10+i+i];
System.out.println("word "+i+" = "+w);
}
}
// close down
es.close();
} catch (Exception e) {
System.out.println("exception :"+e);
}
}
}
I have a device that comunicate with the program via rs232.Device send a message and the program read a byte by byte and put in the buffer so another thread parse the buffer.
Problem is that in the message i will have a 0x00 byte {null element for fifo buffer} so i can`t enter that element in the fifo.How to solve that problem.
I use
BlockingQueue<Byte> queue = new ArrayBlockingQueue<>(1000);
For entering byte in the fifo:
public void serialEvent(jssc.SerialPortEvent event) {
if (event.isRXCHAR()) {//If data is available
try {
buffer = serialPort.readBytes();
System.out.println(bytesToHex(buffer) + " event");
for (int i = 0; i < buffer.length; i++) {
queue.add(buffer[i]);
}
} catch (SerialPortException ex) {
System.out.println(ex);
}
And for poll the element
byte stx = queue.poll();
How to enter null elements because message will have a null bytes?
the code for parsing(thread for parsing):
public void run() {
while (true) {
while (!queue.isEmpty()) {
System.out.println(queue.size() + " 1 proverka");
sb = new StringBuilder();
byte stx = queue.poll();
System.out.println(byteToHex(stx) + " parser");
if (stx == 0x02) {
sb.append(String.format("%02X ", stx));
System.out.println(queue.size() + " 2 proverka");
for (int i = 0; i < 3; i++) {
System.out.println(queue.size() + " 3 proverka " + i + " krug");
len[i] = queue.poll();
System.out.println(byteToHex(len[i]) + " parser");
output:
02 event
0000 event
01 event
4 1 proverka
77 event
2 parser
03 event
75 event
6 2 proverka
6 3 proverka 0 krug
Exception in thread "Thread-1" java.lang.NullPointerException
at es.moduli.ingenico_card_reader.Ingenico_Card_Reader$SerialParser.run(Ingenico_Card_Reader.java:127)
edited with my custom byte class
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package es.moduli.ingenico_card_reader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import jssc.SerialPort;
import jssc.SerialPortEventListener;
import jssc.SerialPortException;
/**
*
* #author GiGo
*/
public class Ingenico_Card_Reader {
static SerialPort serialPort;
static BlockingQueue<CustomByte> queue = new ArrayBlockingQueue<>(1000);
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public void open() {
serialPort = new SerialPort("COM4");
try {
serialPort.openPort();//Open port
serialPort.setParams(9600, 8, 1, 0);//Set params
int mask = SerialPort.MASK_RXCHAR;//Prepare mask
serialPort.setEventsMask(mask);//Set mask
serialPort.addEventListener(new SerialPortEvent());//Add SerialPortEventListener
} catch (SerialPortException ex) {
System.out.println(ex);
}
SerialParser parser = new SerialParser();
parser.start();
}
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
public static String byteToHex(byte b) {
int i = b & 0xFF;
return Integer.toHexString(i);
}
public static int byteToIntBE(byte[] b) {
ByteBuffer bb = ByteBuffer.wrap(b);
bb.order(ByteOrder.BIG_ENDIAN);
int result = bb.getInt();
return result;
}
public static void sleep(long i) {
try {
Thread.sleep(i);
} catch (Exception e) {
}
}
public static String hexString(byte[] b) {
StringBuilder d = new StringBuilder(b.length * 2);
for (int i = 0; i < b.length; i++) {
char hi = Character.forDigit(b[i] >> 4 & 0xF, 16);
char lo = Character.forDigit(b[i] & 0xF, 16);
d.append(Character.toUpperCase(hi));
d.append(Character.toUpperCase(lo));
}
return d.toString();
}
public static class SerialPortEvent implements SerialPortEventListener {
byte buffer[];
#Override
public void serialEvent(jssc.SerialPortEvent event) {
if (event.isRXCHAR()) {//If data is available
//if (event.getEventValue() == 1) {//Check bytes count in the input buffer
//Read data, if 1 byte available
try {
buffer = serialPort.readBytes();
System.out.println(bytesToHex(buffer) + " event");
for (int i = 0; i < buffer.length; i++) {
queue.add(new CustomByte(buffer[i], true));
}
} catch (SerialPortException ex) {
System.out.println(ex);
}
}
// }
}
}
public static class SerialParser extends Thread {
StringBuilder sb = new StringBuilder();
Byte len[];
int len_mess = 0;
Byte mess[];
#Override
public void run() {
while (true) {
while (!queue.isEmpty()) {
System.out.println(queue.size() + " 1 proverka");
sb = new StringBuilder();
byte stx = queue.poll().getBufferByte();
System.out.println(byteToHex(stx) + " parser");
if (stx == 0x02) {
sb.append(String.format("%02X ", stx));
System.out.println(queue.size() + " 2 proverka");
for (int i = 0; i < 3; i++) {
System.out.println(queue.size() + " 3 proverka " + i + " krug");
len[i] = queue.poll().getBufferByte();
System.out.println(byteToHex(len[i]) + " parser");
//sb.append(String.format("%02X ", len[i]));
}
len_mess = (256 * 256 * len[0]) + (256 * len[1]) + (len[2]);
for (int i = 0; i < len_mess; i++) {
mess[i] = queue.poll().getBufferByte();
System.out.println(byteToHex(mess[i]) + "parser");
sb.append(String.format("%02X ", mess[i]));
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
System.out.println(ex.getMessage());
}
}
byte etx = queue.poll().getBufferByte();
System.out.println(byteToHex(etx) + "parser");
if (etx == 0x03) {
sb.append(String.format("%02X ", etx));
System.out.println(sb.toString());
} else {
System.out.println("NOT ETX");
}
} else {
System.out.println("NOT STX");
}
System.out.println(sb.toString());
}
}
}
}
}
THE SAME ERROR
02 event
0000017703 event
6 1 proverka
2 parser
75 event
6 2 proverka
6 3 proverka 0 krug
Exception in thread "Thread-1" java.lang.NullPointerException
at es.moduli.ingenico_card_reader.Ingenico_Card_Reader$SerialParser.run(Ingenico_Card_Reader.java:129)
A BlockingQueue<Byte> can have (byte) 0x00 values however it is very inefficient way to deal with a stream of bytes. This will create around 16 bytes of garbage for each actual byte sent.
Another approach is to use a Pipe stream which is designed for this sort of task.
PipedOutputStream out = new PipedOutputStream();
PipedInputStream in = new PipedInputStream(out);
In the thread reading from the RS232, you can write the bytes you read to the out and in another thread you can process this by reading from in.
This approach doesn't create any garbage on a per byte basis.
Your can wrap the Byte class with your custom Byte class:
public class CustomByte {
private Byte bufferByte;
private boolean isNull;
// Add constructor, getters/setters as required
}
and change the queue type to use this type.
Then when you encounter a null byte just add a CustomByte object with the flag on:
queue.add(new CustomByte(buffer[i], true))
then when polling CustomByte objects you can query the isNull flag
I surfed a lot of questions on the board, about tcp sockets, big-endian and little-endian format but to me nothing apllies to my case.
And I'm sorry for my bad English, I'm working on it :)
I'm loosing my mind on an unexpected behaviour in a simple client-server configuration. Here's the scenario:
Server (C++) <--- TCP socket ---> Client(Java).
Here's the client code:
package NetServ.apps.bigServer.NSLPClient;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.ArrayList;
public class Communicator {
private Socket sock;
private final int port = 6666;
private final String address="127.0.0.1";
private DataOutputStream out;
private DataInputStream in;
public Communicator(){
System.out.println("Creating communicator. Trying to bind to the tcp socket");
try {
sock = new Socket(address, port);
out=new DataOutputStream(sock.getOutputStream());
in=new DataInputStream(sock.getInputStream());
} catch (UnknownHostException e) {
System.out.println("Unable to resolv host");
e.printStackTrace();
} catch (IOException e) {
System.out.println("Generic I/O exception");
e.printStackTrace();
}
System.out.println("Communicator created");
}
public void sendRequest(Request req) throws IOException{
int cmd=0;
if(req.getCmd().equals(CommandType.tg_setup_message))
cmd=0;
if(req.getCmd().equals(CommandType.tg_remove_message))
cmd=1;
if(req.getCmd().equals(CommandType.tg_trigger_message))
cmd=2;
if(req.getCmd().equals(CommandType.tg_probe_message))
cmd=3;
byte[] buff;
Charset charset = Charset.forName("ISO-8859-1");
out.writeInt(cmd);
//out.writeUTF(req.getDstAddr().toString().substring(1));
buff = req.getDstAddr().toString().substring(1).getBytes(charset);
out.writeShort((short)buff.length);
out.write(buff, 0, buff.length);
out.writeInt(req.getProtocol());
out.writeInt(req.getSecure());
//out.writeUTF(req.getDataId());
buff = req.getDataId().getBytes(charset);
out.writeShort((short)buff.length);
out.write(buff, 0, buff.length);
//out.writeUTF(req.getUser());
buff = req.getUser().getBytes(charset);
out.writeShort((short)buff.length);
out.write(buff, 0, buff.length);
out.flush();
out.writeInt(req.getOffpath_type());
if(req.getOffpath_type()!=-1){
out.writeInt(req.getMetric_type());
String tmp = "" + req.getMetric();
//out.writeUTF(tmp);
buff = tmp.getBytes(charset);
out.writeShort((short)buff.length);
out.write(buff, 0, buff.length);
}
switch (req.getCmd()){
case tg_setup_message:
out.writeUTF(req.getUrl());
out.writeInt(req.getLifetime());
out.writeUTF(req.getParameters().toString());
break;
case tg_remove_message:
//TODO
break;
case tg_trigger_message:
//TODO
break;
case tg_probe_message:
for (Short s : req.getProbes()){
//System.out.println("Writing probe code " + s.shortValue());
out.writeShort(s.shortValue());
}
break;
}
if(req.getSignature()!=null){
out.writeInt(1);
out.writeUTF(req.getSignature());
}else{
out.writeInt(0);
}
if(req.getDep()!=null){
out.writeInt(1);
out.writeUTF(req.getDep());
}else{
out.writeInt(0);
}
if(req.getNotif()!=null){
out.writeInt(1);
out.writeUTF(req.getNotif());
}else{
out.writeInt(0);
}
if(req.getNode()!=null){
out.writeInt(1);
out.writeUTF(req.getNode());
}else{
out.writeInt(0);
}
out.flush();
//out.close();
System.out.println("request sent");
}
public ArrayList<String> rcvProbeResponse() throws IOException, SocketException{
ArrayList<String> response= new ArrayList<String>();
System.out.println("Waiting for response...");
boolean timeout=false;
int responseCode=-1;
responseCode=in.readInt();
//responseCode = in.readInt();
//System.out.println("Response code "+responseCode);
if(responseCode==1){ //response is ready! !
System.out.println("Response arriving from NSLP (code 1 )");
int responseCmdCode = in.readInt();
if(responseCmdCode!=2)
return null;
//System.out.println("Response Command Code " + responseCmdCode );
int probeSize = in.readInt();
//System.out.println("Number of probes " + probeSize);
for(int i=0; i<probeSize; i++){
//System.out.println("i: "+i);
String out = in.readUTF();
response.add(out);
}
}
in.close();
if(timeout)
return null;
else
return response;
}
}
Nothing special about that: the protocol between the entities is simply an exchange of integers, shorts and strings, that triggers the server to execute some signaling tasks (the server is the daemon of a signaling protocol).
On the other side the server is legacy code that I modified to comunicate with java.
Here's the relevant code:
[...]
// Set the current socket
communicator->setSocket(sockfd);
// FSM data structure
NetservNslpFsmData * data = new NetservNslpFsmData();
//give the address list of this node to all FSMs created by the client
data->nodeAddressList = &(param.addresses);
// Read from socket the parameters and use them
int ret;
NetservNslpCommunicator::command cmd;
ret = communicator->recvCommandFromJava(&cmd);
if (ret <= 0) {
logSocketError(sockfd, "Command");
// free up the memory allocated
delete data;
return;
}
switch(cmd){
case NetservNslpCommunicator::tg_setup_message:
DLog(param.name, "cmd set: setup");
break;
case NetservNslpCommunicator::tg_remove_message:
DLog(param.name, "cmd set: remove");
break;
case NetservNslpCommunicator::tg_probe_message:
DLog(param.name, "cmd set: probe");
break;
case NetservNslpCommunicator::tg_trigger_message:
DLog(param.name, "cmd set: trigger");
break;
}
ret = communicator->recvIPFromJava(&(data->destAddr));
DLog(param.name, "Dst Address set: "<< data->destAddr.get_ip_str());
if (ret <= 0) {
logSocketError(sockfd, "Destination IP");
// free up the memory allocated
delete data;
return;
}
[...]
int reliable = communicator->recvIntFromJava();
data->reliability = (reliable == NetservNslpCommunicator::TCP);
DLog(param.name, "Reliability set : "<< data->reliability);
int secure = communicator->recvIntFromJava();
data->security = (secure == NetservNslpCommunicator::TCP);
DLog(param.name, "Security set : "<< data->security);
data->dataId = communicator->recvStringFromJava();
DLog(param.name, "DataId : "<< data->dataId);
if (data->dataId == NULL) {
logSocketError(sockfd, "dataId");
// free up the memory allocated
delete data;
return;
}
data->user = communicator->recvStringFromJava();
DLog(param.name, "User : "<< data->user);
if (data->user == NULL) {
logSocketError(sockfd, "user");
// free up the memory allocated
delete data;
return;
}
//Receiving OffPath parameters
data->offpath_type=communicator->recvIntFromJava();
DLog(param.name, "OffType : "<< data->offpath_type);
if(data->offpath_type != -1){
data->metric_type=communicator->recvIntFromJava();
DLog(param.name, "MetricType : "<< data->metric_type);
if(data->metric_type>3|| data->metric_type<1){
logSocketError(sockfd, "metric type");
// free up the memory allocated
delete data;
return;
}
char * tmpStr = communicator->recvStringFromJava();
if (tmpStr == NULL) {
logSocketError(sockfd, "metric");
// free up the memory allocated
delete data;
return;
}
data->metric = tmpStr;
DLog(param.name, "MetricValue : "<< data->metric);
DLog(param.name, "MetricLength : "<< data->metric.length());
}
// check if socket is still alive or some errors occured
if (!communicator->isAlive(sockfd)) {
logSocketError(sockfd, "Socket not alive!");
// free up the memory allocated
delete data;
return;
}
DLog(param.name,"Reading command-specific configuration");
switch(cmd)
{
case NetservNslpCommunicator::tg_setup_message:
data->urlList.push_back(communicator->recvString());
//check if the service data is exchanged together with signaling messages
if (data->urlList.front() != NULL && (strncmp(data->urlList.front(), "file://", 7) == 0))
data->data_included = true;
data->lifetime = communicator->recvIntFromJava();
data->setupParams = communicator->recvStringFromJava();
break;
case NetservNslpCommunicator::tg_remove_message:
break;
case NetservNslpCommunicator::tg_probe_message:
{
DLog(param.name, "Reading probe codes list.");
short probe = 0;
do {
probe = communicator->recvShortFromJava();
DLog(param.name,"Probe Code " << probe);
data->probes.push_back(probe);
} while (probe != 0);
data->probes.pop_back(); //delete the last 0
if (data->probes.empty()) {
logSocketError(sockfd, "Probe list is empty!");
return;
}
break;
}
case NetservNslpCommunicator::tg_trigger_message:
data->triggerType = communicator->recvInt();
switch (data->triggerType){
case NETSERV_MESSAGETYPE_SETUP:
data->urlList.push_back(communicator->recvString());
data->lifetime = communicator->recvInt();
data->setupParams = communicator->recvString();
break;
case NETSERV_MESSAGETYPE_REMOVE:
break;
case NETSERV_MESSAGETYPE_PROBE:
{
short probe = 0;
do {
probe = communicator->recvShortFromJava();
data->probes.push_back(probe);
} while (probe != 0);
data->probes.pop_back(); //delete the last 0
break;
}
default:
ERRLog(param.name, "Trigger type not supported");
closeSocket(sockfd);
return;
}
break;
default:
logSocketError(sockfd, "Trigger type not supported!");
return;
}
DLog(param.name,"Reading optional parameters.");
// Optional parameters passing
bool addParam = 0;
addParam = communicator->recvIntFromJava();
if (addParam) {
data->signature = communicator->recvStringFromJava();
if (data->signature == NULL) {
logSocketError(sockfd, "signature");
// free up the memory allocated
delete data;
return;
}
DLog(param.name, "Message signature : "<< data->signature);
}
addParam = communicator->recvIntFromJava();
if (addParam) {
data->depList.push_back(communicator->recvStringFromJava());
if (data->depList.front() == NULL) {
logSocketError(sockfd, "dependency list");
// free up the memory allocated
delete data;
return;
}
DLog(param.name, "Message dependency list : "<< data->depList.front());
}
addParam = communicator->recvIntFromJava();
if (addParam) {
data->notification = communicator->recvStringFromJava();
if (data->notification == NULL) {
logSocketError(sockfd, "notification");
// free up the memory allocated
delete data;
return;
}
DLog(param.name, "Message notification : "<< data->notification);
}
addParam = communicator->recvIntFromJava();
if (addParam) {
data->node = communicator->recvStringFromJava();
if (data->node == NULL) {
logSocketError(sockfd, "node");
// free up the memory allocated
delete data;
return;
}
DLog(param.name, "Node destination : "<< data->node);
}
[...]
The communicator wraps the socket and uses standard calls to write and read types:
int NetservNslpCommunicator::recvCommandFromJava(NetservNslpCommunicator::command * cmd){
int code = recvIntFromJava();
cout<<"received int "<<code<<endl;
if(code>=0){
switch(code){
case 0:
*cmd=NetservNslpCommunicator::tg_setup_message;
break;
case 1:
*cmd=NetservNslpCommunicator::tg_remove_message;
break;
case 2:
*cmd=NetservNslpCommunicator::tg_trigger_message;
break;
case 3:
*cmd=NetservNslpCommunicator::tg_probe_message;
break;
}
}
return code;
}
int NetservNslpCommunicator::recvIPFromJava(protlib::hostaddress * addr){
cout<<"receiving an IP"<<endl;
char* str = recvStringFromJava();
cout<<"String received "<< str << endl;
addr->set_ipv4(str);
return 1;
}
char * NetservNslpCommunicator::recvStringFromJava(){
short length = recvShortFromJava();
cout<< "receiving a string..."<<endl<<"String length "<<length<<endl;
char * string = new char[length];
int r = 0;
int orLength=length;
while(length)
{
int r = recv(sock, string, length, 0);
if(r <= 0)
break; // Socket closed or an error occurred
length -= r;
}
string[orLength]='\0';
if(orLength==0)
return NULL;
else
return string;
}
int NetservNslpCommunicator::recvIntFromJava(){
int x = 0;
recvBuffer(sock, &x, 4);
return x;
}
short NetservNslpCommunicator::recvShortFromJava()
{
short x = 0;
recvBuffer(sock, &x, 2);
return x;
}
int NetservNslpCommunicator::recvBuffer(int sock, void * buf, size_t size)
{
int counter = 0;
// Create a pollfd struct for use in the mainloop
struct pollfd poll_fd;
poll_fd.fd = sock;
poll_fd.events = POLLIN | POLLPRI;
poll_fd.revents = 0;
int r;
while (size && !stop)
{
/* Non-blocking behavior */
// wait on number_poll_sockets for the events specified above for sleep_time (in ms)
int poll_status = poll(&poll_fd, 1/*Number of poll socket*/, 100);
if (poll_fd.revents & POLLERR) // Error condition
{
if (errno != EINTR)
cout << "NetservNslpCommunicator : " << "Poll caused error " << strerror(errno) << " - indicated by revents" << endl;
else
cout << "NetservNslpCommunicator : " << "poll(): " << strerror(errno) << endl;
}
//ignore hangups when reading from a socket
if (poll_fd.revents & POLLHUP) // Hung up
{
cout << "NetservNslpCommunicator : " << "Poll hung up" << endl;
// return -1;
}
if (poll_fd.revents & POLLNVAL) // Invalid request: fd not open
{
cout << "NetservNslpCommunicator : " << "Poll Invalid request: fd not open" << endl;
return -1;
}
switch (poll_status)
{
case -1:
if (errno != EINTR)
cout << "NetservNslpCommunicator : " << "Poll status indicates error: " << strerror(errno) << endl;
else
cout << "NetservNslpCommunicator : " << "Poll status: " << str error(errno) << endl;
break;
case 0:
if (isTriggerTimerEnabled){
counter++;
if (counter == triggerTimerValue){
isTriggerTimerEnabled = false;
return -1;
}
}
continue;
break;
default:
r = recv(sock, buf, size, 0);
if (r <= 0)
{
if (r == 0) { // connection closed
r = -1; // return an error if socket closes
cout << "NetservNslpCommunicator : " << "No data received from socket!" << endl;
stop=true;
break;
}
if (r == -1 && errno == EINTR) // received interrupt during recv, continuing
continue;
if (r == -1 && errno != EINTR) // socket error, raise exception
break;
}
if (r != -1)
size -= r;
break;
}
}
counter = 0;
isTriggerTimerEnabled = false;
return r;
}
I ask you to focus only on the tg_probe_message part. The other messages are still to implement.
The strange behaviour is: the first time the client sends a request to the server everything goes well, all values are read perfectly. Hence the server answers sending back some integer and a sequences of strings.
This is a trace (application layer only. One TCP packet per line) of what i capture on the socket:
00 //
00 //
00 //
03 // First integer
00 //
0a // Short representing string length
31:37:32:2e:31:36:2e:33:2e:32 //the string: "172.16.3.2"
00
00
00
01
00
00
00
00
00
1b
4e:65:74:53:65:72:76:2e:61:70:70:73:2e:4c:6f:69:62:46:61:6b:65:5f:30:2e:30:2e:31 //The string "NetServ.apps.LoibFake_0.0.1"
00
03
6a:61:65 //the string "jae"
00
00
00
03
00
00
00
01
00
01
31 //the string "1"
00
02
00
00
00 //
00 //
00 // 4 times
00 //
The server answers:
00:00:00:01 //response code
00:00:00:02 //response type
00:00:00:04 //number of strings to read
00:12 //str length
31:30:2e:31:30:2e:30:2e:35:20:41:43:54:49:56:45:20:31
00:12 //str length
31:30:2e:31:30:2e:30:2e:34:20:41:43:54:49:56:45:20:31
00:12 //str length
31:30:2e:31:30:2e:30:2e:33:20:41:43:54:49:56:45:20:32
00:12 //str length
31:30:2e:31:30:2e:30:2e:36:20:41:43:54:49:56:45:20:32
The second time the client sends a request (the same request) something weird occurs. This is what i captured with tcpdump during the second connection:
00 //
00 //
00 //
03 // First integer
00 //
0a // Short representing string length
31:37:32:2e:31:36:2e:33:2e:32 //the string: "172.16.3.2"
00
00
00
01
00
00
00
00
00:1b:4e:65:74:53:65:72:76:2e:61:70:70:73:2e:4c:6f:69:62:46:61:6b:65:5f:30:2e:30:2e:31:00:03:6a:61:65:00:00:00:03:00:00:00:01:00:01:31:00:02:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
With a little of patience you can recognise that the last packet contains ALL the bits of the request (same bits of the first request).
With some debuggin I can see that the command communicator->recvCommandFromJava(&cmd) returns the number 50331648 (03:00:00:00) instead of 3 (00:00:00:03) and when the command communicator->recvIPFromJava(&(data->destAddr)) is executed, which in turn calls the recvStringFromJava(), which uses the recvShortFromJava(), the short representing the string length 00:0a (10) is swapped into the little-endian 0a:00 (2560). I believe this causes the tcp to put all the data available in the next packet and to spoil the subsequent calls.
As you can see from the code I didn't adopted conversion from host-order to net-order in the server (and that is because it works fine for the first request), but it seems that conversion is required during the second request. The documentation on DataOutputStream specifies that int and short are written in big-endian. The server does not apply conversion.
Hence, in the end, this is the question:
Is it possible that C++ could change the Host-Format during execution?
How could this possibly happen?
What I can do to have predicible behaviour on the byte ordering between java client and c++ server?
Endian-ness has nothin to do with putting the data in the next packet. That's just because it's a byte stream protocol.
You have two separate problems to solve: one with ntohl() and friends, the other by continuing to read until you have all the data you're expecting.
I found a solution to my problem that works and I think is elegant enough.
Because I can't predict the behaviour of the server when it reads primitive types large more than one byte, I use a standard contract mechanism.
Every time a client wants to push commands to the server it sends a known Integer code. The server reads the integer and checks if the value is equal to the predeterminated Integer, than it can read all the values without reordering them. Otherwise it will set a flag and will read all the subsequent values swapping them with the function ntohl() and ntohs().