I made a Chat Application (Server/Client) using Java. Note: The server is ran as its own jar file and each client is ran as its own jar file.
Each client is on their own thread.
Whenever I send messages to the server, each client receives the message, however when I send messages from the client, only the server receives the message. When the client sends a message, I want all connected clients and the server to receive the message so all of the clients can communicate together and with the server as well.
I've looked at multiple posts and videos about this, but most were too confusing for me to understand.
Could someone please help me understand how I can send messages between threads? Thanks!
-- My Code --
Client:
public Client(User user, String address, int port) {
try {
socket = new Socket(address, port);
ClientApplicationUI app = new ClientApplicationUI();
app.setTitle("Chat Application - " + user.getUsername());
app.setVisible(true);
ServerConnection connection = new ServerConnection(socket, app);
output = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
new Thread(connection).start();
app.getButton().addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (app.getTextField().getText() != null && app.getTextField().getText().length() > 0) {
String message = MessageUtil.getMessage(Message.LOGGER_PREFIX) + " <" + user.getUsername() + "> " + app.getTextField().getText() + "\n";
try {
output.writeUTF(message);
output.flush();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
});
} catch (UnknownHostException e) {
System.out.println(e);
System.out.println("Could not connect! Reason: " + e);
} catch (IOException e) {
System.out.println("Could not connect! Reason: " + e);
}
}
ServerConnection
public class ServerConnection implements Runnable {
#SuppressWarnings("unused")
private Socket socket;
private DataInputStream in;
private ClientApplicationUI app;
public ServerConnection(Socket socket, ClientApplicationUI app) throws IOException {
this.socket = socket;
this.app = app;
in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
}
#Override
public void run() {
while (true) {
String message;
try {
message = in.readUTF();
app.logMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Server
public class Server {
private Socket socket = null;
private ServerSocket server = null;
private ExecutorService pool = Executors.newFixedThreadPool(4);
public Server (int port) {
try {
ApplicationUI app = new ApplicationUI();
app.setVisible(true);
server = new ServerSocket(port);
app.logMessage(MessageUtil.getMessage(Message.LOGGER_PREFIX) + " " + MessageUtil.getMessage(Message.INFO) + " Server started!\n");
app.logMessage(MessageUtil.getMessage(Message.LOGGER_PREFIX) + " " + MessageUtil.getMessage(Message.INFO) + " Waiting for new connections...\n");
while (true) {
socket = server.accept();
ConnectionHandler clientThread = new ConnectionHandler(socket, app);
app.logMessage(MessageUtil.getMessage(Message.LOGGER_PREFIX) + " " + MessageUtil.getMessage(Message.INFO) + " A new client has been accepted!\n");
pool.execute(clientThread);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server server = new Server(58139);
}
}
ConnectionHandler
public class ConnectionHandler implements Runnable {
private Socket client;
private ApplicationUI app;
private DataInputStream in;
private DataOutputStream out;
public ConnectionHandler(Socket client, ApplicationUI app) throws IOException {
this.client = client;
this.app = app;
in = new DataInputStream(new BufferedInputStream(client.getInputStream()));
out = new DataOutputStream(new BufferedOutputStream(client.getOutputStream()));
}
#Override
public void run() {
try {
app.getButton().addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (app.getTextField().getText() != null && app.getTextField().getText().length() > 0) {
String message = MessageUtil.getMessage(Message.LOGGER_PREFIX) + " <Server> " + app.getTextField().getText() + "\n";
try {
sendMessage(message);
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
});
String message = "";
while (!message.equals("/stop")) {
message = in.readUTF();
app.logMessage(message);
}
} catch (IOException e) {
System.err.println("IO exception in connection handler!");
System.err.println(e.getStackTrace());
} finally {
try {
out.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void sendMessage(String message) throws IOException {
out.writeUTF(message);
out.flush();
}
}
You need to understand, how sockets work. They are always Client and Server.
There are two ways you could achieve what you want:
First solution:
Send the message which is meant for all clients to the server and let the server distribute the message to all the other clients. The server will need to keep track of the already connected clients, i.e. store their Socket.
Second solution: (which totally is not advisable)
If you want to send a message to a client of a network without haveing the actual server involved, you will need that client to act as a server, or the other way around. This means that every client will actually need to listen to every other client, instead of only the server.
You should definitely go with the first solution!
Related
I wanted to ask how to change following code, which needs USB connection and WIFI to work... (and I don't know why wifi...), to code, which needs only USB cable and NO WIFI!, because I don't want to be dependent on wifi...
Could you please help me? Some changes or additions in code? Thanks.
Code for Android:
private final Runnable connectToServer = new Thread()
{
#Override
public void run()
{
try
{// Get the server address from a dialog box.
String serverAddress = "192.168.0.23";
// Make connection and initialize streams
Socket socket = new Socket(serverAddress, 38300);
in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// Consume the initial welcoming messages from the server
for (int i = 0; i < 3; i++) {
System.out.println(in.readLine());
}
solveCube();
} catch (IOException e) {
e.printStackTrace();
}
}
};
private final Runnable initializeConnection = new Thread()
{
#Override
public void run()
{
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(generateCubeString());
out.println(generateCubeString());
String response ="";
try {
response = in.readLine();
if (response == null || response.equals("")) {
System.exit(0);
}
} catch (IOException ex) {
}
if (response.contains("Error")) {
} else {
solveCubeAnimate(response);
}
System.out.println(response);
final String finalResponse = response;
runOnUiThread(new Runnable() {
#Override
public void run() {
textView.setText(finalResponse);
}
});
}
};
Code for PC
private static class Capitalizer extends Thread {
private Socket socket;
private int clientNumber;
public Capitalizer(Socket socket, int clientNumber) {
this.socket = socket;
this.clientNumber = clientNumber;
log("New connection with client# " + clientNumber + " at " + socket);
}
/**
* Services this thread's client by first sending the
* client a welcome message then repeatedly reading strings
* and sending back the capitalized version of the string.
*/
public void run() {
try {
// Decorate the streams so we can send characters
// and not just bytes. Ensure output is flushed
// after every newline.
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
// Send a welcome message to the client.
out.println("Hello, you are client #" + clientNumber + ".");
out.println("Enter a line with only a period to quit\n");
// Get messages from the client, line by line; return them
// capitalized
while (true) {
String input = in.readLine();
if (input == null || input.equals(".")) {
break;
}
out.println(solveCube(input));
}
} catch (IOException e) {
log("Error handling client# " + clientNumber + ": " + e);
} finally {
try {
socket.close();
} catch (IOException e) {
log("Couldn't close a socket, what's going on?");
}
log("Connection with client# " + clientNumber + " closed");
}
}
/**
* Logs a simple message. In this case we just write the
* message to the server applications standard output.
*/
private void log(String message) {
System.out.println(message);
}
}
private static class Connecter extends Thread {
/**
* Services this thread's client by first sending the
* client a welcome message then repeatedly reading strings
* and sending back the capitalized version of the string.
*/
public void run() {
try {
System.out.println("The capitalization server is running.");
int clientNumber = 0;
ServerSocket listener = new ServerSocket(38300);
try {
while (true) {
new Capitalizer(listener.accept(), clientNumber++).start();
}
} finally {
listener.close();
}
} catch (IOException ex) {
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
I am trying my hand at socket programming. I built a simple echo server that prints the client text on the screen and sends back a thank you message to the client. However when I run the client (which individually spawns 10000 requests in a loop) sometimes i get "connection refused" exceptions in some client threads. Sometimes all go through without any exception.
Following is my server code :
public class WebServer {
static int hitCount = 0;
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(7777, 10000);
while (true) {
Socket defaultSocket = serverSocket.accept();
new Thread(new ServerSlave(defaultSocket)).start();
System.out.println("Size is :" + hitCount);
}
}
}
class ServerSlave implements Runnable {
Socket clientSocket;
public ServerSlave(Socket socket) {
clientSocket = socket;
WebServer.hitCount++;
}
public void run() {
try {
DataInputStream inputStream = new DataInputStream(clientSocket.getInputStream());
DataOutputStream outputStream = new DataOutputStream(clientSocket.getOutputStream());
System.out.println(inputStream.readUTF());
outputStream.writeUTF("Thank you for contacting the web server");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Following is my client code :
public class Client {
static int excepCount=0;
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10000; i++) {
new Thread(new Worker("" + i)).start();
}
Thread.sleep(10000);
System.out.println( Client.excepCount);
}
}
class Worker implements Runnable {
String clientName;
public Worker(String name) {
clientName = name;
}
public void run() {
System.out.println("Process started for : " + clientName);
Socket socket = null;
try {
socket = new Socket("127.0.0.1", 7777);
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
outputStream.writeUTF("Hello socket. Client number " + clientName + "here");
InputStream inFromServer = socket.getInputStream();
DataInputStream in =
new DataInputStream(inFromServer);
System.out.println("Server says " + in.readUTF());
System.out.println("Closing socket");
} catch (IOException e) {
Client.excepCount++;
e.printStackTrace();
}finally{
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Not sure what I might be doing wrong. Any suggestions ?
You're expecting too much. It just isn't realistic for a client to form 10,000 connections at maximum speed. You're forgetting about the TIME_WAIT state, and the fact that there are only 64k minus several dozen client-side ports available. It isn't a realistic test. If you want to load-test your server you will need quite a few client hosts, or a longer interval between connections.
I'm writing a simple asynchronous tcp server & client program and I am curious if it's possible that the method "waitForConnections" misses a connection because it's still busy with accepting the new connection or start listening to it.
I tested it with 250 clients and I didn't notice a connection loss.
The code i used for testing:
for(int counter = 0; 250 > counter; counter++)
{
final int localCounter = counter;
new Thread(() -> {
try {
Socket socket = new Socket(ip, port);
System.out.println("Connected!");
DataOutputStream out =
new DataOutputStream(socket.getOutputStream());
out.writeUTF("#" + localCounter + " hello server!");
listenToConnection(socket);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
The server code:
public class Server extends Thread {
private ServerSocket serverSocket;
private final Integer port;
private int amountConnections = 0;
public Server(Integer port) {
this.port = port;
}
public void run() {
startServer(port);
}
private void startServer(Integer port) {
System.out.println("Server started!");
try {
serverSocket = new ServerSocket(port);
waitForConnections();
} catch (IOException e) {
e.printStackTrace();
}
}
private void waitForConnections() {
try {
Socket socket = serverSocket.accept();
System.out.println("New connection from: " + socket.getRemoteSocketAddress() + " - amount connections: " + amountConnections);
amountConnections++;
asyncListenToConnection(socket);
} catch (IOException e) {
e.printStackTrace();
}
finally{
waitForConnections();
}
}
// Creates a new thread for each connection to listen to
private void asyncListenToConnection(Socket socket) {
new Thread(() -> {
while (!socket.isClosed()) {
try {
DataInputStream in =
new DataInputStream(socket.getInputStream());
System.out.println("Connection: " + socket.getRemoteSocketAddress() + " says: " + in.readUTF());
} catch (IOException e) {
closeConnection(socket);
}
}
}).start();
}
private void closeConnection(Socket socket) {
if (!socket.isClosed() || socket.isConnected()) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
System.out.println("Connection: " + socket.getRemoteSocketAddress() + " has left");
}
}
}
I'm new to Java, threading and sockets so any tips are welcome including improving the code.
Since you call
serverSocket = new ServerSocket(port);
with no backlog parameter, according to the Oracle Documentation for ServerSocket at:
http://docs.oracle.com/javase/7/docs/api/java/net/ServerSocket.html#ServerSocket%28int%29
The maximum queue length for incoming connection indications (a
request to connect) is set to 50. If a connection indication arrives
when the queue is full, the connection is refused.
You can reduce the chances of this by increasing the backlog. If you also write the client code, you can have clients automatically retry after a connection refused.
This may be a stupid question, but here goes.
Im writing this chat program, where there is a server, and clients that can connect to it. I want to implement private messaging into the program, but I don't know how to get the clients to directly connect to eachother. For the server, I used a ServerSocket, which runs on a single port. To get that to work, I needed to forward a port to the server. Is there a way to get the clients to wait for connections, without forwarding a port to them?
Thanks
The whole point of TCP/IP is that a single client connects to a predefined port on a server. So yes, you'll also need to have a ServerSocket on the client that's going to accept the direct connection. You'll almost always run into trouble with port forwarding and the like, which is why UPnP was invented one day.
What you are trying to do is 'peer to peer' connectivity, aka P2P, which is always, by its very definition, plagued by firewalling problems. As such it's usually, especially for a chat, easier to use the central server as 'switchboard' server and relay the private messages as well.
I've written not long time ago a template for multiple client - server application, that might help you to solve your problem. The rest of your question was already answerd by #Niels, I think ;)
import java.net.*;
import java.io.*;
class ServeConnection extends Thread {
private Socket socket = null;
private BufferedReader in = null;
private PrintWriter out = null;
public ServeConnection(Socket s) throws IOException {
// init connection with client
socket = s;
try {
in = new BufferedReader(new InputStreamReader(
this.socket.getInputStream()));
out = new PrintWriter(this.socket.getOutputStream(), true);
} catch (IOException e) {
System.err.println("Couldn't get I/O.");
System.exit(1);
}
start();
}
public void run() {
System.out.println("client accepted from: " + socket.getInetAddress()
+ ":" + socket.getPort());
// get commands from client, until is he communicating or until no error
// occurs
String inputLine, outputLine;
try {
while ((inputLine = in.readLine()) != null) {
System.out.println("request: " + inputLine);
outputLine = inputLine;
out.println("I've recived "+outputLine);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("server ending");
out.close();
try {
in.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Server {
public static void svr_main(int port) throws IOException {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
} catch (IOException e) {
System.err.println("Could not listen on port: " + port);
System.exit(1);
}
System.out.println("Server ready");
try {
while (true) {
Socket socket = serverSocket.accept();
try {
new ServeConnection(socket);
} catch (IOException e) {
System.err.println("IO Exception");
}
}
} finally {
serverSocket.close();
}
}
}
class Client {
static Socket echoSocket = null;
static PrintWriter out = null;
static BufferedReader in = null;
public static void cli_main(int port, String servername) throws
IOException {
try {
echoSocket = new Socket(servername, port);
out = new PrintWriter(echoSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(
echoSocket.getInputStream()));
} catch (UnknownHostException e) {
System.err.println("Don't know about host: " + servername);
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't get I/O for " + servername);
System.exit(1);
}
System.out.println("Client ready!");
while (true) {
inputLine = (in.readLine().toString());
if (inputLine == null) {
System.out.println("Client closing!");
break;
}
// get the input and tokenize it
String[] tokens = inputLine.split(" ");
}
out.close();
in.close();
echoSocket.close();
System.out.println("Client closing");
}
}
public class MyClientServerSnippet{
public static void main(String[] args) throws IOException {
if (args.length == 0) {
System.err.println("Client: java snippet.MyClientServerSnippet<hostname> <port>");
System.err.println("Server: java snippet.MyClientServerSnippet<port>");
System.exit(1);
}
else if (args.length > 1) {
System.out.println("Starting client...\n");
Client client = new Client();
client.cli_main(3049, "127.0.0.1");
} else {
System.out.println("Starting server...\n");
Server server = new Server();
server.svr_main(3049);
}
}
}
I have a problem with my Java program. It has a socket connection between a server and many client. Here is the server (the part which concerns the problem):
private static ArrayList<ParallelServer> clientConnected = new ArrayList<ParallelServer>();
public Server(int port) {
this.port = port;
if (!startServer())
JOptionPane.showMessageDialog(new JFrame(""),
"Error!", "ERROR!",
JOptionPane.ERROR_MESSAGE);
}
private boolean startServer() {
try {
server = new ServerSocket(port);
loadDatabase();
} catch (IOException ex) {
ex.printStackTrace();
return false;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return true;
}
public void runServer() {
while (true) {
try {
client = server.accept();
ParallelServer pServer = new ParallelServer(client);
clientConnected.add(pServer);
Thread thread = new Thread(pServer);
thread.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
public static void sendBroadcast(String username) throws IOException {
for(int i = 0; i < clientConnected.size(); i++)
clientConnected.get(i).sendAnswer("#change," + username);
}
The parallel server is:
private Socket client;
private InputStreamReader inputstreamreader;
private BufferedReader bufferedreader;
private PrintWriter printwriter;
public ParallelServer(Socket client) {
this.client = client;
}
public void run() {
try {
inputstreamreader = new InputStreamReader(client.getInputStream());
bufferedreader = new BufferedReader(inputstreamreader);
printwriter = new PrintWriter(client.getOutputStream(), true);
String lineread = "";
while (client.isConnected()) {
lineread = bufferedreader.readLine();
doCommand(lineread);
}
} catch (UnknownHostException unhe) {
} catch (InterruptedIOException intioe) {
} catch (IOException ioe) {
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public void sendAnswer(String answer) throws IOException {
printwriter = new PrintWriter(client.getOutputStream(), true);
printwriter.println(answer);
printwriter.flush();
}
And here is the client:
private String serverurl = "localhost";
private int serverport = 7777;
private PrintWriter printwriter;
private InputStreamReader inputstreamreader;
private BufferedReader bufferedreader;
private Socket server;
public Client() {
server = null;
try {
server = new Socket(serverurl, serverport);
server.setSoTimeout(5000);
} catch (UnknownHostException unhe) {
System.out.println("UnknownHostException: " + unhe.getMessage());
} catch (InterruptedIOException intioe) {
System.out.println("Timeout while attempting to establish socket connection.");
} catch (IOException ioe) {
JOptionPane.showMessageDialog(new JFrame(),"Unable to reach the server!","ERROE!",JOptionPane.ERROR_MESSAGE);
}
}
public String sendCommand(String command) throws IOException {
if(server == null) {
try {
server = new Socket(serverurl, serverport);
server.setSoTimeout(5000);
} catch (UnknownHostException unhe) {
System.out.println("UnknownHostException: " + unhe.getMessage());
} catch (InterruptedIOException intioe) {
System.out.println("Timeout while attempting to establish socket connection.");
} catch (IOException ioe) {
JOptionPane.showMessageDialog(new JFrame(),"Unable to reach the server!","ERROR!",JOptionPane.ERROR_MESSAGE);
}
}
if(server != null) {
printwriter = new PrintWriter(server.getOutputStream(), true);
printwriter.println(command);
printwriter.flush();
inputstreamreader = new InputStreamReader(server.getInputStream());
bufferedreader = new BufferedReader(inputstreamreader);
return bufferedreader.readLine();
}
else
return "#serverProblem";
}
The program is a simple online game with turns. Players' turns are created with a queue and when a player passes his turn, the server send a broadcast message which say "Now it is 'Player 1' turn." (for instance). My problem is that when a client receive the message, its like it add the answer "Now it is 'Player 1' turn." to the next message it will receive. In my case: when a player passes his turn, he sends "#passTurn,username". The ParallelServer class polls it from the queue, puts it at the bottom of the queue, sends the client "#ok" to tell it that the turn has changed successfully and tells the Server class to send the broadcast message. Then, when the same client will try do do a further action, it will consider "Now it is 'Player 1' turn." as the answer the server has given to it. Instead, I would like that the server and the clients work as always and when the broadcast message is cought, the client is notified without any collateral effect.
What can I do?
Thanks.
Your bi-directional message passing mechanism should look something like this:
Server:
Wait on any client InputStream
if (broadcast)
broadcast_message()
else
process_message()
Client:
Receiving Thread:
Wait on server broadcast
Sending Thread:
Wait on messages to be sent to server from the User Input
This should do the trick :)
Hope it helps. Cheers!