BufferedReader readLine() stuck - java

I'm writing a TCP multi Client/Server app that works in an infinite loop.
What happens:
Client types "COMMANDS" and expects to get available commands from server.
Server sends lines to client.
Client reads server input line-by-line and is stuck after the last one is written.
Expected result:
Client is prompted for keyboard input.
Input is written to server.
Server sends back text.
Client reads server input line-by-line.
End of loop.
user types commands > server responds > user gets to type commands again
I know that BufferedReader would stop reading lines once I close the socket. However, that's not what we want since it has to work infinitely.
How do I exit the loop?
Client method:
public class Client {
private final Socket clientSocket;
private final BufferedReader clientInput;
private final PrintWriter clientOutput;
private final BufferedReader keyboardInput;
public Client() throws IOException {
this.clientSocket = new Socket(InetAddress.getLocalHost(), 13370);
this.clientInput = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
this.clientOutput = new PrintWriter(clientSocket.getOutputStream(), true);
this.keyboardInput = new BufferedReader(new InputStreamReader(System.in));
}
public static void main(String[] args) throws IOException {
Client client = new Client();
client.init();
}
private void init() {
try {
while (true) {
System.out.print("> ");
String args = keyboardInput.readLine().trim().replaceAll("\\s+", " ");
clientOutput.println(args.toUpperCase());
// the loops doesn't seem to stop after reading all input lines
String line;
while ((line = clientInput.readLine()) != null) {
System.out.println(line);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class ClientHandler implements Runnable {
private final int id;
private final Socket clientSocket;
private final PrintWriter serverOutput;
private final BufferedReader serverInput;
// private final ClientFiles clientFiles;
public int getId() {
return id;
}
public ClientHandler(int id, Socket clientSocket) throws IOException {
this.id = id;
this.clientSocket = clientSocket;
this.serverInput = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));
this.serverOutput = new PrintWriter(this.clientSocket.getOutputStream(), true);
// this.clientFiles = new ClientFiles(id, clientSocket);
}
#Override
public void run() {
try {
String command;
while ((command = serverInput.readLine()) != null) {
executeCommand(command.split(" "));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
closeConnection();
removeFromHosts();
}
}
private void executeCommand(String[] input) throws IOException {
String command = input[0];
// String option = input[1];
switch (command) {
case "COMMANDS": {
getCommands();
break;
}
// case "LIST_LOCAL": {
// listFiles();
// break;
// }
// case "LIST_FILES": {
// listFiles(option);
// }
// case "PULL": {
// pull(clientSocket, option);
// }
// case "PUSH": {
// push(clientSocket, option);
// }
case "DISCONNECT": {
closeConnection();
break;
}
default: {
write("Invalid command.");
getCommands();
break;
}
}
}
private String read() throws IOException {
return serverInput.readLine();
}
private void write(String message) {
serverOutput.println(message);
}
private void getCommands() {
write("AVAILABLE COMMANDS:");
write("\tCOMMANDS");
write("\t\tlists available commands");
write("\tLIST_HOSTS");
write("\t\tlists hosts connected to server");
write("\tLIST_FILES");
write("\t\tlists files from all hosts connected server");
write("\tLIST_LOCAL");
write("\t\tlists local files");
write("\tLIST_FILES [HOSTS]...");
write("\t\tlists files from provided hosts connected server");
write("\tPULL [HOST] [FILE]");
write("\t\tdownloads file from host");
write("\tPUSH [HOST] [FILE]");
write("\t\tuploads file to host");
write("\tDISCONNECT");
write("\t\tdisconnects client from server");
}
private void closeConnection() {
try {
serverInput.close();
serverOutput.close();
clientSocket.close();
removeFromHosts();
} catch (IOException e) {
e.printStackTrace();
}
}
private void removeFromHosts() {
Server.getClients().remove(this);
}
}
Server just accepts new clients and starts new threads.
public class Server {
private static final AtomicInteger count = new AtomicInteger(1);
private static final ArrayList<ClientHandler> clients = new ArrayList<>();
public static void main(String[] args) {
Server.init();
}
public static void init() {
System.out.println("Opening server socket...");
try (
ServerSocket serverSocket = new ServerSocket(13370)
) {
System.out.println("Server socket opened at port: " + serverSocket.getLocalPort());
while (true) {
System.out.println("Waiting for client connection...");
Socket clientSocket = serverSocket.accept();
System.out.println("Client connected.");
ClientHandler client = new ClientHandler(count.getAndIncrement(), clientSocket);
clients.add(client);
System.out.println(clients);
new Thread(client, "client-" + client.getId()).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static ArrayList<ClientHandler> getClients() {
return clients;
}
}

your client is waiting for more lines.
In your code
while ((line = clientInput.readLine()) != null) {
System.out.println(line);
}
clientInput.readLine() will never return null if server does not close the connection. The execution stops waiting more data.
I suggest to implements a solution like insert an empty line or a special char to signal the client that the response to the command is finished.

Related

How to use existing Objects in an overwritten run() Method?

I'm a beginner in programming and do strugle a bit.
So I'm building a TCP peer to peer Chat and that requires me to divide the tasks -> Threads.
So I want to built a Thread for the "writing" part of that connection (Scanner, DataOutputStream etc.) In order to do that i implemented Runnable and that forces me to write my Thread in the overwritten run() method.
Now I have a bit of a problem, because in Order to send my messages out to the "other end (another client) I need the "socket.getOutputStream" but I cant use it in the run() method and i dont know how to fix this problem, sitting already a week on this problem. Any ideas ?
public class ClientHorcher implements Runnable {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(11111);
System.out.println("Waiting For Connection:-");
Socket socket = serverSocket.accept();
Scanner scanner = new Scanner(System.in);
DataInputStream datenRein = new DataInputStream(socket.getInputStream());
DataOutputStream datenRaus = new DataOutputStream(socket.getOutputStream());
String nickname;
System.out.print("Gib einen Nickname ein: ");
nickname = scanner.nextLine();
while (true) {
String vonMsg = datenRein.readUTF(in);
System.out.println("Client:-" + vonMsg);
if (vonMsg.equals("exit")) {
System.out.println("Beenden!!!");
datenRein.close();
datenRaus.close();
scanner.close();
socket.close();
serverSocket.close();
System.exit(0);
}
System.out.print(nickname + ":-");
String zuMsg = scanner.nextLine();
datenRaus.writeUTF(zuMsg);
if (zuMsg.equals("exit")) {
System.out.println("Quiting!!!");
datenRein.close();
datenRaus.close();
scanner.close();
socket.close();
serverSocket.close();
System.exit(0);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void run() {
}
}`
`public class ClientVerbinder implements Runnable {
public static void main(String[] args) {
try {
Socket socket = new Socket("Localhost", 11111);
System.out.println("Connected");
Scanner scanner = new Scanner(System.in);
DataInputStream datenRein = new DataInputStream(socket.getInputStream());
DataOutputStream datenRaus = new DataOutputStream(socket.getOutputStream());
String nickname;
System.out.print("Gib einen Nickname ein: ");
nickname = scanner.nextLine();
while (true) {
System.out.print(nickname+":-");
String zuMsg = scanner.nextLine();
datenRaus.writeUTF(zuMsg);
if (zuMsg.equals("exit")) {
System.out.println("Beenden!!!");
datenRein.close();
datenRaus.close();
scanner.close();
socket.close();
System.exit(0);
}
String vonMsg = datenRein.readUTF();
System.out.println("CLient"+":-" + vonMsg);
if (vonMsg.equals("exit")) {
System.out.println("Quiting!!!");
datenRein.close();
datenRaus.close();
scanner.close();
socket.close();
System.exit(0);
}
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void run() {
}
}
Multiple possibilities.
Use static variables. Normally you would not do that, but because your code is all-static, you could
Use member variables in an OO-style code.
If you wanted to do it properly, you'd split the server into two parts:
The ServerSocket listener that only listens to connections (Socket connectionToClient = ss.accept), and whenever a connection comes in, creates a new
ClientHandler, passes in the Socket, and the ClientHandler then starts its own thread internally
This way, the ClientHandler has all the data it needs (again as member variables) and can work on its own concerns (aka 'separation of concerns).
Update
This is what I created. Really simple.
Start Server
Start 1st client
Start 2nd client
In any client, type message
Other client receives it
Classes:
SimpleServer
package stackoverflow.simplemtserver;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingDeque;
public class SimpleServer { // make it closeable and close sockets if this is not standalone
public static void main(final String[] args) throws IOException {
final int port = 7623;
new SimpleServer(port);
}
final LinkedBlockingDeque<SimpleServerClientHandler> clientHandlers = new LinkedBlockingDeque<>();
private final ServerSocket mServerSocket;
public SimpleServer(final int pPort) throws IOException {
mServerSocket = new ServerSocket(pPort);
final Thread m = new Thread(() -> runLoop(), getClass().getSimpleName() + " Main Loop");
m.start();
}
private void runLoop() {
while (true) {
try {
System.out.println("Server waiting for connection...");
#SuppressWarnings("resource") final Socket cs = mServerSocket.accept(); // do NOT close the socket here, no try-resource, will interrupt threaded communication!
final SimpleServerClientHandler ch = new SimpleServerClientHandler(this, cs);
clientHandlers.add(ch);
System.out.println("Connection accepted, handler started. Handlers active: " + clientHandlers.size());
} catch (final IOException e) {
// handle this how you need it
e.printStackTrace();
}
}
}
public void signOffClientHandler(final SimpleServerClientHandler pClientHandler) {
clientHandlers.remove(pClientHandler); // we could also accommplish this using stack trace to avoid access from outside, but this is the easier solution
}
public void spreadMessageToClients(final String pMessageFromClient, final SimpleServerClientHandler pSimpleServerClientHandler) {
for (final SimpleServerClientHandler ch : clientHandlers) {
if (ch != pSimpleServerClientHandler) ch.relayMessageToClient(pMessageFromClient); // we can work with identity == and != here
}
}
}
SimpleServerClientHandler
package stackoverflow.simplemtserver;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class SimpleServerClientHandler {
private final SimpleServer mParentServer;
private final Socket mClientSocket;
private final DataInputStream mDIS;
private final DataOutputStream mDOS;
public SimpleServerClientHandler(final SimpleServer pSimpleServer, final Socket pCS) throws IOException {
mParentServer = pSimpleServer;
mClientSocket = pCS;
mDIS = new DataInputStream(mClientSocket.getInputStream());
mDOS = new DataOutputStream(mClientSocket.getOutputStream());
final Thread t = new Thread(() -> runComms(), getClass().getSimpleName() + " Comms Loop");
t.setDaemon(true); // threads now stop once server stops. this is NOT a soft exit
t.start();
}
private void runComms() {
try {
try {
while (true) {
// do all your logic here, work with DIS and DOS
final String messageFromClient = mDIS.readUTF();
if (messageFromClient == null) break;
if (!messageFromClient.startsWith("*")) mParentServer.spreadMessageToClients(messageFromClient, this);
}
} catch (final Exception e) {
// TODO: handle exception
}
} finally {
try {
mClientSocket.close(); // also closes DataIn/Out
} catch (final IOException e) { /* ignore */ }
mParentServer.signOffClientHandler(this);
}
}
public void relayMessageToClient(final String pMessageFromClient) {
try {
mDOS.writeUTF("*" + pMessageFromClient);
} catch (final IOException e) {
// ignore unless needed otherwise
}
}
}
SimpleClient
package stackoverflow.simplemtserver;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Scanner;
public class SimpleClient {
public static void main(final String[] args) throws UnknownHostException, IOException {
final String hostname = "localhost";
final int port = 7623;
try (final Socket s = new Socket(hostname, port);
final DataInputStream dis = new DataInputStream(s.getInputStream());
final DataOutputStream dos = new DataOutputStream(s.getOutputStream());
final Scanner scanner = new Scanner(System.in);) {
final Thread t = new Thread(() -> runListenerLoop(dis), SimpleClient.class.getSimpleName() + " Reader Thread");
t.setDaemon(true);
t.start();
while (true) {
System.out.println("Enter message:");
System.out.flush();
final String msg = scanner.nextLine();
if (msg == null) break;
System.out.println("Spreading message: " + msg);
dos.writeUTF(msg);
}
}
}
private static void runListenerLoop(final DataInputStream pDis) {
while (true) {
try {
System.out.println("Waiting for incoming messages...");
final String msg = pDis.readUTF();
System.out.println("Received: " + msg);
} catch (final SocketException e) {
// if ("java.net.SocketException: Connection reset".equals(e.getMessage()))
break;
} catch (final IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
From here on, there's now more ways you can extend this client/server communication:
use opcodes (read/write int constants or enums before data) to distinguish for special operations and/or messages (like this here puts an asterisk in front of the string to prevent re-re-laying the same message indefinitely over the network)
read multiple strings for user, message, additional options
implement this is a user friendly UI so you dont have to use console I/O
Update 2
The pure peer-to-peer solution is this one.
If the app is run without params, it goes into listening mode, waiting for a connection.
If the app is run with one arguments, it interprets it as listening port and also goes into listening mode.
If the app is un with [hostname] [port] arguments, it will try to connect there
Example:
start first app without arguments (listening)
start second app with arguments "localhost 7642" (connecting)
both apps will now
connect,
then set up I/O resources,
then start the listening thread for incoming messages
then go into the read-keyboard-and-write-to-socket loop
now you can type a message in one of the apps, the other one will receive it
p2p code:
package stackoverflow.simplemtserver;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Scanner;
public class SimplePeerToPeerClient {
static public final int DEFAULT_PORT = 7642;
public static void main(final String[] args) throws UnknownHostException, IOException {
if (args.length == 0) {
System.out.println("Waiting on default port " + DEFAULT_PORT);
waitForConnection(DEFAULT_PORT);
} else if (args.length == 1) {
final int port = Integer.parseInt(args[0]);
System.out.println("Waiting on port " + port);
waitForConnection(port);
} else if (args.length == 2) {
final String hostName = args[0];
final int port = Integer.parseInt(args[1]);
System.out.println("Connecting to " + hostName + " on port " + port);
connectToOtherSide(hostName, port);
} else throw new IllegalArgumentException("Invalid amount of argument! Need none (listen) or 2: [hostname] [port] (connect)");
}
private static void waitForConnection(final int pPort) throws IOException {
try (final ServerSocket ss = new ServerSocket(pPort);) {
#SuppressWarnings("resource") final Socket socket = ss.accept(); // will get closed later
startComms(socket);
} // closes ServerSocket after 1st connection
}
private static void connectToOtherSide(final String pHostName, final int pPort) throws UnknownHostException, IOException {
#SuppressWarnings("resource") final Socket socket = new Socket(pHostName, pPort); // will get closed later
startComms(socket);
}
private static void startComms(final Socket pSocket) throws IOException {
try (
final DataInputStream dis = new DataInputStream(pSocket.getInputStream());
final DataOutputStream dos = new DataOutputStream(pSocket.getOutputStream());
final Scanner scanner = new Scanner(System.in);) {
// run the listener loop
final Thread t = new Thread(() -> runListenerLoop(dis), SimpleClient.class.getSimpleName() + " Reader Thread");
t.setDaemon(true);
t.start();
// run my keyboard-input-send loop
while (true) {
System.out.println("Enter message:");
System.out.flush();
final String msg = scanner.nextLine();
if (msg == null) break; // empty input ends client
System.out.println("Spreading message: " + msg);
dos.writeUTF(msg);
}
} finally {
try {
pSocket.close();
} catch (final IOException e) { /* ignore */ }
}
}
private static void runListenerLoop(final DataInputStream pDis) {
while (true) {
try {
System.out.println("Waiting for incoming messages...");
final String msg = pDis.readUTF();
System.out.println("Received: " + msg);
} catch (final SocketException e) {
// if ("java.net.SocketException: Connection reset".equals(e.getMessage()))
break;
} catch (final IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

java chat application not working on other machines

I have made a basic chat application which runs fine on the same PC but I want it to work on different machines as well.
For the sake of simplicity, I have omitted the GUI code. I receive the IP, name, and port from a separate application which passes it to the client's constructor. I have entered the IP and port of the machine on which the server is running.
Client
public class ClientGui extends JFrame implements Runnable, KeyListener, ActionListener
{
private JPanel contentPane;
private Socket socket;
private String name , ip;
private int port;
private JTextField input;
private JTextArea console;
private JButton send;
private String message;
private DateFormat format;
private BufferedReader in;
private PrintWriter out;
private String reply;
public ClientGui(String name, String ip, int port)
{
format = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT);
this.name = name;
this.ip = ip;
this.port = port;
new Thread(this).start();
}
public void run()
{
try {
socket = new Socket(ip, port);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
while (true) {
out.println(name);
reply = in.readLine();
if (reply.equals("NAME")) {
name = JOptionPane.showInputDialog("Name taken , Enter another");
} else {
input.setEditable(true);
setTitle(name);
break;
}
}
while (true) {
reply = in.readLine();
if (reply.startsWith("MESSAGE")) {
log(reply.substring(7));
}
}
} catch (Exception e) {
try {
out.close();
in.close();
socket.close();
} catch (Exception e2) {
dispose();
System.exit(0);
}
dispose();
System.exit(0);
}
}
}
Server
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashSet;
public class Server implements Runnable
{
private static ArrayList<PrintWriter> writers;
private static HashSet<String> names;
private Socket socket;
private String name;
private String message;
public Server(Socket socket)
{
this.socket = socket;
}
public void run()
{
PrintWriter temp = null;
try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
try (PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
while (true) {
name = in.readLine();
if (names.contains(name)) {
out.println("NAME");
} else {
out.println("NAMEACCEPTED");
names.add(name);
break;
}
}
writers.add(out);
temp = out;
while(true) {
message = in.readLine();
for (PrintWriter writer : writers) {
writer.println("MESSAGE" + name + " : " + message);
}
}
}
} catch (Exception e) {
try {
names.remove(name);
writers.remove(temp);
socket.close();
} catch (Exception ignored) {
}
}
}
public static void main(String[] args)
{
writers = new ArrayList<>();
names = new HashSet<>();
ServerSocket server = null;
try {
int port = Integer.parseInt(args[0]);
server = new ServerSocket(port);
} catch (Exception e) {
System.out.println("Enter a valid port");
System.exit(0);
}
while (true) {
try {
System.out.println("Waiting for clients....");
new Thread(new Server(server.accept())).start();
System.out.println("Client Received");
} catch (Exception e) {
System.out.println("Server can't accept clients");
break;
}
}
}
}
I think it could be, because
1) you haven't installed the rigth jre-Version on both devices
2) you aren't in the same Network or you haven't forwarded you'r Ip
have you tried to use this in a Virtual box?
(client1 == vbox1, Server == Computer, client2 == vbox2)
hopefully, this helped
Sorry about the inconvenience to all
the problem was with the port i was using when i switched it , it worked on different machines
thanks for all your help

Java Socketing: My server class constantly takes in input but my client doesn't?

Try to do some concurrent messaging between the server and the client. When they first connect to eachother and the Server sends the test string, the client gets it perfectly fine the first time. And the client can SEND messages just fine to the Server. But my Client class cant constantly check for messages like my Server can and idk what's wrong. Any suggestions?
Server class code:
import java.lang.*;
import java.io.*;
import java.net.*;
import java.util.Random;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Server {
String testMessage = "You are now connected and can begin chatting!";
boolean connected = false;
int port;
public Server(int port) {
this.port = port;
}
public void Open() {
//creates Threadpool for multiple instances of chatting
final ExecutorService clientProcessingPool = Executors.newFixedThreadPool(10);
Runnable serverTask = new Runnable() {
#Override
public void run() {
try {
System.out.println("Opening...");
ServerSocket srvr = new ServerSocket(port);
while (true) {
Socket skt = srvr.accept();
clientProcessingPool.submit(new ClientTask(skt));
}
} catch (Exception e) {
try {
System.out.println(e);
System.out.print("You're opening too many servers in the same location, fool!\n");
ServerSocket srvr = new ServerSocket(port);
while (true) {
Socket skt = srvr.accept();
clientProcessingPool.submit(new ClientTask(skt));
}
} catch (IOException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
};
Thread serverThread = new Thread(serverTask);
serverThread.start();
}
private class ClientTask implements Runnable {
private final Socket skt;
private ClientTask(Socket skt) {
this.skt = skt;
}
#Override
public void run() {
//for sending messages
if (connected == false) {
System.out.println("======================");
System.out.println("Server has connected!");
processMessage(testMessage);
}
//for receiving messages
while (true) {
try {
// Read one line and output it
BufferedReader br = new BufferedReader(new InputStreamReader(skt.getInputStream()));
String incomingMessage = br.readLine();
if (incomingMessage != null) {
System.out.println("Server: Received message: " + incomingMessage);
processMessage(incomingMessage);
}
//br.close();
//skt.close(); //maybe delete
} catch (Exception e) {
System.out.println("Server had error receiving message.");
System.out.println("Error: " + e);
}
}
}
//for processing a message once it is received
public void processMessage(String message) {
PrintWriter out = null;
try {
out = new PrintWriter(skt.getOutputStream(), true);
} catch (IOException ex) {
System.out.println(ex);
System.out.println("Server had error sending message.");
}
System.out.print("Server: Sending message: " + message + "\n");
out.print(message);
out.flush();
connected = true;
try {
skt.shutdownOutput();
//out.close();
} catch (IOException ex) {
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
Client class code:
import java.lang.*;
import java.io.*;
import java.net.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
class Client {
public String message;
Socket skt;
public int port;
public Client(int port) {
this.port = port;
}
//for receiving messages from Server
public void receiveMessage() {
final ExecutorService clientProcessingPool = Executors.newFixedThreadPool(10);
Runnable serverTask = new Runnable() {
#Override
public void run() {
try {
skt = new Socket(InetAddress.getLocalHost().getHostName(), port);
while (true) {
clientProcessingPool.submit(new Client.ClientTask(skt));
}
} catch (IOException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}
};
Thread serverThread = new Thread(serverTask);
serverThread.start();
}
//for sending messages to Server
public void sendMessage(String outgoingMessage) throws IOException {
try {
skt = new Socket(InetAddress.getLocalHost().getHostName(), port);
PrintWriter pw = new PrintWriter(skt.getOutputStream());
System.out.println("Client: Sending message: " + outgoingMessage);
pw.print(outgoingMessage);
pw.flush();
skt.shutdownOutput();
//skt.close(); //maybe delete
} catch (Exception e) {
System.out.println(e);
System.out.print("Client had error sending message.\n");
JOptionPane.showMessageDialog(null, "That User is not currently online.", "ERROR!!", JOptionPane.INFORMATION_MESSAGE);
}
}
private class ClientTask implements Runnable {
private final Socket skt;
private ClientTask(Socket skt) {
this.skt = skt;
}
#Override
public void run() {
while (true) {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(skt.getInputStream()));
//while (!in.ready()) {}
String incomingMessage = in.readLine();
if (incomingMessage != null) {
System.out.println("Client: Received message: " + incomingMessage); // Read one line and output it
message = incomingMessage;
}
//skt.shutdownInput();
//in.close();
//skt.close(); //maybe delete
} catch (Exception e) {
System.out.print("Client had error receiving message.\n");
}
}
}
}
}
Streams cannot be re-wrapped. Once assigned to a wrapper, they must use that wrapper for the entire life-cycle of the stream. You also shouldn't close a stream until you are done using it, which in this case isn't until your client and server are done communicating.
In your current code, there are a few times where you re-initialize streams:
while (true) {
try {
//Each loop, this reader will attempt to re-wrap the input stream
BufferedReader br = new BufferedReader(new InputStreamReader(skt.getInputStream()));
String incomingMessage = br.readLine();
if (incomingMessage != null) {
System.out.println("Server: Received message: " + incomingMessage);
processMessage(incomingMessage);
}
//don't close your stream and socket so early!
br.close();
skt.close();
} catch (Exception e) {
//...
}
You get the idea; you can use this knowledge to find the stream problems in your client code as well.
With that said, servers are the middle-man between multiple clients. If you want to be able to type in the server's console to send a message to clients, it shouldn't go to only 1 client (unless you had a system that allowed you to specify a name). You need to store every connection in some kind of collection so when you type in the server's console, it goes to every client that's connected. This also helps when a client wants to send a message to every other client (global message). The server's main thread is primarily for accepting clients; I created another thread to allow you to type in the console.
As for your streams, you should create them whenever you start the ClientTask, both server side and client side:
public class Server {
private ExecutorService executor = Executors.newFixedThreadPool(10);
private Set<User> users = new HashSet<>();
private boolean running;
private int port;
public Server(int port) {
this.port = port;
}
public void start() {
running = true;
Runnable acceptor = () -> {
try(ServerSocket ss = new ServerSocket(port)) {
while(running) {
User client = new User(ss.accept());
users.add(client);
executor.execute(client);
}
} catch(IOException e) {
//if a server is already running on this port;
//if the port is not open;
e.printStackTrace();
}
};
Runnable userInputReader = () -> {
try(Scanner scanner = new Scanner(System.in)) {
while(running) {
String input = scanner.nextLine();
for(User user : users) {
user.send(input);
}
}
} catch(IOException e) {
//problem sending data;
e.printStackTrace();
}
};
Thread acceptorThread = new Thread(acceptor);
Thread userThread = new Thread(userInputReader);
acceptorThread.start();
userThread.start();
}
public void stop() {
running = false;
}
public static void main(String[] args) {
new Server(15180).start();
System.out.println("Server started!");
}
}
In the run() method is where the streams should be wrapped.
class User implements Runnable {
private Socket socket;
private boolean connected;
private DataOutputStream out; //so we can access from the #send(String) method
public User(Socket socket) {
this.socket = socket;
}
public void run() {
connected = true;
try(DataInputStream in = new DataInputStream(socket.getInputStream())) {
out = new DataOutputStream(socket.getOutputStream());
while(connected) {
String data = in.readUTF();
System.out.println("From client: "+data);
//send to all clients
}
} catch(IOException e) {
//if there's a problem initializing streams;
//if socket closes while attempting to read from it;
e.printStackTrace();
}
}
public void send(String message) throws IOException {
if(connected) {
out.writeUTF(message);
out.flush();
}
}
}
It's pretty much the same idea with the client:
1. Connect to Server
2. Create "communication" thread
3. Create "user input" thread (to receive input from console)
4. Start threads
public class Client {
private final String host;
private final int port;
private boolean connected;
private Socket socket;
public Client(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws IOException {
connected = true;
socket = new Socket(host, port);
Runnable serverInputReader = () -> {
try (DataInputStream in = new DataInputStream(socket.getInputStream())) {
while (connected) {
String data = in.readUTF();
System.out.println(data);
}
} catch (IOException e) {
// problem connecting to server; problem wrapping stream; problem receiving data from server;
e.printStackTrace();
}
};
Runnable userInputReader = () -> {
try (DataOutputStream out = new DataOutputStream(socket.getOutputStream());
Scanner scanner = new Scanner(System.in)) {
while (connected) {
String input = scanner.nextLine();
out.writeUTF(input);
}
} catch (IOException e) {
//problem wrapping stream; problem sending data;
e.printStackTrace();
}
};
Thread communicateThread = new Thread(serverInputReader);
Thread userThread = new Thread(userInputReader);
communicateThread.start();
userThread.start();
}
public static void main(String[] args) throws IOException {
new Client("localhost", 15180).start();
}
}
There are a few things I used in the code above that you may not be familiar with. They help simplify the syntax for your code:
Lambda Expressions - Prevents the need to create an anonymous class (or subclass) to declare a method
Try-With-Resources - Closes the resources specified automatically once the try block as ended
EDIT
When a user connects, you should store their connection by name or id. That way, you can send data to specific users. Even if your client is running on the same machine as the server, it's still the same idea: client connects to server, server sends message to client based on name or id:
while(running) {
User client = new User(ss.accept());
users.add(client); //add to set
executor.execute(client);
}
Right now, you are simply adding users to a Set. There is currently no way to grab a specific value from this set. What you need to do is give it some kind of "key". To give you an idea, here's an old algorithm I used to use. I have an array full of empty slots. When someone connects, I look for the first empty slot. Once an empty slot is found, I pass the user the index of the array it's being stored at (that will be the user's id), then store the user in the array at the specified index. When you need to send a message to someone, you can use the id to access that specific array index, grab the user you want and send a message:
class Server {
private int maxConnections = 10;
private ExecutorService executor = Executors.newFixedThreadPool(maxConnections);
private User[] users = new User[maxConnections];
//...
while(running) {
Socket socket = ss.accept();
for(int i = 0; i < users.length; i++) {
if(users[i] == null) {
users[i] = new User(socket, i);
executor.execute(users[i]);
break;
}
}
}
//...
public static void sendGlobalMessage(String message) throws IOException {
for(User user : users)
if(user != null)
user.send(message);
}
public static void sendPrivateMessage(String message, int id) {
User user = users[id];
if(user != null) {
user.send(message);
}
}
}
class User {
private Socket socket;
private int id;
private DataOutputStream out;
public User(Socket socket, int id) {
this.socket = socket;
this.id = id;
}
public void send(String message) throws IOException {
out.writeUTF(message);
out.flush();
}
public void run() {
DataInputStream in;
//wrap in and out streams
while(connected) {
String data = in.readUTF();
//Server.sendGlobalMessage(data);
//Server.sendPrivateMessage(data, ...);
sendMessage(data); //sends message back to client
}
}
}

How do i send the message to multiple clients in java Client-Server Program

Multiple Clients say(A, B, C, D etc) make connection to one server through same socket. They all send messages to server as and when required. Client messages are sent only to server(and not to other clients). But whenever server sends a message it should be delivered to all the clients. Please help me out jam only able to get server message on only 1 client
//MultithreadedServer.java
package server;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.Vector;
public class MultithreadedServer
{
Vector<ClientHandler> clients = new Vector<ClientHandler>();
Vector<String> users = new Vector<String>();
private static ServerSocket servSocket;
private static final int PORT = 1247;
public MultithreadedServer() throws IOException{
servSocket = new ServerSocket(PORT);
while(true) {
Socket client = servSocket.accept();
System.out.println("\nNew client accepted.\n");
ClientHandler handler;
handler = new ClientHandler(client);
clients.add(handler);
}
}
public static void main(String[] args) throws IOException
{
MultithreadedServer ms = new MultithreadedServer();
}
class ClientHandler extends Thread
{
private Socket client;
private BufferedReader in;
private PrintWriter out;
String name,message,response;
public ClientHandler(Socket socket)
{
//Set up reference to associated socket...
client = socket;
try
{
in = new BufferedReader(
new InputStreamReader(
client.getInputStream()));
out = new PrintWriter(
client.getOutputStream(),true);
}
catch(IOException e)
{
e.printStackTrace();
}
start();
}
public void sendMessage(String msg) {
System.out.println("is it even coming here?");
out.println("Server:" + msg);
}
public void boradcast(String message) {
// send message to all connected users
for (ClientHandler c : clients) {
c.out.println("Server: hello");
}
}
public String getUserName() {
return name;
}
public void run()
{
try
{
String received;
do
{ System.out.println("Enter Your Message: ");
String msg = in.readLine();
out.println(msg);
boradcast("testing");
received = in.readLine();
out.println("ECHO: " + received);
//Repeat above until 'QUIT' sent by client...
}while (!received.equals("QUIT"));
}
catch(IOException e)
{
e.printStackTrace();
}
finally
{
try
{
if (client!=null)
{
System.out.println(
"Closing down connection...");
client.close();
}
}catch(IOException e)
{
e.printStackTrace();
}
}
}
}}
//ClientProgram
import java.io.*;
import java.net.*;
public class Client
{
private static InetAddress host;
private static final int PORT = 1247;
private static Socket link;
private static BufferedReader in;
private static PrintWriter out;
private static BufferedReader keyboard;
public static void main(String[] args) throws IOException
{
try
{
host = InetAddress.getLocalHost();
link = new Socket(host, PORT);
in = new BufferedReader(
new InputStreamReader(
link.getInputStream()));
out = new PrintWriter(
link.getOutputStream(),true);
keyboard = new BufferedReader(
new InputStreamReader(System.in));
String message, response;
do
{
System.out.print(
"Enter message ('QUIT' to exit): ");
message = keyboard.readLine();
//Send message to server on
//the socket's output stream...
out.println(message);
//Accept response frm server on
//the socket's input stream...
response = in.readLine();
//Display server's response to user...
System.out.println(response);
}while (!message.equals("QUIT"));
}
catch(UnknownHostException uhEx)
{
System.out.println(
"\nHost ID not found!\n");
}
catch(IOException ioEx)
{
ioEx.printStackTrace();
}
finally
{
try
{
if (link!=null)
{
System.out.println(
"Closing down connection...");
link.close();
}
}
catch(IOException ioEx)
{
ioEx.printStackTrace();
}
}
}
}
An obvious way to do this would be to cycle through all ClientHandlers in clients and send the message to each of them:
for (ClientHandler ch : clients){
ch.sendMessage(message); //Or something
}

how to stop multithread chat client

I'm trying to create a multithread chat client-server using Java. I'm using this tutorial as a start: http://pguides.net/java-tutorial/java-tcp-clientserver-chat/
I want the client to stop when I enter the String "quit" but I don't understand how I could do it. Also, I need to remove the client from the connected nicks list when the client disconnects.
Server
/* ChatServer.java */
import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Hashtable;
public class ChatServer {
private static int port = 1001;
public static void main (String[] args) throws IOException {
ServerSocket server = null;
try {
server = new ServerSocket(port);
} catch (IOException e) {
System.err.println("Could not listen on port: " + port);
System.err.println(e);
System.exit(1);
}
Socket client = null;
while(true) {
try {
client = server.accept();
} catch (IOException e) {
System.err.println("Accept failed.");
System.err.println(e);
System.exit(1);
}
/* start a new thread to handle this client */
Thread t = new Thread(new ClientConn(client));
t.start();
}
}
}
class ChatServerProtocol {
private String nick;
private ClientConn conn;
private static Hashtable<String, ClientConn> nicks = new Hashtable<String, ClientConn>();
private static final String msg_OK = "OK";
private static final String msg_INVALID = "INVALID COMMAND";
private static final String msg_SEND_FAILED = "FAILED TO SEND";
private static boolean add_nick(String nick, ClientConn c) {
if (nicks.containsKey(nick)) {
return false;
} else {
nicks.put(nick, c);
return true;
}
}
private static boolean remove_nick(String nick, ClientConn c) {
if (!(nicks.containsKey(nick))) {
return false;
} else {
nicks.remove(nick);
return true;
}
}
public ChatServerProtocol(ClientConn c) throws IOException {
nick = null;
conn = c;
}
private boolean sendMsg(String recipient, String msg) {
if (nicks.containsKey(recipient)) {
ClientConn c = nicks.get(recipient);
c.sendMsg(nick + ": " + msg);
return true;
} else {
return false;
}
}
public String process(String msg) {
if (msg.startswith("Nick"){
String output = "";
if(add_nick(tryauthor, this.conn)) {
this.nick = tryauthor;
output = "Welcome "+tryauthor;
} else {
output = "Nick already in";
}
}
}
else if (msg.startsWith("msg")) {
String[] msg_parts = msg.split(":");
for(int i=0; i<msg_parts.length; i++){
System.out.println(msg_parts[i]);
}
String msg_type = msg_parts[0];
if(msg_type.equals("msg")) {
if(msg_parts.length < 3) output = msg_INVALID;
if(sendMsg(msg_parts[1], msg_parts[2])) output = msg_OK;
else output = msg_SEND_FAILED;
} else {
output = msg_INVALID;
}
}
return output;
}
}
class ClientConn implements Runnable {
private Socket client;
private BufferedReader in = null;
private PrintWriter out = null;
public ClientConn(Socket client) {
this.client = client;
try {
/* obtain an input stream to this client ... */
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
/* ... and an output stream to the same client */
out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
System.err.println(e);
return;
}
}
public void run() {
String msg, response;
try {
ChatServerProtocol protocol = new ChatServerProtocol(this);
/* loop reading lines from the client which are processed
* according to our protocol and the resulting response is
* sent back to the client */
while ((msg = in.readLine()) != "quit\r\n") {
response = protocol.process(msg);
out.println("SERVER: " + response);
}
this.close();
} catch (IOException e) {
System.err.println(e);
}
}
public void sendMsg(String msg) {
out.println(msg);
}
public void close() throws IOException {
in.close();
out.close();
client.close();
}
}
Client
/* ChatClient.java */
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class ChatClient {
private static int port = 1001;
private static String host = "localhost";
private static BufferedReader stdIn;
public static void main (String[] args) throws IOException {
Socket server = null;
try {
server = new Socket(host, port);
} catch (UnknownHostException e) {
System.err.println(e);
System.exit(1);
}
stdIn = new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(server.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
System.out.print("Nick: ");
String auth = stdIn.readLine();
out.println("Nick: " + auth);
String serverResponse = in.readLine();
System.out.println(serverResponse);
if (serverResponse.startsWith("SERVER: welcome")) {
/* create a thread to asyncronously read messages from the server */
ServerConn sc = new ServerConn(server);
Thread t = new Thread(sc);
t.start();
String msg;
/* loop reading messages from stdin and sending them to the server */
while ((msg = stdIn.readLine()) != "quit\r\n") {
out.println(msg);
}
sc.close();
System.out.println("Exit.");
System.out.println("---Client Error---");
}
else {
System.out.println("Exit.");
System.out.println("---Client Error---");
}
}
}
class ServerConn implements Runnable {
private BufferedReader in = null;
private Socket server;
public ServerConn(Socket s) throws IOException {
server = s;
in = new BufferedReader(new InputStreamReader(s.getInputStream()));
}
public void run() {
String msg;
try {
/* loop reading messages from the server and show them
* on stdout */
while ((msg = in.readLine()) != "quit\r\n") {
System.out.println(msg);
}
this.close();
} catch (IOException e) {
System.err.println(e);
}
}
public void close() throws IOException {
in.close();
server.close();
}
}
Where and how should I close the connection?
now the client can be authenticated only if his name is part of a graph I've created before.
This seems to be written in the code. In your Process() method, you look for the author in the authorsgraph and if it is not found you return an error. Is this not what you intended? If the author is not found, should you add them instead? Maybe the add_nice() call should be in the else if you haven't found them along with some way to add the author to the authorsgraph?
Walking through the handling of a new connection in a debugger might help you here. Liberal use of System.out.println() messages might also be of service.
I want the client to stop when I enter the String "quit" but I don't understand how I could do it.
Well this code has a bug. You must use .equals() to check for String equality. != is only testing the msg doesn't have the same String reference, not the same string contents:
// can't use != on a `String` to check contents
while ((msg = stdIn.readLine()) != "quit\r\n") {
It should be:
while (true) {
msg = stdIn.readLine();
if (msg == null || msg.equals("quit")) {
break;
}
...
Notice also that I am not checking the "\r\n". The readLine() method returns the string (to quote from the javadocs) "not including any line-termination characters". Also, you should test against null in case the socket is closed. You also have 2 places in your client code with the same bug.
I need to remove the client from the connected nicks list when the client disconnects.
Your while loop above will exit if the client socket is closed or they enter "quit". You then call close(). After you call close() you could call protocol.remove_nicks() to remove him from the loop. But you don't have the "nick" String there.
Really, maintaining the list of nicks inside of the ChatServerProtocol may not be the right place for it. I would roll the ChatServerProtocol into the ClientConn class myself since there is one of each per connection. Either way some object needs to store the "nick" string that the author logged in with and then call remove_nick() with that string after "quit" is entered or the connection closes.
Lastly, use of HashTable has been deprecated since Java 5. I would use Collections.synchronizedMap(new HashMap<...>()) instead.
Hope this helps.
I want the client to stop when I enter the String "quit" but I don't understand how I could do it.
You can terminate the current Java programm immediately with System.exit(status). As the operating system automatically releases all resources (such as tcp sockets) acquired by a process when it terminates, that also closes your connection.
Also, I need to remove the client from the connected nicks list when the client disconnects.
That's harder. Keep in mind that a disconnect can happen for reasons other than the client terminating, for instance because the network cable is unplugged. That is, the client can not reliably inform the server that it is no longer there, the server must detect this itself. I would expect the tcp implementation of the server operating system to detect that the client is no longer there (how quickly is probably implementation dependent), and throw an exception to the application using that socket, which you can catch and remove the nick from the list.

Categories