I'm setting up a server software to accept multiple connection from ESP32 board. During testing, the server able to receive multiple connections and multiple message in 100ms without stuck. However, the problem now is the server cannot detect an offline disconnect from the ESP32. I need to stop specific thread for the stop connection because when the board is disconnected, it will try to reconnect again. A new thread is open for the new connection but the old thread is still running.
The connected clients will be listed in a JTable in Server side. I have tried to use Thread.interrup() but it's not working because the code is made to waiting receive something from client to start connection.
Below is the code to remove same IP Address to avoid redundant connection because I'm using fix IP Address and also the interrupt thread.
How do I stop the running thread of remove IP Address because of disconnection?? To get successful interrupt thread do I need to stop readLine() in run()? Below is a code snippet in ClientHandler and the run() where it will wait for data/message from client to connect.
socket = clientSocket;
//Get client IP Address
clientIP = socket.getInetAddress().getHostAddress();
Socket socket1;
for(int i = 0; i < clientSockets.size(); i++) {
socket1 = clientSockets.get(i);
if(socket1.getInetAddress().getHostAddress().equals(clientIP)) {
tableModel.removeRow(i);
clientSockets.remove(socket1);
JTextArea.append(clientIP + " - Disconnected.");
JTextArea.append("1. Before interrupt--------");
for(Thread t : Thread.getAllStackTraces().keySet())
{
JTextArea.append(t.getName());
}
JTextArea.append("--------");
for(Thread t : Thread.getAllStackTraces().keySet())
{
if(t.getId() == threads.get(i).getId())
{
threads.remove(i);
t.interrupt();
break;
}
}
JTextArea.append("2. After interrupt--------");
for(Thread t : Thread.getAllStackTraces().keySet())
{
JTextArea.append(t.getName());
}
JTextArea.append("--------");
}
}
run(){
while ((message = netin.readLine())!=null)// Need to stop the readLine() here to interrupt the thread if not cannot stop it.
try {
String[] data = message.split("&");
Vector<Object> tableRow = new Vector<Object>();
if(data[0].equals("connection")) {
msg1= data[1];
msg2= data[2];
msg3= data[3];
tableRow.add(clientIPAddress);
tableRow.add(msg1);
tableRow.add(msg2);
tableRow.add(msg3);
// Add a client row into the table
tableModel.addRow(tableRow);
}
JTextArea.append("Stop Thread.");
} catch (Exception ex) {
JTextArea.append("Run Exception:" + Thread.currentThread().getId() + "-" + ex);
}
Related
I am implementing multiplayer game with server socket mechanism.
Game is running in one loop mechanism.
Server only broadcast any message to every player connected.
Server creates thread for every connected player
Messages are JSONs and they are being sent every 100ms from client side.
Messages are being broadcasted by server and read by client without any interval.
This is server loop:
while (true) {
try {
System.out.println("LISTENING...");
Socket clientSocket = serverSocket.accept();
System.out.println("GOT CONNECTION");
PlayerThread playerThread = new PlayerThread(clientSocket);
playerThread.start();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Exception occured on server");
break;
}
}
This is PlayerThread loop:
while (true) {
try {
String inputMessage = in.readUTF();
System.out.println("GOT: " + inputMessage);
JsonNode jsonNode = objectMapper.readTree(inputMessage);
String senderName = jsonNode.get("senderName").asText();
if (!players.contains(this)) {
playerName = senderName;
players.add(this);
}
for (PlayerThread p : players) {
p.getOut().writeUTF(inputMessage);
}
And finally listening to messages:
public void listen() {
if (connected) {
try {
if (in.available() > 0) {
String inputMessage = in.readUTF();
System.out.println("GOT MESSAGE: " + inputMessage);
handleMessages(inputMessage);
}
} catch (IOException e) {
LOGGER.log(Level.INFO, "Connection lost");
connected = false;
}
} else
LOGGER.log(Level.INFO, "Player is not connected");
}
Method above is run in main game loop. It checks if there's something in inputStream and then reads it.
This is how correct message looks like:
GOT MESSAGE: {"type":"UPDATE_STATE","content":null,"senderName":"GRACZ 52","posX":10.0,"posY":5.0}
It works ok when there are 2 players, but the more players connect the more probably is to get message like this (or similiar broken messages):
GOT MESSAGE: 0} U{"type":"UPDATE_STATE","content":null,"senderName":"GRACZ 65","posX":10.0,"posY":5.0}
or
GOT MESSAGE: {"type":"UPDATE_STATE","content":null,"senderName":"GRACZ 24","pos
There different errors in message like Y letter, half-message or multiple messages in one row. Why such thing happen? It looks like when there are more players and they write into output stream in server side, this stream is not read yet so they are appending and appending. But it doesn't explain why there are broken and most imporant, how to resolve it?
I can move reading stream to another thread because in.readUTF() locks process but I wanted to keep it synchronized in main game loop and I don't think this will help (am I wrong?)
You need to synchronize your write loop on an object that's common between all PlayerThreads so that messages don't interleave.
synchronized(/*Should be a "global" server object*/) {
for (PlayerThread p : players) {
p.getOut().writeUTF(inputMessage);
}
}
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.
I have a network client which tries 3 times in a loop to connect to server. During this time I use sleep thread. Is there any way to replace Thread.sleep(700); with some code which skip the waiting period right after the client is connected.
NClient pc;
if (pc == null)
{
try
{
Thread.sleep(700);
}
catch (InterruptedException x)
{
//TODO
}
if (pc != null)
{
outPrint.println("Connected");
break;
}
}
I would like to improve the user experience by reducing the waiting period in which the connection negotiation is in progress. What are the options in Java to do this?
The answer to this question depends on the implementation of NClient. Typically, I'd use a connect timeout for this. The example below indicates how to do this with a Socket. I don't know what NClient is, so I can't give you an NClient example unfortunately.
Create a method that attempts to connect - up to 3 times
Socket connectToServer() {
Socket socket = new Socket();
final int connectTimeoutMs = 700;
for (int i=0; i<3; i++) {
try {
// the call to connect blocks the current thread for a maximum of 700ms if it can't connect
socket.connect(new InetSocketAddress("localhost", 8080), connectTimeoutMs);
} catch (IOException e) {
// failed to successfully connect within 700 milliseconds
e.printStackTrace();
}
}
return socket;
}
Use the above as follows
Socket socket = connectToServer();
if (socket.isConnected()) {
// do stuff with the valid socket!
}
In short, use a connect timeout!
I wrote a simple TCP server to transfare some user Data to it and save it in an simple MySQL table. If i now run more than 2000 clients after each other it stops working. While running i get some IO error java.io.EOFException you may also see the misstake i made for that. But the most importand is that i get this
IO error java.net.SocketException: Connection reset
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Unknown Source)
at Server.main(Server.java:49)
Enough Memory schould be there but the threads are still running and i dont see where i made the misstake that they dont get terminated. So i got up to 3900 threads running than.
So here is the part of the Server:
try {
// create new socket
ServerSocket sock = new ServerSocket(port);
textArea.setText(textArea.getText() + "Server started\n");
while (true) {
// accept the connection
Socket newsock = sock.accept();
// handle the action
Thread t = new ThreadHandler(newsock);
newsock.setSoTimeout(2000); // adding client timeout
t.start();
}
} catch (Exception e) {
guess really simple. Here is how i handle the socket:
class ThreadHandler extends Thread {
private Socket socket;
private MySQLConnection sqlConnection;
ThreadHandler(Socket s) {
socket = s;
sqlConnection = new MySQLConnection();
}
public void run() {
try {
DataOutputStream out = new DataOutputStream(
socket.getOutputStream());
DataInputStream in = new DataInputStream(new BufferedInputStream(
socket.getInputStream()));
Server.textArea.append((new Date()) + "\nClient connected IP: " + socket.getInetAddress().toString()+"\n");
int firstLine = in.readInt(); // get first line for switch
switch (firstLine) {
case 0:
// getting the whole objekt for the database in own lines!
String name2 = in.readUTF();
int level2 = in.readInt();
int kp2 = in.readInt();
String skill = in.readUTF();
LeadboardElement element2 = new LeadboardElement();
element2.setName(name2);
element2.setLevel(level2);
element2.setKillPoints(kp2);
element2.setSkill(skill);
sqlConnection.saveChaToLeadboard(element2);
break;
//case 1 return the top10
###.... shorten here the rest of the cases
out.close();
in.close();
//close this socket
socket.close();
Server.textArea.append("Client disconnected IP: " + socket.getInetAddress().toString()+ "\n" + (new Date())
+ "\n----------------------------------------------------\n");
// autoscrolldown
Server.textArea.setCaretPosition(Server.textArea.getDocument()
.getLength());
} catch (Exception e) {
System.out.println("IO error " + e);
try {
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}finally{
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
the saveChaToLeadboard simply gets the name level kp and skill and uses a preparedStatement so save it to my MySQL Table.
I hope you can help me i just dont see the misstake of it. I think i need to Join it somewhere but if i put a join at the end of it (after socket.close()) it still does the same.
Here the save to database methode:
public void saveChaToLeadboard(LeadboardElement element) {
try {
// load driver
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection(this.databaseURL
+ DATABASE_NAME, this.user, this.password);
// insert values into the prep statement
preparedStatement = connection
.prepareStatement(PREP_INSERT_STATEMENT);
preparedStatement.setString(1, element.getName());
preparedStatement.setInt(2, element.getLevel());
preparedStatement.setInt(3, element.getKillPoints());
if(!element.getSkill().equalsIgnoreCase("")){
preparedStatement.setString(4, element.getSkill());
}else{
preparedStatement.setString(4, null);
}
// execute
preparedStatement.executeUpdate();
connection.close();
} catch (Exception e) {
Server.textArea.append(e.getMessage() + "\n");
Server.textArea.setCaretPosition(Server.textArea.getDocument()
.getLength());
try {
connection.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
Thanks alot!
Regards
Your run() method is mangled, but I suspect that part of the problem is that you are not always closing network sockets and streams. In particular, I suspect that you are not closing them if there is an exception while reading, or processing the data you read. You should always close sockets and streams in a finally block (or the Java 7 equivalent).
Another potential problem is that some of the connections may be stalling due to the other end not sending data. To deal with that, you would need to set a read timeout on the socket ... so that connections to slow / stuck clients can be closed.
Finally, it is probably unrealistic to even try to process 2000+ connections in parallel with a thread per connection. That's a LOT of resources1. I recommend you use a thread pool with a fixed upper limit in the low hundreds, and stop accepting new connections if all threads are in use.
1 - Each thread stack occupies at least 64K of memory on a HotSpot JVM, and possibly as much of 1Mb. Then there are the Heap resources that the thread directly or indirectly refers to, and OS resources needed to maintain the state of the threads and the sockets. For 2000 threads, that's probably multiple Gb of memory.
IMHO 2000 threads is on the high side for a single process and 2000 database connections definately is.
Regardless of whether or not you're hitting limits with 2000 incoming connections, your approach simply will not scale.
To acheive scalability you need to look at using resource pools - this means:
a pool of reader threads reading from the sockets queuing the data for processing.
a pool of worker threads processing the data queued by the reader threads.
a pool of database connections used by the worker threads - this connection pool could be adjusted so that each worker thread has it's own connection but the important thing is that you don't continually open and close database connections.
Look at the concurreny API for the thread pools and the NIO API for the IO.
This arrangement will allow you to tune your server to acheive the desired throughput.
i'm working on a simple GUI chat program in Java. The goal is for the user to choose whether to host a server or to connect as a client. All of this works. The problem I'm having is letting either the client or the server chat. ideally, the user or the server can type into the textField and hit enter (or press the send button), and then the message will be sent to every client that is connected. During execution, the server runs an infinite while loop where it waits for more clients. The problem I'm having is two-fold:
1) I'm not sure if the way I'm passing the string to the inputstream is right, and 2) I don't know when I can have the server receive and then re-send the data, since it waits at server.accept().
here's the run method:
public void run()
{
conversationBox.appendText("Session Start.\n");
inputBox.requestFocus();
while (!kill)
{
if (isServer)
{
conversationBox.appendText("Server starting on port " + port + "\n");
conversationBox.appendText("Waiting for clients...\n");
startServer();
}
if (isClient)
{
conversationBox.appendText("Starting connection to host " + host + " on port " + port + "\n");
startClient();
}
}
}
here's the startClient method:
public void startClient()
{
try
{
Socket c = new Socket(host, port);
in = new Scanner(c.getInputStream());
out = new PrintWriter(c.getOutputStream());
while (true)
{
if (in.hasNext())
{
Chat.conversationBox.appendText("You Said: " + message);
out.println("Client Said: " + message);
out.flush();
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
and here's the startServer method:
public void startServer()
{
try
{
server = new ServerSocket(port);
while (true)
{
s = server.accept();
conversationBox.appendText("Client connected from " + s.getLocalAddress().getHostName() + "\n");
}
}
catch (Exception e)
{
conversationBox.appendText("An error occurred.\n");
e.printStackTrace();
isServer = false;
reEnableAll();
}
}
And finally, here's the part of actionPerformed where I get the data and (attempt) to write it to the outputstream:
if (o == sendButton || o == inputBox)
{
if(inputBox.getText() != "")
{
out.println(inputBox.getText());
inputBox.setText("");
}
}
I guess my question is: How can I rearrange my methods so that the server can wait for text from the client and then send it back to all the clients? And, how do I send the text from the client to the server?
Among the problems with this code:
You keep creating clients and servers. Surely you should only do one of each?
You are performing blocking network operations on the event thread instead of in a separate thread.
You are looping at EOS via while (true) ... if in.hasNext(). This should be while (in.hasNext()) ...
You are accepting a socket and not apparently doing anything with it. It looks like you can only handle one client at a time. You should start a new thread to handle each accepted socket.