How do I solve this indexOutOfBoundsException in my server send/receive thread? - java

I am creating a multiplayer game in Java with a server and multiple clients. Everything runs perfectly, until I press the Kick-button in the server to kick a client.
Error at receive thread of server, after kicking the first person who joined out of three:
java.lang.IndexOutOfBoundsException: Index: 2, Size: 2
at java.util.ArrayList.rangeCheck(ArrayList.java:604)
at java.util.ArrayList.get(ArrayList.java:382)
> at networktest.Server$3.run(Server.java:186)
at java.lang.Thread.run(Thread.java:722)
The pointed line is the ois = new ObjectInputStream where I receive datatype in the server receive thread. The server kicks the first person perfectly, but removes the second one in the list too, with an error of java.lang.ClassCastException.
server receive:
private static Thread receive = new Thread()
{
#Override
public void run()
{
ObjectInputStream ois;
while (true)
{
for (int i = 0; i < list_sockets.size(); i++)
{
try
{
ois = new ObjectInputStream(list_sockets.get(i).getInputStream());
int receive_state = (Integer) ois.readObject(); // receive state
ois = new ObjectInputStream(list_sockets.get(i).getInputStream());
byte datatype = (byte) ois.readObject(); // receive datatype
if(datatype == 2){
ois = new ObjectInputStream(list_sockets.get(i).getInputStream());
ChatLine chatLine = (ChatLine) ois.readObject(); // receive ChatLine
} else if (datatype == 0){
ois = new ObjectInputStream(list_sockets.get(i).getInputStream());
DataPackage dp = (DataPackage) ois.readObject(); // receive dp
list_data.set(i, dp);
}
if (receive_state == 1) // Client Disconnected by User
{
disconnectClient(i);
i--;
}
}
catch (Exception ex) // Client Disconnected (Client Didn't Notify Server About Disconnecting)
{
System.err.println("Error # receive:");
ex.printStackTrace();
disconnectClient(i);
i--;
}
}
try {
this.sleep(3);
} catch (InterruptedException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
};
user send:
Thread send = new Thread()
{
public void run()
{
ObjectOutputStream oos;
byte datatype = 0;
while (connected){
if (socket != null){
try {
DataPackage dp = new DataPackage();
dp.x = Client.player.x;
dp.y = Client.player.y;
dp.username = username;
dp.charType = charType;
dp.walking = (byte)Client.player.walking;
if (Client.outputChatLine.line != null)
datatype = 2;
else {
datatype = 0;
}
oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(Integer.valueOf(Client.this.state)); // send state
oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(Byte.valueOf(datatype)); // send datatype
if (datatype == 2)
{
oos.reset();
oos.writeObject(Client.outputChatLine);
Client.outputChatLine = new ChatLine();
} else {
oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(dp);
}
if (Client.this.state == 1) {
connected = false;
socket = null;
JOptionPane.showMessageDialog(null, "Client Disconnected", "Info", 1);
System.exit(0);
}
}
catch (Exception ex){}
}
try {
this.sleep(2);
} catch (InterruptedException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
};
disconnect client method:
public static void disconnectClient(int index)
{
try
{
list_clients_model.removeElementAt(index);
list_client_states.remove(index);
list_data.remove(index);
list_sockets.remove(index);
}
catch (Exception ex) {}
}
Does anyone know how to solve this?

It looks like you're expecting some other thread to fill some data into list_sockets while you're sleep(3)ing. But you have no synchronization to ensure that this happens only while you're sleeping.
It could equally well happen that the other thread is updating list_sockets simultaneously with your own thread calling list_sockets.get(i). And the ArrayList implementation it is almost certainly not written to have two different of its methods executing simultaneously in two different threads. For example, the other thread could be in the middle of resizing the backing array when you're trying to read an element, and then any crazy thing can go wrong, including the error you're seeing.
You need to learn about inter-thread synchronization. At the very least you need synchronized blocks to protect access to shared data structures. And while you're at it; look at wait/notify or some higher-level concurrency tools to get rid of that horrible 3-millisecond polling loop -- instead have the thread that drops work into the list explicitly wake up the worker thread.

Related

Java socket time out does not work

I've a class which is responsible for listening two other machines which have exactly the same classes, so it's a network of three computers having the same code. The connection is there and I can see them passing data to each other. Everything until there works OK.
Things get tricky when I take out one of the machines and observe how the other two behave. Expectedly, when one of the machines stops working for some reason, other two should continue. And if two of them stop, the remaining should go on.
I tried to implement this mechanism below. However, when I take out one of the machines, the program keeps waiting, so it does not switch to "two-way comparison mode".
public void listen() {
try {
logger.info("Creating listener sockets");
while (isRunning) {
final byte[] buf = new byte[bufferSize];
final DatagramPacket packetOne = new DatagramPacket(buf, buf.length);
final DatagramPacket packetTwo = new DatagramPacket(buf, buf.length);
MediatorMessageMsg mediatorMessageOne = null;
MediatorMessageMsg mediatorMessageTwo = null;
try {
socketReceiverOne.receive(packetOne);
ByteArrayInputStream firstInput = new ByteArrayInputStream(buf);
mediatorMessageOne = MediatorMessageMsg.parseDelimitedFrom(firstInput);
socketReceiverTwo.receive(packetTwo);
ByteArrayInputStream secondInput = new ByteArrayInputStream(buf);
mediatorMessageTwo = MediatorMessageMsg.parseDelimitedFrom(secondInput);
logger.trace("Received packets");
} catch (final SocketTimeoutException e) {
logger.trace(e.getMessage());
continue;
} catch (final SocketException e) {
logger.warn(e);
logger.warn("Ignore the error and go on.");
continue;
} catch (final IOException e) {
logger.error("Incoming communication stopped!");
logger.error(e);
stop();
}
// if two mediators sent the data, it's OK
if (packetOne.getLength() > 0 && packetTwo.getLength() > 0) {
handlePackets(mediatorMessageOne, mediatorMessageTwo);
logger.info("Number of active mediators: 2. Comparison style: 1v1v1");
}
// if only one sent the data, compare it with our own
else if (packetOne.getLength() > 0 || packetTwo.getLength() > 0) {
// whicehever sent the data, compare its data with our own
logger.info("Number of active mediators: 1. Comparison style: 1v1");
if (packetOne.getLength() > 0) {
handlePackets(mediatorMessageOne);
} else {
handlePackets(mediatorMessageTwo);
}
}
// if no data is sent, then pass our own directly
else {
logger.info("Number of active mediators: 0. Comparison style: No Comparison");
// our datamodel to retrieve data on our own
DataModel modelOwn = DataModel.getInstance();
MediatorMessageMsg newMessage = MediatorMessageMsg.newBuilder().setHeading(modelOwn.getHeading()).setSpeed(modelOwn.getSpeed()).setSender(getId()).build();
// publish(topicName, newMessage);
}
Thread.sleep(1);
}
socketReceiverOne.close();
socketReceiverTwo.close();
logger.info("stopped");
} catch (final IllegalArgumentException e) {
logger.error("Illegal argument received: " + e);
} catch (final Exception e) {
logger.error("Unexpected error occured: " + e);
} finally {
if (socketReceiverOne instanceof DatagramSocket && socketReceiverTwo instanceof DatagramSocket) {
if (!socketReceiverOne.isClosed() || !socketReceiverTwo.isClosed()) {
socketReceiverOne.close();
socketReceiverTwo.close();
}
}
}
}
To save your time, let me share my opinion on the matter. I suspect the problem to be in this part:
socketReceiverOne.receive(packetOne);
ByteArrayInputStream firstInput = new ByteArrayInputStream(buf);
mediatorMessageOne = MediatorMessageMsg.parseDelimitedFrom(firstInput);
socketReceiverTwo.receive(packetTwo);
ByteArrayInputStream secondInput = new ByteArrayInputStream(buf);
mediatorMessageTwo = MediatorMessageMsg.parseDelimitedFrom(secondInput);
To me it seems like the program expects a package and when it cannot receive it, it keeps waiting. Although I have time out exception condition, I cannot get this done.
private int socketTimeout = 1000 * 2;// 2sec
socketReceiverOne.setSoTimeout(socketTimeout);
socketReceiverTwo.setSoTimeout(socketTimeout);
Any thoughts?
Okay I found where I was mistaken. I needed more ports (for in and out). Once I incorporated those ports, the problem did not occur again.

How to create multiple peer-to-peer chats within client-server messaging program

I am trying to create a messenger program and have successfully set up client-server connections using sockets. However I am finding it difficult to code the process of having several clients communicating simultaneously. Shown in the code below is the methods for the chats that are held within a ClientThread class that regulates the interaction between client and server using threads stored in a shared ArrayList. How would you implement the code for multiple peer-to-peer chats here?
startChat method:
public void startChat()
{
// start the convo!
// first of all the user chooses who to speak to
// starts a loop until user enters a valid username or 'Group'
String line = "";
boolean validCommand = false;
while(validCommand == false)
{
try {
line = in.readLine();
} catch (IOException e) {
System.out.println("Problem reading reply about user chat");
}
if(line.equalsIgnoreCase("Group"))
{
validCommand = true;
chatAll(); // an integer of negative one starts a chat with everyone
}
else
{
synchronized(this){
// find user
for(int i = 0; i < threads.size(); i++)
{
if(threads.get(i) != null && threads.get(i).username != null)
{
if(threads.get(i).username.equals(line)) // means that we have found the index of the thread that the client wants to speak to
{
/*// START : BETWEEN THESE CAPITALISED COMMENTS IS MY ATTEMPT TO INITIATE TWO WAY CHAT
int thisIndex = -1;
for(int j = 0; j < threads.size(); j++) // gets the index of this thread object in the array
{
if(threads.get(j) == this)
{
thisIndex = j;
// out.println(j);
}
}
if(thisIndex != -1)
{
threads.get(i).out.println(username + " is trying to connect");
threads.get(i).processChat(thisIndex); // this is the line causing the problem!
}
// END : BETWEEN THESE CAPITALISED COMMENTS IS MY ATTEMPT TO INITIATE TWO WAY CHAT */
threads.get(i).out.println(username + " is trying to connect");
out.println("Chat with " + threads.get(i).username);
processChat(i);
validCommand = true;
}
// if the command is not group and not a username, it is not valid and we ask the user to re-enter
else if(i == threads.size() - 1)
{
out.println("This command is not valid, please re-enter");
}
}
}
} // end of synchronised bit
} // end of else statement
} // end of while loop
}
allChat method:
void chatAll()
//for the purpose of group chat
{
out.println("Group chat initiated");
boolean d = true;
while(d == true)
{
String message = "";
try {
message = in.readLine();
} catch (IOException e) {
System.out.println("Can't read line from client");
}
if(message.contains("goodbye") == true)
{
d = false;
}
else
{
synchronized(this)
{
for(int j = 0; j < threads.size(); j++)
{
if(threads.get(j) != null)
{
threads.get(j).out.println(username + ": " + message);
}
}
}
}
}
}
processChat method:
void processChat(int i)
//for the purpose of talking to pre-defined user
{
boolean d = true;
while(d == true)
{
String message = "";
try {
message = in.readLine();
} catch (IOException e) {
System.out.println("Can't read message from client");
}
if(message.contains("goodbye") == true)
{
d = false;
}
else {
if(threads.get(i) != null)
{
threads.get(i).out.println(username + ": " + message);
}
}
}
}
Just for good measure and a reference here is the overall client class (confusingly labelled ThreadedClient as opposed to ClientThread haha)
ThreadedClient class:
import java.net.*;
import java.io.*;
public class ThreadedClient implements Runnable {
// client socket
private static Socket clientSocket = null;
//I/O streams to and from the server
private static BufferedReader in = null;
private static PrintStream out = null;
// Input stream to read user input
private static BufferedReader inputReader = null;
private boolean open = true;
public ThreadedClient(String host, int port)
{
startConnection(host, port);
}
public void startConnection(String host, int port)
{
//open up the socket
try {
clientSocket = new Socket(host, port);
} catch (UnknownHostException e) {
System.out.println("The host name '" + host + "' isn't known");
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("Cannot create socket");
}
// connect I/O streams
try {
in = new BufferedReader(new InputStreamReader(new DataInputStream(clientSocket.getInputStream())));
out = new PrintStream(clientSocket.getOutputStream());
inputReader = new BufferedReader(new InputStreamReader(System.in));
} catch (IOException e) {
System.out.println("Problem connecting streams");
}
// process the chat itself
// the thread deals with input coming in
Thread thread = new Thread(this);
thread.start();
// the loop deals with output
while(open == true)
{
String message;
try {
message = inputReader.readLine();
out.println(message);
if(message.contains("goodbye") == true)
{
open = false;
}
} catch (IOException e) {
System.out.println("Problem sending messages");
}
}
// chat is done, so we can close resources
try {
in.close();
inputReader.close();
out.close();
clientSocket.close();
} catch (IOException e) {
System.out.println("Problem closing resources");
}
}
// run method for sending input out. I imagine this will not be necessary in the GUI implemented version, as we can use
// an action listener for the send function, e.g. one that reads a text field into a output stream everytime the user clicks enter
public void run() {
while(open == true)
{
try {
String response = in.readLine();
if(response.contains("goodbye") == true)
{
open = false;
}
System.out.println(response);
} catch (IOException e) {
System.out.println("Problem recieving messages");
}
}
}
public static void main(String[] args)
{
ThreadedClient socket = new ThreadedClient("localhost", 50000);
}
}
I know that this code may not be as advanced as some others I have seen on this forum as well as DreamInCode and others but I was trying to build it from scratch and have been stuck here for what feels like a millennia. Trawling through the internet has not helped :(
Any suggestions and criticisms would be an absolute God send!
Thanks in advance guys.
OK.
You can do like this: Im focus on Console Application
- Define a class call Message:
class Message
{
public String username; // the sender that send this message to u.So you can reply back to this user
public boolean groupMessage; // this message is group message or not
public String message;
}
Define a global variable: ArrayList messages; to hold all incomming messages.
So when you start chat with a client --> create new Thread to read message from him.When you receive a message . You have to put that message to the array list: messages ( you have to remember to sync it. because it will be invoked by many thread)
synchorized(messages){
messages.add(....); // new message here
}
Then , you create a new Thread to show message & can reply back to the sender. In this read you will pop a message from array list messages & show it.
while(isrunning)
{
synchorized(messages){
if(messages.size()<=0) messages.wait(); // when you receive a new message you have to notify
}
synchorized(messages){
Message msg = messages.get(0);
messages.remove(0);
showmessage_to_ouput(msg); // something like this.
String s = read from input // to reply to this message.
Reply(....)// here you can check if this message is group message--> reply to all,..etc
}
}
P/S: That's a idea :) good luck
I can give you a solution , but you have to implement it
We have:
- Server A, Client B & C. B & C already connected to Server via TCP connection
- The first, client B want to chat with C. So B have to send a message by UDP to server
- 2nd, Server will receive a UDP messages from B ==> Server know which ip & port of B that B connected to Server by UDP. Then server send to C a message (TCP) that contains info about UDP ip:port of B .
- 3rd: Client C will receive that message from server via TCP . So C know ip:port that B is listenning .--> If C accept chat with B . C have to send a UDP message to Server to tell server that C accept to talk with B.
- 4th: Server will receive that message via UDP . So Server also know ip:port of C in UDP.
- 5th : The server will transfer UDP ip:port of C to B via TCP (or UDP if you want).
- 6th: Client B will receive it & know udp ip:port of C. So they can start to chat via UDP protocol now.
IT is call UDP/TCP Hole punching. You can research more about it to implement.
P/S: But this method doesnt work with Symetric NAT

Having to recreate ObjectOutputStream every time

I have seen a few people ask similar questions but the only answer anyone ever posts is that you shouldn't have to do it.
But I've tested it both ways - and it only works this way.
Server Side
try {
// Obtain input and output streams to the client
while(true) {
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Object input = in.readObject();
if(input == RequestEnums.GETCURRENTGRID) {
out.writeObject(ContagionServerData.getImagePixels());
out.writeObject(ContagionServerData.getImageHeight());
out.writeObject(ContagionServerData.getImageWidth());
}
}
} catch( Exception e ) {
e.printStackTrace();
}
Client Side
try {
inputStream = new ObjectInputStream(serverSocket.getInputStream());
outputStream = new ObjectOutputStream(serverSocket.getOutputStream());
outputStream.writeObject(RequestEnums.GETCURRENTGRID);
int[] imagePixels = (int[]) inputStream.readObject();
int imageHeight = (Integer) inputStream.readObject();
int imageWidth = (Integer) inputStream.readObject();
copyImage(background, imagePixels, imageHeight, imageWidth);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
This works all day long.
But if I change it to this -
try {
// Obtain input and output streams to the client
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
while(true) {
Object input = in.readObject();
if(input == RequestEnums.GETCURRENTGRID) {
out.writeObject(ContagionServerData.getImagePixels());
out.writeObject(ContagionServerData.getImageHeight());
out.writeObject(ContagionServerData.getImageWidth());
out.flush();
}
}
} catch( Exception e ) {
e.printStackTrace();
}
(I have created the input and output stream farther up in the code)
try {
outputStream.writeObject(RequestEnums.GETCURRENTGRID);
outputStream.flush();
int[] imagePixels = (int[]) inputStream.readObject();
int imageHeight = (Integer) inputStream.readObject();
int imageWidth = (Integer) inputStream.readObject();
copyImage(background, imagePixels, imageHeight, imageWidth);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Then I successfully receive the correct data the first time from the server - but everytime after that - I just receive the same data and not the updated data and no errors as to why.
When you send data on an object stream, it will send each object only once. This means if you modify and object and send it multiple times, you need to either use writeUnshared(mutableObject) or reset() which clears the cache of objects already sent.
You can't create an ObjectOutput/InputStream repeatly. If you want to make sure data is sent instead of buffered use flush(). If you are sending data like int instead of Objects, try DataOutput/InputStream.
See the Javadoc for ObjectOutputStream.reset() and ObjectOutputStream.writeUnshared().

ObjectInputStream failing to read

I have an ObjectOutputStream and an ObjectInputStream. I try to send ints and objects through them. Now I manage to send through and read up to a point and I don't know why it stops there.
Here is the point:
Reader:
while (true) {
start = in.readInt();
System.out.println("PART 1");
int temp1 = in.readInt();
int temp2 = in.readInt();
int temp3 = in.readInt();
System.out.println("PART12");
Chunk temp = new Chunk(temp1,temp2, temp3);
while (true) {
It doesn't get to part12 (doesn't pass the first int...)
Writer:
if (chunkList != null) {
for (Chunk c: chunkList) {
out.writeInt(-1);
out.writeInt(c.getLocation().getX());
out.writeInt(c.getLocation().getY());
out.writeInt(c.getLocation().getZ());
if (c.getTileList() != null) {
it passes all of it successfully.
I am every 2ms out.flushing in a separate thread.
Thread:
while (true)
{
while (c.sendPacket()) {
try
{
if (c.getOut() != null)
{
c.getOut().flush();
}
}
catch (IOException ioexception)
{
ioexception.printStackTrace();
}
try
{
sleep(2L);
}
catch (InterruptedException interruptedexception) { }
}
}
Why does it stop reading at the part with the 3 ints?
I have a feeling that this is a thread-safety issue. As a general rule, streams are not designed to be thread-safe. So, unless the you are synchronizing the two threads at a higher level, one thread writing to a stream and a second thread calling flush is unsafe.

cannot get server socket to close

I'm making a simple chat server and just made it so each connection runs on a new thread.
The old version started a single thread for the server, it did a while loop, which would stop when a stop message was sent then close the socket.
The new version loops forever and create a new thread for each new connection. Now I cannot close the socket connection.
If you press a key and the main thread stops, the socket stays open. Thus when I run the program again I need to change the socket number.
code of server
while(true)
{
///////////////////////////////////////////////////
// get a new connection
///////////////////////////////////////////////////
System.out.println("Aceepting connections on port 1030 \r");
try{
// Get New Connection
// wait for ever on accepting new connections
server.setSoTimeout(0);
connection=server.accept();
cConnection thread = new cConnection("thread3", connection);
} catch(IOException ec)
{
System.out.println(ec.getMessage());
}
}
code that starts server
Now each message comes in on a new thread, so I cannot tell it to stop and close the socket.
You need to provide a flag that must be globally accesible, so when some client wants to stop the server then change the variable ans stops the bucle. By example:
class YourServer {
private static boolean execute = true;
public static synchronized void stop() {
execute = false;
}
public void yourMethod() {
while(execute) {
// implement your server here
}
}
}
When a client send the command STOP you must be do
YourServer.stop();
If you want a stop command to stop the server you can call System.exit() to force the program to store or just closing server is likely to be all you need.
Looking into your problem, I understood one thing, that since you are putting
while (true), so your control always gets stuck at connection=server.accept(); listening for a new connection. So in order to stop the sockets you need to first find a way to stop looping in that while loop. Either you can set a Variable, like (int clientsConnected) to check the number of Clients, when that comes to zero stop that while loop. So you can stop your sockets.
Below is my sample code for clients which is doing the same thing for closing the Sockets.
Hopefully this solves your problem.
class GetNamesFromServer implements Runnable
{
private Socket sForName, sForId;
private BufferedReader in, inForName, inForId;
private PrintWriter outForName, outForId;
private static String clientNames;
public GetNamesFromServer(Socket s1, Socket s2)
{
sForName = s1;
sForId = s2;
}
public void run()
{
try
{
outForName = new PrintWriter(sForName.getOutputStream(), true);
outForName.println(Client.clientName);
System.out.println("Send Name : " + Client.clientName);
outForName.flush();
}
catch(IOException e)
{
System.err.println("Error sending Name to the Server.");
}
try
{
inForId = new BufferedReader(new InputStreamReader(sForId.getInputStream()));
Client.clientId = (inForId.readLine()).trim();
System.out.println("Client ID is : " + Client.clientId);
}
catch(IOException e)
{
System.err.println("Error Receiving ID from Server.");
}
try
{
inForName = new BufferedReader(new InputStreamReader(sForName.getInputStream()));
while (true)
{
clientNames = inForName.readLine();
if (clientNames != null && clientNames != "")
{
clientNames = clientNames.substring(1, clientNames.length() - 1);
System.out.println("Names Received : " + clientNames);
String[] names = clientNames.split(", ");
Client.nameClients.clear();
for (String element: names)
Client.nameClients.add(element);
Client.nPane.setText("");
int size = Client.nameClients.size();
System.out.println("Size of list : " + size);
for (int i = 0; i < size; i++)
{
String name = Client.nameClients.get(i);
String colour = Character.toString(name.charAt(0));
name = name.substring(1, name.length()) + "\n";
appendToNamePane(name, ReceiveMessages.getColour(Integer.parseInt(colour)), "Lucida Console");
}
System.out.println("Clients Online : " + Client.nameClients);
}
int index = Client.nameClients.indexOf(Client.clientId + Client.clientName);
**if (index == -1)
{
sForName.close();
break;
}**
}
}
catch(IOException e)
{
System.err.println("Error Receiving Names of Clients from Server");
}
}
NEW EDITION :
You can add a cap to maximum number of clients that can connect, once that reaches your while loop will not go to connection = server.accept(); and hence when they are done chatting (after some time) i.e. totalClients = 0, you can stop your sockets as well, to stop the program.
if (totalClients == 0)
{
socket.close();
serverSocket.close();
}
Regards

Categories

Resources