I am sending a message via a DatagramChannel as follows:
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
String request_message = "request";
buf.put(request_message.getBytes());
buf.flip();
int bytesSent = udpserver.send(buf, successor_port_1); // udpserver is a DatagramChannel
I then read the message on the server:
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
String message = new String(buf.array(), Charset.forName("UTF-8"));
System.out.println(message); //print message for testing purposes
if (message == "request"){
//code here does not get executed
}
The problem is, my code doesn't enter the "if" statement even though message = "request" which also seems to be confirmed by my print statement.
Any help would be appreciated!
The reason for this is that Strings in java need to be compared with .equals, so your test should be:
if (message.equals("request")){
This is because, in Java, == tests whether two object are the same instance (it tests reference equality - do both the references point to the same memory) rather than equal.
You can carry out a quick test:
System.out.println("request" == new String("request"));
Output:
false
For more information read this SO answer.
Related
I use NIO with reactor pattern to connect a server to a client. My codes are as follows:
Server side codes, in the block of if(selectionKey.isWritable){} :
public void isWritable(SelectionKey selectionKey) throws Exception {
SocketChannel socketChannel =
(SocketChannel) selectionKey.channel();
Integer myInteger = (Integer) selectionKey.attachment();
if (myInteger == null){
int myJob = jobFacade.isAnyJob(socketChannel, 100 /*deadline*/);
if (myJob > 0){
ByteBuffer inputBuffer = ByteBuffer.wrap("available\n".getBytes("UTF-8"));
socketChannel.write(inputBuffer);
myInteger = myJob;
socketChannel.register(
selector, SelectionKey.OP_WRITE, myInteger);
}else if (myJob == -1){
ByteBuffer inputBuffer = ByteBuffer.wrap("unavailable\n".getBytes("UTF-8"));
socketChannel.write(inputBuffer);
socketChannel.close();
UnsupportedOperationException un = new UnsupportedOperationException();
throw un;
}else if (myJob == -2){
ByteBuffer inputBuffer = ByteBuffer.wrap("pending\n".getBytes("UTF-8"));
inputBuffer.flip();
socketChannel.write(inputBuffer);
myInteger = null;
socketChannel.register(
selector, SelectionKey.OP_WRITE, myInteger);
}
// is there any new job to do?
}else{
int myInt = myInteger.intValue();
if ( myInt > 0 ){
long startRange = jobFacade.findByID(myInt);
sendTextFile(startRange, Integer.parseInt(properties.getProperty("workUnit")),
properties.getProperty("textPath"), socketChannel);
myInteger = -3;
socketChannel.register(
selector, SelectionKey.OP_WRITE, myInteger);
}else if (myInt == -3){
sendAlgorithmFile(socketChannel, properties.getProperty("algorithmPath"));
myInteger = -4;
socketChannel.register(
selector, SelectionKey.OP_WRITE, myInteger);
// send algorithm file
}else if (myInt == -4){
int isOK = jobFacade.isAccepted(socketChannel.socket().getInetAddress().toString(),
Long.parseLong(properties.getProperty("deadline")));
if(isOK == -1){
ByteBuffer inputBuffer = ByteBuffer.wrap("notaccepted\n".getBytes("UTF-8"));
socketChannel.write(inputBuffer);
myInteger = null;
socketChannel.register(
selector, SelectionKey.OP_WRITE, myInteger);
}else {
ByteBuffer inputBuffer = ByteBuffer.wrap("accepted\n".getBytes("UTF-8"));
socketChannel.write(inputBuffer);
myInteger = isOK;
socketChannel.register(
selector, SelectionKey.OP_READ, myInteger);
}
// send "accepted" or "not accepted"
}
}
}
It is no need to know what my methods in each block do except that these methods generate a number with this order at first. 1)myInteger=null, 2) myInteger > 0, 3) myInteger = -3, 4) myInteger = -4
In this order, OP-WRITE will register consecutively for four times. And this part is so important. So lets see my Client side code and then I will tell you my problem:
BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
sentence = inFromServer.readLine();
System.out.println("Response from Server : " + sentence);
if (sentence.equals("available")){
BufferedReader inFromServer1 = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
while ((sentence = inFromServer1.readLine()) != null) {
myJob = myJob + sentence ;
}
inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String acception = inFromServer.readLine();
if (acception.equals("accepted")){
File file = new File("account.json");
byte[] bytes = new byte[2048];
InputStream inputStream = new FileInputStream(file);
OutputStream outputStream = clientSocket.getOutputStream();
int count;
try {
while ((count = inputStream.read(bytes)) > 0){
outputStream.write(bytes, 0, count);
}
outputStream.close();
inputStream.close();
}catch (IOException io){}
continue;
}else if (acception.equals("notaccepted")){
continue;
}
Now, my problem is that when I run my server and then my client, my server will run without waiting for my client to get input stream. First, the client get "available" but when the second getInputStream will be reached in client, the server paced all the phase of OP-WRITE registering and wait for client to get streams of data (As I defined in my code).
Actually, my server do its job well. It will pass all the stages in required order. But the problem is that sending and receiving data is not synchronous.
I do not know what my problem is. But I guess when I register OP-WRITE consecutively, it means that my server did not send all bytes of data, so just the first getInputStream will get the data.
On the other hand, I need this order to run my program. So, Is there any Idea?
I find out my problem. There is no problem with my code. OP_WRITE can be registered any time with any order. The most important thing is to write to buffer and read from socket correctly.
Actually, when I send something for the second time to my client, I did not clear the buffer. In this case I found it, and correct it.
But when I send some characters to my client and then want to send a file, because in my client side I have a loop to get all characters, the content if the file is gotten by the same loop.
The question here is that how I can make them separate?
I will help you clarify the problem before thinking about patterns:
You have one thread/process that passes a message asking another thread/ process to act upon the message.
The receiver needs to read the message and maybe start some child threads of its own to perform that work because it can receive other requests.
It would be nice to tell the sender that an acknowledgment that the request was received.
It seems necessary that the message passing is protected. Because if another requests comes in while you are reading you could end up processing garbage.
You can configure nio to have several readers and just one writer, just read one portion of a buffer, etc. Check the how-tos, api docs. It is plenty powerful
exactly after sending a message
There is no such thing as a message in TCP. It is a byte stream. Two writes at the sender are very likely to by read by one read at the receiver. If you want messages you have to implement them yourself, with count words, terminators, STX/ETX, XML, etc.
I have a Java Modbus/TCP application which is reading constantly data from a device.
This is working normal 99.9% of times, but after a weekend working, it could enter in a strange mode, in which for some seconds, I am getting fake values of my Read Multiple Holding Registers functions.
I have checked by using Modscan application, and fake values appear on the client site, which means that the server device is answering properly.
The answer that I can get is an Byte array filled of 0's, 1's and some times other random values.
Here is my Modbus/TCP answer reading:
private byte[] getModbusReply(){
byte[] reply = null;
int transactionId;
int protocol;
int tcpLen;
int id;
int replyCode;
int mbLen = 1;
try{
InputStream is = socket.getInputStream();
transactionId = (is.read()<<8)+is.read();
protocol = (is.read()<<8)+is.read();
tcpLen = (is.read()<<8)+is.read();
id = is.read();
replyCode = is.read();
if(replyCode>0x3F){
mbLen = 1;
}else{
switch(replyCode){
case 0x03:
case 0x04:
mbLen = is.read();
break;
case 0x10:
case 0x06:
mbLen = 4;
break;
default://unsupported Modbus Methods
return null;
}
}
reply = new byte[mbLen+1];
reply[0] = (byte)replyCode;
for(int i=1;i<reply.length;i++){
int res=is.read();
if(res<0){
//Modbus Stream Reading is returning -1
return null;
}
reply[i] = (byte)res;
}
}catch(Exception e){
e.printStackTrace();
return null;
}
return reply;
}
Return null is processed outside the function as a wrong exception.
I had add 2 protections:
read() method returns -1 after EOF, so I add:
int res=is.read();
if(res<0){
//Modbus Stream Reading is returning -1
return null;
}
Return null for unsupported Modbus/TCP methods:
default:///unsupported Modbus Methods
return null;
Maybe I am missing something more on stream reading to which I have no protection.
It isn't sufficient to just return null when something goes wrong or you get something you don't understand. That way you're just leaving the connection open and in an unsynchronized state where you have no reason to believe the next byte is the beginning of a new message. So, from then on you will read unintelligible junk. You need to close the connection and reopen it. Or, implement the rest of the protocol.
You aren't checking for end of stream at nine out of the ten points you read from it.
You should also have a good look at DataInputStream. It already does everything that you're already doing the hard way. Note particularly readShort() and readFully().
Sever code
if(success){
out.write("true".getBytes().length);
out.write("true".getBytes());
out.flush();
}
else{
out.write("false".getBytes().length);
out.write("false".getBytes());
out.flush();
}
Client Code
int size = inputStream.read();
byte[] buf = new byte[size];
inputStream.read(buf);
ns = new String(buf);
Boolean.valueOf(ns);
Although the sever send the result client read it wrong. What is the problem in here? how can i solve it. As example sever send value true but client receive it as false
You need to step thread what you are doing exactly. Obviously the simplest way to sent a boolean is as a single byte like this.
out.write(success ? 1 : 0);
and to read this you would do
boolean success = in.read() != 0;
However, if you need to send a string, I would check what string you are reading and what the correct length is, because there is any number of reasons a binary protocol can fail, e.g. because the previous thing you read/wrote was incorrect.
Server and Client are probably using different charsets.
Use an explicit one (and the same) in both sides.
see http://docs.oracle.com/javase/6/docs/api/java/lang/String.html
public byte[] getBytes(String charsetName)
throws UnsupportedEncodingException
and
public String(byte[] bytes,
String charsetName)
throws UnsupportedEncodingException
I've written a Java RDT client/server program that exchanges datagram packets to correct packet corruption and loss. The final bit of code I added requires that I compare each packet's source address to the address of the original packet.
//global
InetAddress originalSender;
//C'tor
originalSender = null;
...
//primary method
public byte[] rdt_receive() throws IOException
{
while (true) {
DatagramPacket recPacket = new DatagramPacket(new byte [PACKET_SIZE], PACKET_SIZE);
dgSock.receive(recPacket);
if (originalSender == null) {
System.out.println("Address is set!\n");
originalSender = recPacket.getAddress();
}
if( originalSender != recPacket.getAddress() ) {
System.out.println("Wrong sender!");
sendAck((short) (expectedSequence == 0 ? 1 : 0), recPacket.getSocketAddress());
continue;
}
// continue method...
"Address is set" is never printed so if(originalSender == null)never returns true, even for the first packet. I've also tried SocketAddress and .getSocketAddress() to no avail. Help is much appreciated.
EDIT:
"Wrong sender" is printed in an infinite loop. I'm running both client and server on the same computer and the first received packet is successfully written to a file. The program works before adding the block of if statements, correctly sending a text file and closing both client and server.
Are InetAddress/SocketAddress automatically assigned by the OS if set to null?
You can't compare InetAddresses with ==. You need to call equals().
But you don't need this. Just connect() the socket to the original sender when you get the first packet. Then UDP will do the filtering for you.
I am working on a TFTP server application. I managed to process a successful file transfer from server to client however the other way around is bugged.
Client instead of transmitting the entire file simply terminated whit compiler returning no errors. Debugger shows IOBE exception on the marked code referring that the array is out of range.
The whole transfer process goes like so:
Client transmits a file name and requested operation WRQ - Write Request
Server received the packet and determines the operation if WRQ is gives the new file appropriate name.
Server now starts executing receiveData() until it gets a packet < 512 indicationg EOT
Client keeps transferring data it read from the file.
Key code:
Client:
private void sendWRQ() throws Exception
{
String rrq = "WRQ-" + data;
outgoingData = rrq.getBytes();
DatagramPacket output = new DatagramPacket(outgoingData, outgoingData.length, serverAddress, serverPort);
clientSocket.send(output);
//Thread.sleep(50);
sendData();
}
byte outgoingData = new byte[512];
private void sendData() throws Exception
{
DatagramPacket dataTransfer = new DatagramPacket(outgoingData, outgoingData.length, serverAddress, serverPort);
InputStream fis = new FileInputStream(new File(data));
int x;
while((x = fis.read(outgoingData,0,512)) != -1) // << Debugged gives IOBE
{
dataTransfer.setLength(x);
clientSocket.send(dataTransfer);
Thread.sleep(5);
}
fis.close();
}
Server:
private void listen() throws Exception
{
DatagramPacket incTransfer = new DatagramPacket(incomingData, incomingData.length);
serverSocket.receive(incTransfer);
clientAddress = incTransfer.getAddress();
clientPort = incTransfer.getPort();
String output = new String(incTransfer.getData());
if(output.substring(0, 3).equals("RRQ"))
{
File test = new File(output.substring(4));
responseData = output.substring(4);
if(test.exists())
{
sendResponse("Y");
} else {
sendResponse("N");
}
} else if (output.substring(0, 3).equals("WRQ"))
{
File test = new File(output.substring(4));
if(test.exists())
{
Calendar cal = Calendar.getInstance();
SimpleDateFormat prefix = new SimpleDateFormat(date_format);
String date = prefix.format(cal.getTime()).toString();
responseData = date + output.substring(4);
receiveData();
} else {
responseData = output.substring(4);
receiveData();
}
}
}
private void receiveData() throws Exception
{
DatagramPacket receiveData = new DatagramPacket(incomingData, incomingData.length);
OutputStream fos = new FileOutputStream(new File(responseData));
while(true)
{
serverSocket.receive(receiveData);
if(receiveData.getLength() == 512)
{
fos.write(receiveData.getData());
} else {
fos.write(receiveData.getData(), receiveData.getOffset(), receiveData.getLength());
break;
}
}
fos.close();
}
The only way that can happen is if the offset or length parameters violate the constraints specified for InputStream.read(byte[], int, int); in this case probably the buffer isn't 512 bytes long. There's no need to specify the 2nd nd third parameters in this case, just omit them, then it becomes read(buffer, 0, buffer.length) internally, which can't be wrong.
Okay, the way this is coded, the 'outgoingData' field is:
1) Initialized to a length of 512
2) Then, in sendWRQ(), 'outgoingData' is re-initialized to whatever rrq.getBytes() sends back.
3) Then, in sendData(), 'outgoingData' is used as the intermediate buffer to read data from file and put it in the dataTransfer object.
However, since 'outgoingData' is re-initialized in step #2, the assumption in step #3 that 'outgoingData' is still 512 bytes in length is false.
So while EJP was correct in saying that using read(outgoingData, 0, outgoingData.length()) will work, there are some architecture issues that if you address, you'll clean up a lot of potential errors.
For instance:
WIth the code provided, there is seemingly no reason to have outgoingData declared at the class level and shared among two functions. Depending on the rest of the app, this could end up being a Threading issue.
Perhaps byte[] buffer = rrq.getBytes(); in sendWRQ() and byte[] buffer = new byte[1024]; in sendData().
Also, the 'data' parameter is at the class level.... for what reason? Might be better able to be controlled if its a passed in parameter.
Lastly, I've had good luck using the do{} while() loop in network situations. Ensures that the send() gets at least one chance to send the data AND it keeps the code a bit more readable.