I have a socketserver set up with a remote client, and it is functional. Upon opening the client and logging in, I noticed that sometimes, there is an error that seems to be due to the client reading an int when it shouldn't be.
Upon logging on, the server sends a series of messages/packets to the client, and these are anything from string messages to information used to load variables on the client's side.
Occasionally, while logging in, an error gets thrown showing that the client has read a packet of size 0 or a very large size. Upon converting the large-sized number into ascii I once found that it was a bit of a string "sk." (I located this string in my code so it's not entirely random).
Looking at my code, I'm not sure why this is happening. Is it possible that the client is reading an int at the wrong time? If so, how can I fix this?
InetAddress address = InetAddress.getByName(host);
connection = new Socket(address, port);
in = new DataInputStream(connection.getInputStream());
out = new DataOutputStream(connection.getOutputStream());
String process;
System.out.println("Connecting to server on "+ host + " port " + port +" at " + timestamp);
process = "Connection: "+host + ","+port+","+timestamp + ". Version: "+version;
write(0, process);
out.flush();
while (true) {
int len = in.readInt();
if (len < 2 || len > 2000) {
throw new Exception("Invalid Packet, length: "+len+".");
}
byte[] data = new byte[len];
in.readFully(data);
for (Byte b : data) {
System.out.printf("0x%02X ",b);
}
try {
reader.handlePackets(data);
} catch (Exception e) {
e.printStackTrace();
//connection.close();
//System.exit(0);
//System.out.println("Exiting");
}
}
//Here is code for my write function (Server sided):
public static void write(Client c, Packet pkt) {
for (Client client : clients) {
if (c.equals(client)) {
try {
out.writeInt(pkt.size());
out.write(pkt.getBytes());
out.flush();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
So looking at the write function, I don't really see how it could be confusing the client and making it read for the size of the packet twice for one packet (at least that's what I think is happening).
If you need more information please ask me.
The client side code looks fine, and the server side code looks fine too.
The most likely issue is that this is some kind of issue with multi-threading and (improper) synchronization. For example, maybe two server-side threads are trying to write a packet to the same client at the same time.
It is also possible that your Packet class has inconsistent implementations of size() and getBytes() ... or that one thread is modifying a Packet objects while a second one is sending it.
Related
I've learned in Java how to stream data over a network connection using ServerSocket and Socket, such as:
Client.java:
Socket socket = new Socket(address, port);
int i;
while ((i = System.in.read()) != -1)
socket.getOutputStream().write(i);
Server.java:
ServerSocket server = new ServerSocket(port);
Socket socket = server.accept();
int i;
while ((i = socket.getInputStream().read()) != -1)
System.out.println(i);
This would simply have Client blocking on System.in.read() at one end, and Server blocking on socket.getInputStream().read() at the other, and the bytes get passed when ENTER is pressed in the Client program.
How would I accomplish something similar within a single program, without using Sockets? For example, if I had Thread A waiting on keyboard input which is then streamed to Thread B which is able to "consume" the bytes at an arbitrary time in the future, just as Server (above) is able to consume bytes from socket.getInputStream() at some arbitrary time?
Is PipedInput/OutputStream the right solution for this, or ByteArrayInput/OutputStream, or something else? Or am I overthinking it?
Yes, you can use PipedInputStream/PipedOutputStream for "streaming" data "locally" in your JVM. You create one PipedInputStream and one PipedOutputStream instance, connect them with the connect() method and start sending/receiving bytes. Check the following example:
PipedInputStream pipedIn = new PipedInputStream();
PipedOutputStream pipedOut = new PipedOutputStream();
pipedIn.connect(pipedOut);
Thread keyboardReadingThread = new Thread() {
#Override
public void run() {
System.out.println("Enter some data:");
Scanner s = new Scanner(System.in);
String line = s.nextLine();
System.out.println("Entered line: "+line);
byte[] bytes = line.getBytes(StandardCharsets.UTF_8);
try {
pipedOut.write(bytes);
pipedOut.flush();
pipedOut.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Keyboard reading thread terminated");
}
};
keyboardReadingThread.start();
Thread streamReadingThread = new Thread() {
#Override
public void run() {
try {
int bytesRead = 0;
byte[] targetBytes = new byte[100];
System.out.println("Read data from the PipedInputStream instance");
while ((bytesRead = pipedIn.read(targetBytes)) != -1) {
System.out.println("read "+bytesRead+" bytes");
String s = new String(targetBytes, 0, bytesRead, StandardCharsets.UTF_8);
System.out.println("Received string: "+s);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Streaming reading thread terminated");
}
};
streamReadingThread.start();
keyboardReadingThread.join();
streamReadingThread.join();
First the two piped stream instances are connected. After that two threads will read from the keyboard and read from the PipedInputStream instance. When you run your application you will get an output similar to this (with Some example input for testing being the keyboard input):
Enter some data:
Read data from the PipedInputStream instance
Some example input for testing
Entered line: Some example input for testing
Keyboard reading thread terminated
read 30 bytes
Received string: Some example input for testing
Streaming reading thread terminated
Also notice that the threads are not synchronized in any way, so the System.out.println() statements might get executed in a different order.
This is mostly an extension of the answer #VGR gave in the comments.
If the entirety of your "Network" exists within the same, single JVM, then you don't need anything like sockets at all - you can just use Objects and methods.
The entire point of Sockets was to allow the JVM to perform actions outside of itself (typically with another JVM somewhere in the outside world).
So unless you are trying to interact with objects outside of your current JVM, it is as simple as this.
public class ClientServerExample
{
public static void main(String[] args)
{
Server server = new Server();
Client client = new Client();
client.sendMessage("Hello Server", server);
}
static class Server
{
String respond(String input)
{
String output = "";
System.out.println("Server received the following message -- {" + input + "}");
//do something
return output;
}
}
static class Client
{
void sendMessage(String message, Server server)
{
System.out.println("Client is about to send the following message to the server -- {" + message + "}");
String response = server.respond(message);
System.out.println("Client received the following response from the server -- {" + response + "}");
//maybe do stuff with the response
}
}
}
Here is the result from running it.
Client is about to send the following message to the server -- {Hello Server}
Server received the following message -- {Hello Server}
Client received the following response from the server -- {}
Note that server doesn't return anything because I didn't do anything in the server. Replace that comment with some code of your own and you will see the results.
EDIT - to better explain a real world example, where a server will respond to requests in FIFO, here is a modified version of the above example.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class ClientServerExample
{
public static void main(String[] args)
{
System.out.println("===========STARTING SYNCHRONOUS COMMUNICATION============");
synchronousCommunication();
System.out.println("===========FINISHED SYNCHRONOUS COMMUNICATION============");
System.out.println("===========STARTING ASYNCHRONOUS COMMUNICATION============");
asynchronousCommunication();
System.out.println("===========FINISHED ASYNCHRONOUS COMMUNICATION============");
}
public static void synchronousCommunication()
{
Server server = new Server();
Client client = new Client();
String response = "";
response = client.sendMessage("Good morning Server!", server).join();
System.out.println("Client received the following response from the server -- {" + response + "}");
response = client.sendMessage("Good evening Server!", server).join();
System.out.println("Client received the following response from the server -- {" + response + "}");
}
public static void asynchronousCommunication()
{
Server server = new Server();
Client client = new Client();
List<CompletableFuture<String>> responses = new ArrayList<>();
responses.add(client.sendMessage("Good morning Server!", server));
responses.add(client.sendMessage("Good evening Server!", server));
for (CompletableFuture<String> eachResponse : responses)
{
System.out.println("Client received the following response from the server -- {" + eachResponse.join() + "}");
}
}
static class Server
{
CompletableFuture<String> respond(final String input)
{
System.out.println("Server received the following message -- {" + input + "}");
return
CompletableFuture.supplyAsync(
() ->
{
try
{
//sleep for 2 seconds, to represent arbitrary delay in receiver processing
Thread.sleep(2000);
return input.contains("morning") ? "Good morning to you too!" : "Good evening to you too!";
}
catch (Exception e)
{
throw new IllegalStateException("What happened?", e);
}
});
}
}
static class Client
{
CompletableFuture<String> sendMessage(String message, Server server)
{
System.out.println("Client is about to send the following message to the server -- {" + message + "}");
return server.respond(message);
}
}
}
Both of these examples are performing a FIFO approach to data processing. They receive the request, calculate a response, and then send back a CompletableFuture, which is basically an Object that contains the response that will arrive once the Server gets around to it, sort of like a Promise in Javascript.
For the synchronous example, we see that a client message is sent, and then processed before the next one is sent. As a result, we have a minor delay between the 2 (about 2 seconds).
For the asynchronous example, we see that both client messages are sent, and their CompletableFutures are put into a batch list, which is converted to normal strings once all requests have been sent.
The synchronous example takes around 10 seconds.
The asynchronous example takes around 5 seconds.
Both of these are different ways of performing FIFO in the way that you described. They both are examples where multiple clients send a request to the server, and then the server finishes them when they get around to it. That 5 seconds delay is meant to represent the idea of "getting around to it". In reality, getting around to it usually means that the server has so much on it's plate that it will take a long time before it has a chance to give a full response.
Let me know if you need another example to better help you understand.
I am building an IM application, from the client side, I write my code like this (I use SocketChannel in blocking mode, history reason, I think it is not related to this problem):
try {
LogUtil.info(TAG, this.label + " tryConnect, attempt = " + (3 - retry));
clientChannel = SocketChannel.open();
clientChannel.configureBlocking(true);
clientChannel.socket().setSoTimeout(100);
clientChannel.socket().setTrafficClass(0x10);
clientChannel.socket().setTcpNoDelay(true);
clientChannel.socket().setPerformancePreferences(3, 3, 1);
clientChannel.socket().connect(address, 10000);
LogUtil.info(TAG, this.label + " socket connected successfully");
break;
} catch (AlreadyConnectedException ace) {
LogUtil.info(TAG, label + " AlreadyConnectedException");
break;
} catch (NotYetConnectedException ace) {
LogUtil.info(TAG, label + " NotYetConnectedException");
break;
} catch (SocketTimeoutException e) {
LogUtil.info(TAG, label + " SocketTimeoutException");
break;
} catch (Exception e) {
clientChannel = null;
throw new SocketConnectionException(label + ", exception = " + ThrowableUtil.stackTraceToString(e));
}
The problem is, when sometimes I shut down the server, the client-side will keeps writing successfully (small chunks of data, less than 50 bytes in total). After about 3 minutes, the client side hits the write fail exception.
Why didn't the client side fail immediately after the server has been closed? How do I fix this problem? Maybe reduce the send buffer to 10 bytes ?
EDIT
Here's how I actually write data:
public void writeXML(ByteBuffer buffer, int retry) {
synchronized (writeLock) {
if (retry < 0) {
throw new SocketConnectionException(label + "Write Exception");
}
tryConnect(false);
try {
int written = 0;
while (buffer.hasRemaining()) {
// I think it should be an exception here after I closed server
written += clientChannel.write(buffer);
}
if (LogUtil.debug) {
LogUtil.info(TAG, "\t successfully written = " + written);
}
} catch (Exception e) {
e.printStackTrace();
tryConnect(true);
writeXML(buffer, --retry);
}
}
}
Because in between you and the peer application there are:
a socket send buffer
a TCP implementation
another TCP implementation
a socket receive buffer.
Normally when you write, the data just gets transferred into your socket send buffer and is sent on the wire asynchronously. So if there is going to be an error sending it you won't find out straight away. You will only find out when the TCP sends have failed enough times over whatever the internal send timeout period is for TCP to decide that an error condition exists. The next write (or read) after that will get the error. It could be some minutes away.
It turns out that the read operation can detect a closed connection(via #EJP's reply, it is different from a lost connection) immediately.
In my reading thread, I have this line:
int read = clientChannel.read(buffer);
, When it returns -1 means the server is shutdown (Shutdown on purpose is different than network unreachable), I guess the write operation needs to fill the TCP send buffer, so there's no way to detect a connection lost quickly.
I am trying to create a monitor of messages between two applications. The idea is this monitor works in the middle of simple client/server application, and log the messages to the standard output. This program must be against of fails of the client/server (disconnections, time out's, etc). In the code, i call the client as "origin" and the server as "destiny". The problem is if the server dies after the first successfully connection, i don't know how do i ask that the destiny is still alive? (See catch exception in the code). I execute the next step's:
1.- I start the client/server application
2.- I start my program (with a Thread)
3.- I send one message from the client to my program, my program delivers this message to the server, the server answers to my program, my program delivers the message back to the client successfully.
4.- Now, i kill and restart the client/server application (without restarting my program)
5.- I repeat step "3" but at this time, when the program reaches the "len_message_from_destiny = streamFromDestiny.read(buffer_msg_destiny);" it produces the catch that i need to code for ask if the server is really alive (that is true in this step)". An attempt to read in this situation produces a "SocketException" with this description: "Software caused connection abort: recv failed java mail".
If i put in the code of catch that i need all instruction again for connect the socket and new streams, doesn't work too.
package interceptorprocess;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
public class GenericInterceptorProcess implements Runnable
{
private final String prefix_log_messages;
public GenericInterceptorProcessConfigurations confs;
//COMMUNICATION'S ORIGIN'S VARIABLES
ServerSocket serverSocketLocal;
Socket socketForLocal;
DataInputStream streamFromOrigin;
DataOutputStream streamToOrigen;
int len_message_from_origen;
byte[] buffer_msg_origin = new byte[4096];
byte[] message_origin = null;
//COMMUNICATION'S DESTINY'S VARIABLES
Socket socketToDestiny;
DataInputStream streamFromDestiny;
DataOutputStream streamToDestiny;
int len_message_from_destiny;
byte[] buffer_msg_destiny = new byte[4096];
byte[] message_destiny;
GenericInterceptorProcess(GenericInterceptorProcessConfigurations confs_p)
{
confs = confs_p;
prefix_log_messages = confs.prefix_log_messages;
}
#Override
public void run()
{
//OCCASIONAL USE
String aux;
try
{
logger("STARTING SERVER --- PORT NUMBER: " + confs.local_port);
//CREATING THE LOCAL SERVER SOCKET
serverSocketLocal = new ServerSocket(confs.local_port);
//THIS LOOP MAINTAINS THE CONNECTIVITY WITH ONE CLIENT AT TIME
while ( true )
{
//CONNECTION TO THE ORIGIN
logger("WAITING FOR A CONNECTION OF A CLIENT...");
socketForLocal = serverSocketLocal.accept();
streamFromOrigin = new DataInputStream(socketForLocal.getInputStream());
streamToOrigen = new DataOutputStream(socketForLocal.getOutputStream());
logger("CONNECTED CLIENT: " + socketForLocal.getRemoteSocketAddress() );
//CONNECTION TO THE DESTINY
try
{
socketToDestiny = new Socket();
socketToDestiny.setSoTimeout(confs.timeout_destiny);
socketToDestiny.connect(new InetSocketAddress(confs.destiny_ip,confs.destiny_port),confs.timeout_connections);
//CREATING THE DESTINY'S STREAMS
streamFromDestiny = new DataInputStream(socketToDestiny.getInputStream());
streamToDestiny = new DataOutputStream(socketToDestiny.getOutputStream());
}
catch(IOException ex)
{
logger("CONNECTION REJECTED BY DESTINY: " + ex.getMessage());
closeOriginStream();
continue;
}
logger("CONNECTED DESTINY: " + socketToDestiny.getRemoteSocketAddress() );
//THIS LOOP MAINTAINS THE MESSAGES'S CHANGES
while ( true )
{
logger("WAITING FOR A MESSAGE..");
//THIS TRY/CATCH EXITS FOR CONNECTION RESETS
try
{
len_message_from_origen = streamFromOrigin.read(buffer_msg_origin);
}
catch(SocketException ex)
{
closeAll();
break;
}
if ( len_message_from_origen < 0 )
{
closeAll();
break;
}
message_origin = new byte[len_message_from_origen];
//SAVE THE ORIGIN'S MESSAGE INTO AN ARRAY WHO HAS THE EXACT SIZE OF THIS MESSAGE
System.arraycopy(buffer_msg_origin, 0, message_origin, 0, len_message_from_origen);
aux = new String(message_origin);
logger("RECEIVED MESSAGE FROM ORIGIN: " + aux);
//MAKE THE CHANGES IN THE INPUT'S MESSAGE
ChangesInMessages.makeChanges(message_origin,confs.type_changes_for_input_messages);
aux = new String(message_origin);
logger("RECEIVED MESSAGE FROM ORIGIN WITH MODIFICATIONS: " + aux);
//I HAD TO PUT THIS BLOCK BECAUSE IF THE DESTINY APPLICATIONS FAILS
//OR NOT ANSWER, THE PROGRAM MUST KEEP LISTENING THE FOLLOWING MESSAGES
try
{
//SENDING MESSAGE TO DESTINY
streamToDestiny.write(message_origin);
//READING THE ANSWER MESSAGE
logger("READING MESSAGE FROM DESTINY...");
//AT THIS POINY, WE MAY HAVE A PROBLEM IF THE SERVER DIES
len_message_from_destiny = streamFromDestiny.read(buffer_msg_destiny);
}
catch (SocketTimeoutException ex)
{
logger("IT DIDN'T COULD RETRIEVE A MESSAGE FROM DESTINY (timeout): " + ex.getMessage());
continue;
}
catch (SocketException ex)
{
boolean flagDestinyStillDead = false;
//IF WE REACH THIS EXCEPTION, IT MINDS THE DESTINY HAS DIED AFTER THE FIRST
//SUSSECCESFULLY CONNECTION, THUS, WE HAVE TO ASK IF THE DESTINY IS REALLY ALIVE
//HOW DO I DO THAT?
//I DONT KNOW HOW TO DO THIS SECCTION///
//NOTE: IF THE SERVER STILL DEAD, I HAVE TO CANCEL THIS MESSAGE AND
//RESTART THE LOOP
if ( flagDestinyStillDead )
{
closeAll();
break;
}
}
message_destiny = new byte[len_message_from_destiny];
//SAVE THE DESTINY'S MESSAGE INTO AN ARRAY WHO HAS THE EXACT SIZE OF THIS MESSAGE
System.arraycopy(buffer_msg_destiny, 0, message_destiny, 0, len_message_from_destiny);
aux = new String(message_destiny);
logger("RECEIVED MESSAGE FROM DESTINY " + aux);
//MAKE THE CHANGES IN THE OUTPUT'S MESSAGE
ChangesInMessages.makeChanges(message_destiny,confs.type_changes_for_output_messages);
aux = new String(message_destiny);
logger("RECEIVED MESSAGE FROM DESTINY WITH MODIFICATIONS: " + aux);
//SENDING THE ANSWER BACK TO THE ORIGIN
logger("SENDING BACK THE MESSAGE TO ORIGIN...");
streamToOrigen.write(message_destiny);
logger("MESSAGE DELIVERED SUCCESSFULLY!");
} //INTERNAL LOOP OF MESSAGES
} //INTERNAL LOOP OF CLIENTS
} //TRY
catch(IOException ex )
{
logger("THE SERVICE DIED: " + ex.getMessage() );
ex.printStackTrace();
} //CATCH
} //RUN
private void closeDestinyStream() throws IOException
{
streamFromDestiny.close();
streamToDestiny.close();
}
private void closeOriginStream() throws IOException
{
streamFromOrigin.close();
streamToOrigen.close();
}
private void closeAll() throws IOException
{
closeDestinyStream();
closeOriginStream();
}
private void logger(String message)
{
System.out.println(Utilidades.date() + " " + prefix_log_messages + " " + message);
}
}
Regards!
Sorry for my english, i am not a native speaker.
I will attempt to answer your question using my definition of "alive" and "dead" that I put in the comment. So we know that if the server does not respond within 5 seconds, it is dead. Also, if we could not connect to the server within 5 seconds, then the server is also dead.
We can check if it is alive/dead like this:
boolean flagDestinyStillDead = false;
//give the server 5 seconds to do whatever it needs to get back alive
try {
Thread.sleep( 5000 );
}
catch ( InterruptedException ie ) {
//ignore this. this probably won't happen unless you purposely cause it
}
//we now create a new connection, because the old connection died
socketToDestiny = new Socket();
//we try connecting to the server
try {
socketToDestiny.connect(new InetSocketAddress(confs.destiny_ip,confs.destiny_port), 5000 );
//if our connection was successful, we also need to create a new input and output stream
streamToDestiny = new DataOutputStream( socketToDestiny.getOutputStream() );
streamFromDestiny = new DataInputStream( socketToDesinty.getInputStream() );
//we give the server 5 seconds to respond to any of our messages
socketToDestiny.setSoTimeout( 5000 );
//ask the server if its alive
streamToDestiny.writeUTF( "Are you alive?" );
//if the server responds, then by our definition of "alive", the server is alive
String receivedMessage = streamToDestiny.readUTF();
if ( receivedMessage.equals( "Yes, I am alive now!" ) ) {
flagDestinyStillDead = false;
}
//if the server did not respond, then we would get a SocketTimeoutException
//and we never would reach here
}
catch ( SocketTimeoutException e ) {
//server had 5 seconds to accept our connection, and since the connection timed out
//we presume that the server is still dead
flagDestinyStillDead = true;
}
catch ( IOException e ) {
//we gave the server 5 seconds already to get back alive using Thread.sleep(...)
//if any of our communications fail, then the server must be dead.
flagDestinyStillDead = true;
}
So, here's our process in English:
1. We lost connection to the server, oh no!
2. Ok, well if it doesn't respond in around 5 seconds, then we'll
presume it died
3. Fine, we'll wait 5 seconds.
4. Ok, 5 seconds passed. Let's connect again with 5 second timeout.
4.1 Connection is reestablished. Ok, now we send the server a message to check that it can respond.
4.1.1 We send the server a message and it responds. Ok, it's alive
4.1.2 We send the server a message and it doesn't respond after 5 seconds. Ok, it's dead
4.2 Connection is not reestablished. Ok, well we already waited 5 seconds. Since the server won't connect even after 5 seconds is up, we presume it's dead.
Please note that when I do streamToDestiny.writeUTF( "Are you alive?" ), you'll need some kind of readUTF() code on the server to read this message. Then the server has to writeUTF( "Yes, I am alive now!" ); back. You'll have to modify this small part of the code to fit with however your server and client runs.
EDIT I have it working now thanks to the comments below. I also explained what I fixed in the comments. Thanks for the help guys.
Im working on a multiplayer game in java. It's coming along pretty well so far, but Im having an issue with the server sending information to the client. The process should be that, the server receives information from the client and interprets what it's supposed to do. In this case, the client sends a chat message to the server split with commas. "chat,Bob,the message is here."
At this point in time, the server should essentially send back that same information to the client that sent the message. Somehow, along the way though, the ByteBuffer which is what is housing the information gets corrupted?
The following is the pertinent code for the server:
// Read the data
SocketChannel sc = (SocketChannel) key.channel();
// interpret
int bytesEchoed = 0;
while (true) {
//Clears this buffer.
echoBuffer.clear();
int number_of_bytes;
String message = new String(echoBuffer.array());
String[] splits = message.split(",");
try {
number_of_bytes = sc.read(echoBuffer);
} catch (java.io.IOException e) {
key.cancel();
number_of_bytes = -1;
}
//-----------Interpret Packets--------------------
//-------------Chat-----------------
if (splits[0].contentEquals("chat")) {
//do chat shit
String name = splits[1];
String text = splits[2];
String sendBack = "chat," + name + "," + text + ","+"\r";
System.out.println(sendBack);
if (splits[0].equals("chat")) {
echoBuffer.clear();
echoBuffer.put(sendBack.getBytes());
}
}
//
if (number_of_bytes <= 0) {
break;
}
//
//
echoBuffer.flip();
sc.write(echoBuffer);
bytesEchoed += number_of_bytes;
}
System.out.println("Echoed " + bytesEchoed + " from " + sc);
// once a key is handled, it needs to be removed
it.remove();
}
}
}
}
Can anyone tell me what I am messing up?
I wasn't doing clear() before I was putting the sendBack string to the bytebuffer, and that was adding the text to the end of the buffer, instead of the beginning. Also, on the client side I was using readLine() to get the incoming data, but there was no carriage return "\r" or new line "\n" on the outgoing server data, resulting in my client reading nothing. Those two things fixed, have it working properly.
For my homework assignment, I have a network of Nodes that are passing messages to each other. Each Node is connected to a set amount of other Nodes (I'm using 4 for testing). Each Link has a weight, and all the Nodes have computed the shortest path for how they want their messages sent. Every Packet that is sent is composed of the message protocol (a hard-coded int), an integer that tells how many messages have passed through the sending Node, and the routing path for the Packet.
Every Node has a Thread for each of its Links. There is an active Socket in each Link. The Packets are sent by adding a 4-byte int to the beginning of the message telling the message's length.
Everything works fine until I stress the network. For my test, there are 10 Nodes, and I get 5 of them to send 10000 packets in a simple while() loop with no Thread.sleep(). Without exception, there is always an error at some point during execution at the if(a!=len) statement.
Please let me know if I can clarify anything. Thanks in advance! Here is the code (from the Link Thread; send() and forward() are called from the Node itself):
protected void listen(){
byte[] b;
int len;
try{
DataInputStream in = new DataInputStream(sock.getInputStream());
while(true){
len = in.readInt();
b = new byte[len];
int a = in.read(b,0,len);
if(a!=len){
System.out.println("ERROR: " + a + "!=" + len);
throw new SocketException(); //may have to fix...this will happen when message is corrupt/incomplete
}
Message m = new Message(b);
int p = m.getProtocol();
switch (p){
case CDNP.PACKET:
owner.incrementTracker();
System.out.print("\n# INCOMMING TRACKER: " + m.getTracker() + "\n>>> ");
owner.forward(m);
}
}
}catch (IOException e){
e.printStackTrace();
}
}
public void send(int tracker){
String[] message = { Conv.is(CDNP.PACKET), Conv.is(tracker), owner.getMST().toString() };
Message m = new Message(message);
forward(m);
}
public synchronized void forward(Message m){
try{
OutputStream out = sock.getOutputStream();
//convert length to byte array of length 4
ByteBuffer bb = ByteBuffer.allocate(4+m.getLength());
bb.putInt(m.getLength());
bb.put(m.getBytes());
out.write(bb.array());
out.flush();
}catch (UnknownHostException e){
System.out.println("ERROR: Could not send to Router at " + sock.getRemoteSocketAddress().toString());
return;
}catch (IOException e1){
}
}
int a = in.read(b,0,len);
if(a!=len){
That won't work. The InputStream may not read all the bytes you want, it may read only what is available right now, and return that much without blocking.
To quote the Javadocs (emphasis mine):
Reads up to len bytes of data from the input stream into an array of bytes. An attempt is made to read as many as len bytes, but a smaller number may be read, possibly zero. The number of bytes actually read is returned as an integer.
You need to continue reading in a loop until you have all the data you want (or the stream is finished).
Or, since you are using a DataInputStream, you can also use
in.readFully(b, 0, len);
which always reads exactly len bytes (blocking until those have arrived, throwing an exception when there is not enough data).