i am creating a multiple-client/server app whenever any client disconnects from
my server it just hangs.
how can i set any condition that will tell me print some message whenever
any client disconnects from the server
here is my server code
class ServerThread implements Runnable {
public void run() {
Socket socket = null;
try {
System.out.println("server starting.......");
serverSocket = new ServerSocket(SERVERPORT);
} catch (IOException e) {
e.printStackTrace();
}
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println("Ready to accept.......");
socket = serverSocket.accept();
System.out.println(" client Connected with ip address =" +socket.getRemoteSocketAddress().toString());
CommunicationThread commThread = new CommunicationThread(socket);
new Thread(commThread).start();
} catch (IOException e) {
e.printStackTrace();
System.out.println("catch block");
}
}
}
}
class CommunicationThread implements Runnable {
private Socket clientSocket;
private BufferedReader input;
public CommunicationThread(Socket clientSocket) {
this.clientSocket = clientSocket;
try {
this.input = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
String read = input.readLine();
updateConversationHandler.post(new updateUIThread(read));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
any help will be appreciated
It's not clear whether you mean disconnect because the conversation was over (ie: everything completed successfully) or the disconnect is because of some network problems (or the client canceled the request).
If it's the first case, then it's easy: the protocol you are using (your own, or http, or whatever) is in charge of defining how to determine that a conversation was over. If that situation arises, then you just close the socket.
If it's the second case, then you'd have to have an algorithm in place to determine whether or not the connection must be closed. For instance, by implementing a timeout, or a slow-read threshold. Take a look at the Socket's javadoc for instructions on how to set a timeout.
It's also worth noting that it's fine to create your own servers when you want to practice or learn something, but you'd be better off using an existing solution, like vert.x or a slimmed down version of Wildfly, for instance. The overhead of such servers is very low, nowadays, while still providing very robust networking capabilities.
Related
So my question goes here. Now if my Server has over 20 clients, it also has 20 threads and my desktop with an ryzen CPU goes to 100% at usage at 30 Threads. Now I'd like to handle a mass-amount of clients by one server, but the CPU is just getting over-used. My wise is very simple how I do it, but there must be a better way; because I saw many good java servers so far yet. I don't know what I do wrong though. In the following I share my code, how I do it in principle.
while(this.isRunning()) {
ServerSocket server = new ServerSocket(8081);
Socket s = server.accept();
new Thread(new WorkerRunnable(s)).start();
//now here if e.g. over 25 users connect there are 25 threads. CPU is at 100%. Is there a better way to handle this?
The worker runnable is identifing the clients. After that they will get into a chat-room. Its like a group chat for e.g.
Edit: Relevant parts of my very unfinished code which is still very WIP
private boolean state;
private ServerSocket socket;
#Override
public void run() {
while(this.isRunning()==true) {
try {
if(this.socket==null) this.socket = new ServerSocket(this.getPort());
Socket connection = this.socket.accept();
IntroductionSession session = new IntroductionSession(this, connection);
new Thread(session).start();
//register timeout task for 3 secs and handle it async
System.out.println(ManagementFactory.getThreadMXBean().getThreadCount());
//this.handleIncomingConnection(connection);
} catch(Exception e) {
e.printStackTrace();
//System.exit(1);
}
}
}
private class IntroductionSession implements Runnable {
private boolean alive = true;
private BaseServer server;
private Socket socket;
private boolean introduced = false;
public IntroductionSession(BaseServer server, Socket socket) {
this.server = server;
this.socket = socket;
}
private void interrupt() {
System.out.println("Not mroe alive");
this.alive = false;
}
private void killConnection() {
this.killConnection("no_reason");
}
private void killConnection(String reason) {
try {
if(this.from_client!=null) this.from_client.close();
if(this.to_client!=null) this.to_client.close();
this.socket.close();
switch(reason) {
case "didnt_introduce":
System.out.println("Kicked connection, cause it didn't introduce itself");
break;
case "unknown_type":
System.out.println("Kicked unknown connection-type.");
break;
case "no_reason":
default:
//ignore
break;
}
} catch (IOException e) {
switch(reason) {
case "didnt_introduce":
System.out.println("Error at kicking connection, which didn't introduce itself");
break;
case "unknown_type":
System.out.println("Error at kicking unknown connection-type.");
break;
case "no_reason":
default:
System.out.println("Error occured at kicking connection");
break;
}
e.printStackTrace();
}
}
private ObjectInputStream from_client;
private ObjectOutputStream to_client;
#Override
public void run() {
while(this.alive==true) {
try {
if(this.to_client==null) {
this.to_client = new ObjectOutputStream(this.socket.getOutputStream());
//this.to_client.flush();
}
if(this.from_client==null) this.from_client = new ObjectInputStream(this.socket.getInputStream());
//Time runs now, if socket is inactive its getting kicked
new Timer().schedule(new java.util.TimerTask() {
#Override
public void run() {
if(IntroductionSession.this.introduced==false) {
IntroductionSession.this.killConnection("didnt_introduce");
Thread.currentThread().interrupt();
IntroductionSession.this.interrupt();
}
}
}, 5000
);
Object obj = this.from_client.readObject();
while(obj!=null) {
if(obj instanceof IntroductionPacket) {
IntroductionPacket pk = (IntroductionPacket) obj;
introduced = true;
if(isCompatible(pk)==false) {
try {
this.to_client.writeObject(new DifferentVersionKickPacket(BaseServer.version));
this.to_client.close();
this.from_client.close();
IntroductionSession.this.socket.close();
System.out.println("Kicked socket, which uses another version.");
} catch(Exception e) {
Thread.currentThread().interrupt();
//ignore
System.out.println("Error at kicking incompatible socket.");
e.printStackTrace();
}
} else {
this.server.handleIncomingConnection(this.socket, this.from_client, this.to_client);
}
Thread.currentThread().interrupt();
}
}
} catch(StreamCorruptedException e) {
//unknown client-type = kick
this.killConnection("unknown_type");
} catch (IOException|ClassNotFoundException e) {
e.printStackTrace();
this.killConnection("no_reason");
}/* catch(SocketException e) {
}*/
}
Thread.currentThread().interrupt();
}
}
Extending class, which is an actual server:
#Override
public void handleIncomingConnection(Socket connection, ObjectInputStream from_client, ObjectOutputStream to_client) {
new AuthenticationSession(connection, from_client, to_client).run();
}
private class AuthenticationSession implements Runnable {
private Socket socket;
private ObjectInputStream from_client;
private ObjectOutputStream to_client;
public AuthenticationSession(Socket socket, ObjectInputStream from_client, ObjectOutputStream to_client) {
this.socket = socket;
this.to_client = to_client;
this.from_client = from_client;
}
//TODO: Implement app id for access tokens
#Override
public void run() {
try {
while(this.socket.isConnected()==true) {
/*ObjectOutputStream to_client = new ObjectOutputStream(socket.getOutputStream()); //maybe cause problems, do it later if it does
ObjectInputStream from_client = new ObjectInputStream(socket.getInputStream());*/
Object object = from_client.readObject();
while(object!=null) {
if(object instanceof RegisterPacket) {
RegisterPacket regPacket = (RegisterPacket) object;
System.out.println("Username:" + regPacket + ", password: " + regPacket.password + ", APP-ID: " + regPacket.appId);
} else {
System.out.println("IP " + this.socket.getInetAddress().getHostAddress() + ":" + this.socket.getPort() + " tried to send an unknown packet.");
this.socket.close();
}
}
}
}/* catch(EOFException eofe) {
//unexpected disconnect
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
/*catch(Exception e) {
//e.printStackTrace();
Thread.currentThread().interrupt();
}*/
}
}
Please dont look at its very bad formatting and stuff I did in hope to fix it, the tasks dont die whyever though.
Generally, in production grade server code, we don't work with direct creation of socket and handling of requests. It's a nightmare to work with low level sockets, close connections and prevent leaks. Rather, we rely on production grade frameworks such as Java Spring Framework or Play Framework.
My question is, why aren't you using any server-side frameworks such as the ones I listed above?
If you're wondering how these frameworks handle thousands of concurrent requests, look into design patterns such as Thread Pool. These frameworks abstract away the complexities and handle the thread pool for you.
If the clients aren't expected to receive an immediate response, you could also look into introducing messaging queue such as Kafka. The server will pick the messages one by one from the queue and process them. However, bear in mind that this is asynchronous and may not meet your requirements.
If you're not just restricted to one server, you could look into deploying your server code to Azure or AWS VMSS (Virtual machine scale set). Based on CPU load rules you configure, the system will autoscale and dynamically manage resources for you.
I would suggest reading upon system design principles related to servers to reinforce your understanding.
Don't reinvent the wheel.
Since you are doing a Chat Application you need to think of doing a Single Threaded Event Loop.
You can Keep a Map of String (Client id) and Socket (Client socket).
Map<String, Socket> clientSockets;
You Server thread will accept new Client Sockets and will just put it in the above map. Then there will be another Thread which will do the Event Loop and whenever there is data in any of the Client Socket in InputStream it should send that data to all other Client Sockets (Group Chat). This should happen infinitely with a Sleep interval.
I am developing a program that has a chat feature and I am using sockets in it.
In my case, I want to handle each of the client in a different window chat(PLEASE SEE ATTACHED IMAGE).
As of now, when 1 client is connected, there is no problem. But when 2 clients are connected, the first client will be overridden by the 2nd one and he can't receive messages from server not unless I close the connection for the latest client connected(Server still receiving messages from all client although only 1 client can receive from server).
How am I gonna do this? I am using captain casa framework
I want to manage it like what did the image below do.
IMAGE HERE
Here is my code:
Server:
public void mainserver(){
Thread server = new Thread(new Runnable() {
#Override
public void run() {
try {
serverSocket = new ServerSocket(port);
System.out.println("Server Online... \nWaiting for Connections");
} catch (IOException e) {
e.printStackTrace();
}
while (accept){
try {
socket = serverSocket.accept();
System.out.println("New Connection Estasblished!!!");
chatHandler chat = new chatHandler(socket);
chat.start();
} catch (IOException e) {
System.out.println("server not terminate all connections");
System.exit(-1);
}
}
}
});
server.start();
}
public class chatHandler extends Thread{
Socket socket;
public chatHandler(Socket socket){
this.socket = socket;
}
public void run(){
try {
din = new DataInputStream(socket.getInputStream());
dout = new DataOutputStream(socket.getOutputStream());
dout.writeUTF("Hi! Thank you for reaching us! How may I help you!?");
while (!read.equals(".end")){
read = din.readUTF();
if (getServerArea()!=null){
setServerArea(getServerArea()+"\n"+read);
}else {
setServerArea(read);
}
}
System.out.println("end of chat server");
} catch (IOException e) {
e.printStackTrace();
}finally {
System.out.println("Exit");
try {
dout.close();
din.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void serverSend(javax.faces.event.ActionEvent event) { // "Send" button
write = getServerField();
try {
dout.writeUTF(write);
dout.flush();
if (getServerArea()!=null){
setServerArea(getServerArea()+"\n"+write);
setServerField("");
}else {
setServerArea(write);
setServerField("");
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(write);
}
Client:
public void client(){
Thread client = new Thread(new Runnable() {
#Override
public void run() {
try {
socket = new Socket("localhost",port);
din = new DataInputStream(socket.getInputStream());
dout = new DataOutputStream(socket.getOutputStream());
while (!read.equals("bye")){
read = din.readUTF();
if (getClientArea()!=null){
setClientArea(getClientArea()+"\n"+read);
}else {
setClientArea(read);
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
din.close();
dout.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
client.start();
}
public void clientSend(javax.faces.event.ActionEvent event) {
write = getClientField();
try {
dout.writeUTF(write);
dout.flush();
if (getClientArea()!=null){
setClientArea(getClientArea()+"\n"+write);
setClientField("");
}else {
setClientArea(write);
setClientField("");
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(write);
}
I believe I understand the problem, and how to correct it.
You are using a unique thread (chatHandler) for each new connection.
This thread writes an automatic "Hello" upon connection, but thereafter is dedicated to reading messages (in the while loop you only read din) and updating the console accordingly. Since each thread is managing a reference to din, all incoming messages are OK.
However, it seems that writing back to a client (serverSend) is not in a thread; it is triggered by a button event. At this point, dout will be a reference to the most recent connection, and not a reference to the client intended to get the message. That is why the most recent client gets all future messages.
The correction is to choose the correct 'dout' for the intended client. When the server 'operator' chooses to write a message back (clicking the send button), somehow you need to obtain the correct 'dout' for that client.
One way to do this is to establish dout prior to creating the thread (using socket), and maintain a relationship between each client, and it's corresponding dout (i.e. in a Map).
If the problem is still not clear (that each client must have a unique reference to dout), please let me know and I will try to clarify.
Basically I need to make a server that handles multiple devices sending/receiving information. I have to be able to send commands to the devices. The number of devices is about 40 for now but will increase to maybe 400 over time. The devises will always send information once every 40seconds-60seconds which is set on the device so it can vary, but may also send more information depending on other factors, such as a responses to commands sent to it. So I have read there is java NIO which I can use or what I have currently done is created a thread for each incoming connection. The sending is not a constant thing so it needs to happen on demand, based on users input on my jsp website. So this is where I am stuck. How do I accomplish the sending of commands from outside the program where the connection is.
This is what I currently have:
Main server class to handle connections and make threads.
try (ServerSocket serverSocket = new ServerSocket(portNumber)) {
while (listening) {
ServerThread r = new ServerThread(serverSocket.accept());
Thread thread = new Thread(r);
thread.setDaemon(true);
System.out.println(thread.getId() + "thread");
thread.start();
thread.join();
}
} catch (IOException e) {
System.err.println("Could not listen on port " + portNumber);
System.exit(-1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Then the ServerThread class:
public class ServerThread implements Runnable{
private Socket socket = null;
public AtomicBoolean isStopped=new AtomicBoolean(false);
public ServerThread(Socket socket) {
this.socket = socket;
}
public void run() {
while(!this.isStopped.get()){
try (
DataInputStream in = new DataInputStream(socket.getInputStream());
) {
ReceiveThread r = new ReceiveThread(in);
Thread thread = new Thread(r);
thread.setDaemon(true);
thread.start();
thread.join();
socket.close();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Then the ReceiveThread handles the reading/decoding.
On demand infrequent communication is better handled via UDP maybe with re-transmission implementation if you need to make sure that data is received, alternatively you can use NIO channels to handle that.
Creating a Thread for every client if the communication is infrequent is wasteful and pointless.
I have a program running on a server (Server A) which listens on one port for an external connection (from an external server, B), then listens on another port for an internal connection on the same server (Server A). It then passes data from the internal to external connection and back again.
I want to know if there is a way I can detect that the client external was disconnected. I just need one external connection at a time, but I would like to be able to accept a new one if the external client reboots or something like that.
This socket level stuff is all fairly new to me, so if there is a better way of going about this, I'm all ears. The one stipulation is that the client running on Server B must be the one to initiate the connection and the connection must live for as long as possible.
public void handleConnection() {
System.out.println("Waiting for client message...");
try {
SSLSocket extSocket = (SSLSocket) this.externalServerSocket.accept();
ObjectInputStream externalOis = new ObjectInputStream(extSocket.getInputStream());
ObjectOutputStream externalOos = new ObjectOutputStream(extSocket.getOutputStream());
System.out.println("Client connection establisthed");
// Loop here to accept all internal connections
while (true) {
SSLSocket internalSocket = (SSLSocket) this.internalServerSocket.accept();
new ConnectionHandler(externalOis, externalOos, internalSocket);
}
} catch (IOException e) {
System.err.println(e.getMessage());
return;
}
}
class ConnectionHandler implements Runnable {
private SSLSocket internalSocket;
private ObjectOutputStream internalOos;
private ObjectInputStream internalOis;
private ObjectInputStream externalOis;
private ObjectOutputStream externalOos;
public ConnectionHandler(ObjectInputStream externalOis,
ObjectOutputStream externalOos, SSLSocket internalSocket) {
this.internalSocket = internalSocket;
try {
this.internalOis = new ObjectInputStream(this.internalSocket.getInputStream());
this.internalOos = new ObjectOutputStream(this.internalSocket.getOutputStream());
this.externalOis = externalOis;
this.externalOos = externalOos;
} catch (IOException e) {
System.err.println(e.getMessage());
}
new Thread(this).start();
}
#Override
public void run() {
try
{
// process data
Object o = internalOis.readObject();
externalOos.writeObject(o);
Object o2 = externalOis.readObject();
internalOos.writeObject(02);
internalOos.close();
internalOis.close();
this.internalSocket.close();
} catch (IOException e) {
System.err.println(e.getMessage());
} catch (ClassNotFoundException e) {
System.err.println(e.getMessage());
}
}
}
If the client disconnects, readObject() will throw EOFException, and write() will throw an IOException: connection reset. That's all you need.
Below I have put a fragment of code to help understand my problem. I have a server code, works fine for the first time the client loads and sends a packet. After the first packet is received, the server is stuck on "accept".
I have wireshark configured for this port, and the server is getting those packets. I just wonder why accept wont return more than once. Its driving me nuts.
Server Code
public class DAPool implements Runnable {
private ServerSocket serverSocket;
private ArrayList<DA> pool;
private LinkedList<Socket> clientConnQ;
public DAPool(int newPoolSize, int serverPort) {
try {
serverSocket = new ServerSocket(serverPort, 500, InetAddress.getByName("127.0.0.1"));
} catch (IOException e) {
e.printStackTrace();
return;
}
poolSize = newPoolSize;
clientConnQ = new LinkedList<Socket>();
pool = new ArrayList<DA>(poolSize);
DA deviceThread;
for (int threads = 0; threads < poolSize; threads++) {
deviceThread = new DA();
connPool.add(deviceThread);
deviceThread.start();
}
}
public void run() {
while (true) {
Socket incomingSocket;
try {
incomingSocket = serverSocket.accept();
} catch (IOException e) {
e.printStackTrace();
return;
}
insertNewConnToQ(incomingSocket);
}
}
private class DA extends Thread {
private Socket clientSocket;
private ObjectInputStream inputObjectStream;
public DA() {
}
public void run() {
while (true) {
while (clientConnQ.isEmpty()) {
synchronized (clientConnQ) {
try {
clientConnQ.wait();
} catch (InterruptedException ignored) {
ignored.printStackTrace();
}
}
}
synchronized (clientConnQ) {
clientSocket = (Socket) clientConnQ.removeFirst();
try {
inputObjectStream = new ObjectInputStream(clientSocket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
return;
}
// Do something useful here
}
}
}
}
}
Client Code
public class SendQueue extends Thread {
LinkedList<Message> requestQ;
Message sendRequest, requestMessage;
Socket clientSocket;
OutputStream outputStream;
ObjectOutputStream objectOutputStream;
public SendQueue(Socket newClientSocket) {
requestQ = new LinkedList<Message>();
clientSocket = newClientSocket;
}
public void run() {
while (true) {
synchronized (requestQ) {
while (requestQ.isEmpty()) {
try {
requestQ.wait();
} catch (InterruptedException ignored) {
ignored.printStackTrace();
}
}
sendRequest = requestQ.removeFirst();
}
try {
outputStream = clientSocket.getOutputStream();
objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(sendRequest);
objectOutputStream.flush();
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
} catch (RuntimeException e) {
e.printStackTrace();
}
}
}
public int sendRequest(Message message) {
synchronized (requestQ) {
requestQ.addLast(message);
requestQ.notify();
}
return 0;
}
}
I don't see a timeout being set on the serverSocket.
ServerSocket.accept() is a blocking operation so it will block until either an error occurs, a timeout occurs, or a connection is accepted.
Try
SererSocket.setSOTimeout(10000)
You also don't seem to be closing your streams when your finished.
Are you sure that it is sticking on the accept call? Did you get a stacktrace that shows it waiting on accept?
Assuming it is getting stuck elsewhere I'm wondering if it isn't because clientConnQ is being held in one of your DA instances. The synchronized block covers the // Do something useful here section.
I wonder if it might work if you changed the code to be
synchronized (clientConnQ) {
clientSocket = (Socket) clientConnQ.removeFirst();
}
try {
...
Once you have your clientSocket from clientConnQ then no other instance can process that socket.
Ok, if I got a $ for everytime I asked a silly question :)
Here goes. A client socket connects and thats when a server receives a accept call. For some silly reason I was waiting on accept for receiving further data from the client. Infact, I should just wait for something on the "stream" and then process the stream. I should not wait on the accept for that connection.
Accept is to be called to "connect" to the socket, not to receive data continuously.
Thanks for your all your help. You forced me to think about thread synchronization, the design, sockets in general and finally arrive that the solution.
Fantastic responses people. Thanks.
Siddharth