I implemented simple server-client chat in Java. Here the source for the server:
public class Server {
final private static int PORT = 50000;
private static class Read extends Thread {
private static Socket socket;
private static String address;
public Read(Socket socket) {
this.socket = socket;
address = socket.getInetAddress().toString().substring(1);
}
public void run() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String msg;
while (true) {
msg = in.readLine();
if (msg == null) {
in.close();
return;
}
System.out.println(address + ": " + msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static class Write extends Thread {
private static Socket socket;
public Write(Socket socket) {
this.socket = socket;
}
public void run() {
try {
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
String msg;
while (true) {
if (socket.isClosed()) {
out.close();
return;
}
if (stdin.ready()) {
msg = stdin.readLine();
out.println(msg);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
ServerSocket serverSocket;
boolean listening = true;
serverSocket = new ServerSocket(PORT);
while (listening) {
Socket socket = serverSocket.accept();
String address = socket.getInetAddress().toString().substring(1);
System.out.println("Connection Established " + address);
Thread read = new Read(socket);
Thread write = new Write(socket);
read.start();
write.start();
try {
read.join();
write.join();
} catch(InterruptedException e) {
}
socket.close();
System.out.println("Connection Closed " + address);
}
serverSocket.close();
}
}
It works fine but there is a problem. For every established connection the memory continuously grows. I presume the problem is that the memory allocated for the threads is not released afterwards but I'm not quite sure. How can I fix that?
EDIT: The client program:
class Client {
final private static int PORT = 50000;
private static class Read extends Thread {
private Socket socket;
private String address;
public Read(Socket socket) {
this.socket = socket;
address = socket.getInetAddress().toString().substring(1);
}
public void run() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String msg;
while (true) {
msg = in.readLine();
if (msg == null) {
System.out.println("Connection closed " + address);
System.exit(0);
}
System.out.println(address + ": " + msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static class Write extends Thread {
private Socket socket;
public Write(Socket socket) {
this.socket = socket;
}
public void run() {
try {
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
Scanner sc = new Scanner(System.in);
String msg;
while (true) {
msg = sc.nextLine();
out.println(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
PrintWriter out;
BufferedReader in;
Scanner sc = new Scanner(System.in);
while (true) { //for the test only
Socket socket = null;
try {
socket = new Socket("78.90.68.125", PORT);
} catch(java.net.ConnectException e) {
System.out.println("Connection error: host unreachable");
System.exit(1);
}
/*
String address = socket.getInetAddress().toString().substring(1);
System.out.println("Connection established " + address);
Thread read = new Read(socket);
Thread write = new Write(socket);
read.start();
write.start();
try {
read.join();
write.join();
} catch(InterruptedException e) {
e.printStackTrace();
}
finally {
*/
socket.close();
// }
//System.out.println("Connection closed " + address);
}
}
}
Try making
private static class Read extends Thread {
private static Socket socket;
private static String address;
and
private static class Write extends Thread {
private static Socket socket;
to non-static.
Also, I dont know how you checking for memory, but do remember that Java is garbage collected and you will see increase in memory usage initially till the time garbage collector (GC) collects it and will increase again till next GC run. So it consistently increasing without any dip for long time only then there is a memory leak else you are good to go.
I ran the above code as is and ran for around 1-2 hours and it is steady memory usage of around 54MB on Mac machine using JDK 6. I am not using JConsole that comes with jdk to see mem usage. I found NO issues.
Below is the graph as I mentioned in my ans also, you have peak and dip ..in the end when I stopped client it is flat.
Ivan,
a few things to work with Threading.
Never do this:
try {
read.join();
write.join();
} catch(InterruptedException e) {
}
Always put something into the catch clause, and be it a log.error. You have no chance to know it occurs.
Then, all streams/closings etc must go into a finally block. Otherwise you cannever be sure to close everything necessary.
YOu might want to reuse connections. Try this:
http://commons.apache.org/pool/
Can you tell us if you reach the sysout for closing connections regulary?
Basically try to create log statements every time you open a connection and every time you close it. Probably you see what you are missing.
Try putting your socket.close() inside a finally block to ensure that it runs.
But I think your code may have bigger problems in that since you are not using a connection pool, you are needlessly opening new connections.
Related
I've been trying to make the code below to have multiple clients communicate with the same server.
Currently, it works one client at a time with the server but it seems to be that when the second client opens, code stops at new ObjectInputStream(connection.getInputStream()); in the Class 3 (client) - see below.
I've tried making the inputstream object transient to be shared in different threads but it didn't work, nor with making runClient method synchronized.
If I were to implement Serializable in the client class using serialVersionUID, how can I make multithreading work with the same server or is there any better way..?
Class 1 - server main
public class EchoServer {
private ServerSocket server;
private int portNum;
public static final int DEFAULT_PORT = 8081;
public EchoServer(int portNum) {
this.portNum = portNum;
}
public void runServer() {
System.out.println("Echo Server started...");
try {
server = new ServerSocket(portNum);
Socket connection = server.accept();
new Thread(new ClientHandler(connection)).run();
} catch(IOException ex) {
System.err.println("Error encountered! Port is likely already in use! Exiting program...");
ex.printStackTrace();
}
}
public static void main(String[] args) {
if (args.length > 0) {
(new EchoServer(Integer.parseInt(args[0]))).runServer();
} else {
(new EchoServer(DEFAULT_PORT)).runServer();
}
}
}
Class 2
public class ClientHandler implements Runnable {
private ObjectOutputStream output;
private ObjectInputStream input;
private String message;
/** Integer to hold the message number. */
private int messagenum;
private Socket connection;
public ClientHandler(Socket connection) {
this.connection = connection;
}
#Override
public void run() {
do{
handleRequest();
} while (true);
}
public void handleRequest() {
try {
output = new ObjectOutputStream(this.connection.getOutputStream());
input = new ObjectInputStream(this.connection.getInputStream());
do {
try {
message = (String) input.readObject();
System.out.println(messagenum +" Output> " +message);
} catch (EOFException | SocketException e) {
message = null;
}
if (message != null) {
output.writeObject(messagenum +" FromServer> " +message);
output.flush();
++messagenum;
}
} while (message != null);
input.close();
output.close();
this.connection.close();
} catch (IOException | ClassNotFoundException ex) {
System.err.println("Error encountered! Exiting program...");
ex.printStackTrace();
}
}
}
Class 3 - client main
public class EchoClient implements Serializable {
private static final long serialVersionUID = 1L;
private Socket connection;
private ObjectOutputStream output;
private transient ObjectInputStream input;
private String message = "";
private static String serverName;
public static final String DEFAULT_SERVER_NAME = "localhost";
private static int portNum;
BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));
public EchoClient(String serverName, int portNum) {
this.serverName = serverName;
this.portNum = portNum;
}
public synchronized void runClient() {
try {
connection = new Socket(InetAddress.getByName(serverName), portNum);
output = new ObjectOutputStream(connection.getOutputStream());
input = new ObjectInputStream(connection.getInputStream());
do {
System.out.print("Input> ");
message = keyboard.readLine();
if (message != null){
output.writeObject(message);
output.flush();
message = (String) input.readObject();
System.out.println(message);
}
} while (message != null);
input.close();
output.close();
connection.close();
} catch (IOException ioException) {
ioException.printStackTrace();
} catch (ClassNotFoundException exception) {
exception.printStackTrace();
}
}
public static void main(String[] args) {
switch (args.length) {
case 2:
(new EchoClient(args[0], Integer.parseInt(args[1]))).runClient();
break;
case 1:
(new EchoClient(DEFAULT_SERVER_NAME, Integer.parseInt(args[0]))).runClient();
break;
default:
(new EchoClient(DEFAULT_SERVER_NAME, server.EchoServer.DEFAULT_PORT)).runClient();
}
}
}
Call server.accept() in the loop to accept multiple client connections as mentioned in the other answers. Start a new thread with the Thread.start method instead of Thread.run- What's the difference between Thread start() and Runnable run().
volatile boolean isRunning = true;
public void runServer() {
System.out.println("Echo Server started...");
try {
server = new ServerSocket(portNum);
while(isRunning) {
Socket connection = server.accept();
new Thread(new ClientHandler(connection)).start();
}
} catch(IOException ex) {
System.err.println("Error encountered! Port is likely already in use! Exiting program...");
ex.printStackTrace();
}
}
run server needs to wait for connections in a loop otherwise it will connect once and that is it. It needs to close its connections too. Clean up its threads. that's just in server main. I'm pretty sure this is a duplicate. So keep on researching
As said by efekctive, you need your server.accept() in a loop, else it will accept the first client and exit the program. So put these two lines in runServer() in a loop like this:
boolean isRunning = true;
while(isRunning){
Socket connection = server.accept();
new Thread(new ClientHandler(connection)).run();
}
I have been working with TCP server/client stuff for a while. I am actully good at UDP programming when it comes to connecting more than one user that is multiple clients. I tried to do the same on a TCP server that i made using Threads but whenever the Thread gets to this piece of code
String reader = (String)in.readObject();
an error is generated and the thread stops executing the code but the thread still runs the program keeping it alive.
Anyway here is the entire source code :
public class TestServer implements Runnable {
private Thread run, streams, connect, receive, send;
private ServerSocket socket;
private Socket conn;
private ObjectInputStream in;
private ObjectOutputStream out;
private boolean running, incomingMessage = false;
private int port;
public TestServer(int port) throws IOException {
this.port = port;
socket = new ServerSocket(port);
console("Server stated on : " + InetAddress.getLocalHost() + " : " + port);
run = new Thread(this, "Run");
run.start();
}
public void run() {
running = true;
connect();
receive();
}
private void connect() {
connect = new Thread("Connect") {
public void run() {
while(running) {
try {
conn = socket.accept();
} catch (IOException e) {
e.printStackTrace();
}
console("You are now connected" + conn.getInetAddress().toString() + " : " + conn.getPort());
try {
setupStreams();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}; connect.start();
}
private void setupStreams() throws IOException {
streams = new Thread("Streams") {
public void run() {
try {
console("Setting up Streams");
out = new ObjectOutputStream(conn.getOutputStream());
out.flush();
in = new ObjectInputStream(conn.getInputStream());
console("Streams are now setup");
incomingMessage = true;
receive.start();
} catch(IOException e) {
e.printStackTrace();
}
}
}; streams.start();
}
private void receive() {
receive = new Thread("Receive") {
public void run() {
while(incomingMessage) {
String message = "";
try {
message = (String) in.readObject();
//This is the only flaw the program
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
console("Client : " + message);
}
}
};
}
private void console(String message) {
System.out.println(message);
}
public static void main(String[] args) {
try {
new TestServer(1234);
} catch (IOException e) {
e.printStackTrace();
}
}
}
FYI am not new to this. The error is caused because the server starts receiving packets even when there are no packets to be received. But because the thread forces it to receive it, i generates the error in the thread and dont know any other way to counter this. So please help. Thanks in Advance.
You shouldn't need 2 threads per connection. One thread is all that's required. After the connection is accepted, pass it to a worker thread to start reading. This can be done in a while loop in the worker thread.
Even though the socket's input stream can be read, the ObjectInputStream() class is more sensitive. If there is any error, its state is corrupted and it can't be used.
while (true) {
try {
Object input = in.readObject();
message = (String) input;
} catch (IOException e) {
e.printStackTrace();
break; //unrecoverable
} catch (ClassNotFoundException e) {
e.printStackTrace();
break; //unrecoverable
}
console("Client : " + message);
}
It's a better design to use a specific message protocol instead of sending serialized Java objects. For example if you are sending Strings like your sample, an InputStreamReader can be used to convert bytes to characters more easily and with less error handling.
These resources would be helpful to you:
https://docs.oracle.com/javase/tutorial/networking/sockets/clientServer.html#later
Java - Listening to a socket with ObjectInputStream
ObjectInputStream(socket.getInputStream()); does not work
I have a multithreaded server and can have multiple clients at once connected. These threads call a class that has multiple linked lists and the clients can add and remove information to it.
For example
This is the server
public class ShareServer {
public static void main(String[] args) throws IOException {
//if (args.length != 1) {
//System.err.println("Usage: java ShareServer <port number>");
//System.exit(1);
//}
//int portNumber = 2000;
boolean listening = true;
try (ServerSocket serverSocket = new ServerSocket(2000)) {
while (listening) {
new ClientThread(serverSocket.accept()).start();
}
} catch (IOException e) {
System.err.println("Could not listen on port " + 2000);
System.exit(-1);
}
}
}
This is the clientsthread
public class ClientThread extends Thread {
private Socket socket = null;
private ObjectOutputStream out;
private ObjectInputStream in;
FindMatch look= new FindMatch();
string fruit;
public ClientThread(Socket socket) {
super("ClientThread");
this.socket = socket;
}
public void run() {
try {
out = new ObjectOutputStream (socket.getOutputStream());
in = new ObjectInputStream (socket.getInputStream());
int count=0;
boolean flag = false;
try{
fruit = (Double)in.readObject();
flag = look.checkForMatch(string fruit);
if(flag==true)
sendMessage("found a match")
}
catch(ClassNotFoundException classnot){
System.err.println("Data received in unknown format");
}
socket.close();
}catch (IOException e) {
e.printStackTrace();
}
}
void sendMessage(string fuit)
{
try{
out.writeObject(msg);
out.flush();
System.out.println("server>" + msg);
}
catch(IOException ioException){
ioException.printStackTrace();
}
}
}
public LinkedList<String> fruitEntries = new LinkedList<Integer>();
public LinkedList<?> clientID = new LinkedList <?>();
this is the code that it calls
boolean checkFormatch(string fruit){
for(int i = 0; i< fruitEntries.length();i++){
if(fruit == fruitEntries.get(i)){
tell client at clientID(i);
fruitEntries.remove(i);
clientID.remove(i);
retutn true;
}
}
}
This code is far from perfect I just threw this together. the general idea is right though. I will have maybe 6 linked lists of info in mine.
I'm not sure how to keep track of what thread a client has either so I would appreciate help with that.
Personally I would use RMI instead of sockets. RMI handles all the messy listening threading etc.
Consider using one of the java.util.concurrent classes -- ConcurrentSkipListMap
ConcurrentHashMap
Also, when you compare Strings you need fruit.compareTo(...) which is rather slow so creating a hash is probably better.
I am implementing a simple client-server architecture where multiple clients should be able to connect to the server and strings could be exchanged between the server and client.
My idea is that I'll have two threads on each side: a listener, constantly checking if there is anything new in the inputstream, and a writer thread, that writes into the socket if there is something to write.
However, the second thread doesn't even start... Only the first sysout is displayed.
//start new thread to handle client input
new Thread(
new ServerWorker(clientSocket, this, this.getIdCounter())).start();
System.out.println("server side listener started");
//start new thread to handle client output
new Thread(new ServerWorkerListener(clientSocket)).start();
System.out.println("server side writer started");
Here is some code from the ServerWorker:
public void run() {
try {
OutputStream output = clientSocket.getOutputStream();
while (true) {
// output.write(("Pling!\n\n").getBytes());
for (Client tempClient : server.getClientList()) {
if ((tempClient.getId() == this.id)
&& tempClient.isShouldSend()) {
output.write((tempClient.getOutputStream() + "\n\n")
.getBytes());
tempClient.setInputStream("");
tempClient.setShouldSend(false);
}
}
}
} catch (IOException e) {
System.out.println("Error in serverWorker");
e.printStackTrace();
}
}
I really don't know what I'm missing...
Whole of ServerWorker:
public class ServerWorker implements Runnable {
protected Socket clientSocket = null;
protected String serverText = null;
protected int id;
protected Server server;
public ServerWorker(Socket clientSocket, Server server,
int id) {
this.clientSocket = clientSocket;
this.serverText = serverText;
this.id = id;
this.server = server;
}
public void run() {
try {
OutputStream output = clientSocket.getOutputStream();
while (true) {
// output.write(("Pling!\n\n").getBytes());
for (Client tempClient : server.getClientList()) {
if ((tempClient.getId() == this.id)
&& tempClient.isShouldSend()) {
output.write((tempClient.getOutputStream() + "\n\n")
.getBytes());
tempClient.setInputStream("");
tempClient.setShouldSend(false);
}
}
}
} catch (IOException e) {
System.out.println("Error in serverWorker");
e.printStackTrace();
}
}
}
Whole of ServerWorkerListener:
public class ServerWorkerListener implements Runnable {
private BufferedReader input;
private Socket clientSocket;
public ServerWorkerListener(Socket clientSocket) {
this.clientSocket = clientSocket;
run();
}
#Override
public void run() {
System.out.println("its running");
try {
BufferedReader in = new BufferedReader(new InputStreamReader(
clientSocket.getInputStream()));
while (true) {
System.out.println("it's looping");
String inputLine = null;
if ((inputLine = in.readLine()) != null) {
JOptionPane.showMessageDialog(null, inputLine, "InfoBox: "
+ "Message from client",
JOptionPane.INFORMATION_MESSAGE);
}
}
} catch (UnknownHostException e) {
System.err.println("Don't know about client");
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to client");
System.exit(1);
}
}
}
You are invoking run() inside the constructor of ServerWorkerListener, which you must not do. The new thread ought to invoke run(), otherwise, since it contains an infinite loop, it will never return from the constructor and hence never invoke the Thread’s constructor, not to speak of its start method. So removing run() the invocation from the constructor should solve the problem.
public class Main {
public static void main(String[] args) {
Server server = new Server(9008);
}
}
public class Server {
private ServerSocket server;
private Socket client;
public Server(int port) {
try {
// Create out server with our desired port
server = new ServerSocket(port);
// Server started, let the user know
System.out.println("Server started at port " + port + "...");
} catch (IOException e) {
// Unable to start server, print error
System.out.println("Unable to start server on port " + port + "...");
}
// Start our main server method
runServer();
}
public void runServer() {
while (true) {
try {
// Wait for new clients and accept them
client = server.accept();
// Let the user know - print
System.out.println("New user connected - " + client.getLocalAddress().getHostAddress());
// Start thread for our client
Thread clientThread = new Thread(new ClientConnection(client));
clientThread.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
So at this points everything is going fine, now inside my clientThread the problem starts
public class ClientConnection implements Runnable {
private Socket socket;
public ClientConnection(Socket client) {
// Set client socket
this.socket = client;
}
public void run() {
try {
// Read from our client input
BufferedReader readClient = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while ((line = readClient.readLine()) != null) {
System.out.println("Client says - " + readClient.readLine());
}
} catch(IOException e) {
}
}
}
Is there a better way to handle this?
My actual client
public class Main {
public static void main(String args[]) {
try {
Socket socket = new Socket("localhost", 9008);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
writer.write("Hello\n");
writer.flush();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
I will get "Client says - null" displayed
UPDATE: The way to read in an InputStream/Reader is somethink like
while ((myString = readClient.readLine()) != null) {
System.out.println(myString);
}
this way the loop will exit when the connection is closed.
Also, move the try/catch outside the loop, or do some error control. If you get an exception, you do not want to just try get again in the loop.
UPDATE2: In case my comment was not clear enough, over your updated code do
String line;
while ((line = readClient.readLine()) != null) {
System.out.println("Client says - " + line);
}
Just one read per iteration, at the while, so the loop can exit if line is null (that means the connection has been closed).