Java - Send Message to all Clients - java

I'm creating a chat server using Socket Servers and Thread. I have 3 Classes in my Server ChatServer.java, ThreadedServer.java, and Main.java. My Client has 2 ChatClient.java, Main.java. I need to make it so when a message is sent in from a client to server, the server sends out the message to all Clients.
ChatServer.java
package server;
import java.io.*;
import java.net.*;
public class ChatServer {
protected Socket s;
protected Socket ss;
public ChatServer() {
try {
ServerSocket ss=new ServerSocket(6969);
Runtime.getRuntime().addShutdownHook(new Thread() {
public void main(String []args) throws IOException {
ss.close();
}
});
while (true) {
Socket s =ss.accept();
new ThreadedServer(s).start();
}
}catch(Exception e) {
System.out.println(e);
}
new ThreadedServer(s).start();
}
}
ThreadedServer.java
package server;
import java.io.*;
import java.net.*;
public class ThreadedServer extends Thread{
protected Socket socket;
public ThreadedServer(Socket clientSocket) {
this.socket = clientSocket;
}
public void run() {
try {
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
while (true) {
String str=(String)dis.readUTF();
String remote = socket.getInetAddress().toString();
String newmes = remote + ": " + str;
dos.writeUTF(newmes);
System.out.println(newmes);
if (str.toUpperCase() == "QUIT") {
socket.close();
break;
}else if (str.toUpperCase() == "EXIT") {
socket.close();
break;
}
}
}catch(Exception e) {
System.out.println(e);
}
}
}
Main.java (Server)
package server;
public class Main {
public static void main(String[] args) {
ChatServer chat = new ChatServer();
}
}
Here are the Client.java's
ChatClient.java
package client;
import java.io.*;
import java.net.*;
public class ChatClient {
public ChatClient() {
try {
Socket s = new Socket("10.4.27.29",6969);
Runtime.getRuntime().addShutdownHook(new Thread() {
public void main(String []args) throws IOException {
s.close();
}
});
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
DataInputStream dis = new DataInputStream(s.getInputStream());
while (true) {
String message = System.console().readLine();
dos.writeUTF(message);
System.out.println(dis.readUTF());
}
}catch(Exception e) {
System.out.println(e);
}
}
}
Main.java (Client)
package client;
public class Main {
public static void main(String[] args) {
ChatClient chat = new ChatClient();
}
}
Please any help would be amazing.
Also any ideas to make this nicer would be appreciated. Thanks!!

Try running this. This is one I made that works.
ChatServer.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
/**
* A multithreaded chat room server. When a client connects the
* server requests a screen name by sending the client the
* text "SUBMITNAME", and keeps requesting a name until
* a unique one is received. After a client submits a unique
* name, the server acknowledges with "NAMEACCEPTED". Then
* all messages from that client will be broadcast to all other
* clients that have submitted a unique screen name. The
* broadcast messages are prefixed with "MESSAGE ".
*
* Because this is just a teaching example to illustrate a simple
* chat server, there are a few features that have been left out.
* Two are very useful and belong in production code:
*
* 1. The protocol should be enhanced so that the client can
* send clean disconnect messages to the server.
*
* 2. The server should do some logging.
*/
public class ChatServer {
/**
* The port that the server listens on.
*/
private static final int PORT = 9001;
/**
* The set of all names of clients in the chat room. Maintained
* so that we can check that new clients are not registering name
* already in use.
*/
private static HashSet<String> names = new HashSet<String>();
/**
* The set of all the print writers for all the clients. This
* set is kept so we can easily broadcast messages.
*/
private static HashSet<PrintWriter> writers = new HashSet<PrintWriter>();
/**
* The appplication main method, which just listens on a port and
* spawns handler threads.
*/
public static void main(String[] args) throws Exception {
System.out.println("The chat server is running.");
ServerSocket listener = new ServerSocket(PORT);
try {
while (true) {
new Handler(listener.accept()).start();
}
} finally {
listener.close();
}
}
/**
* A handler thread class. Handlers are spawned from the listening
* loop and are responsible for a dealing with a single client
* and broadcasting its messages.
*/
private static class Handler extends Thread {
private String name;
private Socket socket;
private BufferedReader in;
private PrintWriter out;
/**
* Constructs a handler thread, squirreling away the socket.
* All the interesting work is done in the run method.
*/
public Handler(Socket socket) {
this.socket = socket;
}
/**
* Services this thread's client by repeatedly requesting a
* screen name until a unique one has been submitted, then
* acknowledges the name and registers the output stream for
* the client in a global set, then repeatedly gets inputs and
* broadcasts them.
*/
public void run() {
try {
// Create character streams for the socket.
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// Request a name from this client. Keep requesting until
// a name is submitted that is not already used. Note that
// checking for the existence of a name and adding the name
// must be done while locking the set of names.
while (true) {
out.println("SUBMITNAME");
name = in.readLine();
if (name == null) {
return;
}
synchronized (names) {
if (!names.contains(name)) {
names.add(name);
break;
}
}
}
// Now that a successful name has been chosen, add the
// socket's print writer to the set of all writers so
// this client can receive broadcast messages.
out.println("NAMEACCEPTED");
writers.add(out);
// Accept messages from this client and broadcast them.
// Ignore other clients that cannot be broadcasted to.
while (true) {
String input = in.readLine();
if (input == null) {
return;
}
for (PrintWriter writer : writers) {
writer.println("MESSAGE " + name + ": " + input);
}
}
} catch (IOException e) {
System.out.println(e);
} finally {
// This client is going down! Remove its name and its print
// writer from the sets, and close its socket.
if (name != null) {
names.remove(name);
}
if (out != null) {
writers.remove(out);
}
try {
socket.close();
} catch (IOException e) {
}
}
}
}
}
ChatClient.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/**
* A simple Swing-based client for the chat server. Graphically
* it is a frame with a text field for entering messages and a
* textarea to see the whole dialog.
*
* The client follows the Chat Protocol which is as follows.
* When the server sends "SUBMITNAME" the client replies with the
* desired screen name. The server will keep sending "SUBMITNAME"
* requests as long as the client submits screen names that are
* already in use. When the server sends a line beginning
* with "NAMEACCEPTED" the client is now allowed to start
* sending the server arbitrary strings to be broadcast to all
* chatters connected to the server. When the server sends a
* line beginning with "MESSAGE " then all characters following
* this string should be displayed in its message area.
*/
public class ChatClient {
BufferedReader in;
PrintWriter out;
JFrame frame = new JFrame("Chatter");
JTextField textField = new JTextField(40);
JTextArea messageArea = new JTextArea(8, 40);
/**
* Constructs the client by laying out the GUI and registering a
* listener with the textfield so that pressing Return in the
* listener sends the textfield contents to the server. Note
* however that the textfield is initially NOT editable, and
* only becomes editable AFTER the client receives the NAMEACCEPTED
* message from the server.
*/
public ChatClient() {
// Layout GUI
textField.setEditable(false);
messageArea.setEditable(false);
frame.getContentPane().add(textField, "North");
frame.getContentPane().add(new JScrollPane(messageArea), "Center");
frame.pack();
// Add Listeners
textField.addActionListener(new ActionListener() {
/**
* Responds to pressing the enter key in the textfield by sending
* the contents of the text field to the server. Then clear
* the text area in preparation for the next message.
*/
public void actionPerformed(ActionEvent e) {
out.println(textField.getText());
textField.setText("");
}
});
}
/**
* Prompt for and return the address of the server.
*/
private String getServerAddress() {
return JOptionPane.showInputDialog(
frame,
"Enter IP Address of the Server:",
"Welcome to the Chatter",
JOptionPane.QUESTION_MESSAGE);
}
/**
* Prompt for and return the desired screen name.
*/
private String getName() {
return JOptionPane.showInputDialog(
frame,
"Choose a screen name:",
"Screen name selection",
JOptionPane.PLAIN_MESSAGE);
}
/**
* Connects to the server then enters the processing loop.
*/
private void run() throws IOException {
// Make connection and initialize streams
String serverAddress = getServerAddress();
Socket socket = new Socket(serverAddress, 9001);
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// Process all messages from server, according to the protocol.
while (true) {
String line = in.readLine();
if (line.startsWith("SUBMITNAME")) {
out.println(getName());
} else if (line.startsWith("NAMEACCEPTED")) {
textField.setEditable(true);
} else if (line.startsWith("MESSAGE")) {
messageArea.append(line.substring(8) + "\n");
}
}
}
/**
* Runs the client as an application with a closeable frame.
*/
public static void main(String[] args) throws Exception {
ChatClient client = new ChatClient();
client.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
client.frame.setVisible(true);
client.run();
}
}

Related

Chat Program: Client to Server Connectivity Issue

Project Summary
Goal: A simple application that connects a client to a server hosted on one computer and allows that client from another computer to send chat messages to everyone on that specified server.
Class ClientWindow: Runs an application that prompts the user to enter in a server ID and port. Once entered, it starts the chat program.
Problem:
For some reason unknown to me, the chat program ONLY works when connecting to a server that you've created on your own computer. Other clients from other computers cannot connect to that server.
Possible reasons for problem:
I might have incorrectly constructed the server's receiving/sending sockets
I might have incorrectly constructed the client's receiving/sending sockets
Something is getting wonky in the packets being sent between client and server
Somehow IP addresses for the sockets are being defined incorrectly
Here is the code for the chat program.
Start server
This class starts the server on the specified port. (12348 in this instance)
package server;
public class ChatServer {
public static void main(String[] args) {
// TODO Auto-generated method stub
Server.start(12348);
}
}
Server
This is the class that creates the server and listens for client connections and messages.
package server;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;
public class Server {
private static DatagramSocket socket;
private static boolean running;
private static int ClientID; // Ensures that no two users have the same client ID
private static ArrayList<ClientInfo> clients = new ArrayList<ClientInfo>();
public static void start(int port) {
try {
socket = new DatagramSocket(port);
System.out.println("Application Starting...");
running = true;
listen();
System.out.println("Server started on port: " + port);
} catch (Exception e) {
System.out.println("Failed somewhere");
e.printStackTrace();
}
}
private static void broadcast(String message) {
for (ClientInfo info : clients) {
send(message, info.getAddress(), info.getPort());
}
}
private static void send(String message, InetAddress address, int port) {
try {
message += "\\e";
byte[] data = message.getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
socket.send(packet);
System.out.println("Sent message to," + address.getHostAddress() + ":" + port);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void listen() {
Thread listenThread = new Thread("ChatProgram Listener Thread") {
public void run() {
try {
while(running) {
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
socket.receive(packet);
String message = new String(data);
//Identify where the end of the message is
message = message.substring(0, message.indexOf("\\e"));
//Manage message
if(!isCommand(message, packet)) {
broadcast(message);
}
}
} catch(Exception e){
e.printStackTrace();
}
}
}; listenThread.start();
}
/*
* SERVER COMMAND LIST
* \con:[name] -> Connects client to server
* \dis:[id] -> Disconnects client from server
*
*
*
*/
private static boolean isCommand(String message, DatagramPacket packet) {
if (message.startsWith("\\con:")) {
// RUN CONNECTION CODE
String name = message.substring(message.indexOf(":") + 1);
clients.add(new ClientInfo(name, ClientID++, packet.getAddress(), packet.getPort()));
broadcast("User " + name + " Connected!");
return true;
}
if (message.startsWith("\\dis:")) {
// RUN DISCONNECTION CODE
return true;
}
return false;
}
public static void stop() {
running = false;
}
}
Start Client
Starts the client application.
package client;
import java.awt.EventQueue;
import java.awt.Frame;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTextField;
import java.awt.FlowLayout;
import java.awt.event.ActionListener;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.awt.event.ActionEvent;
public class ClientWindow {
private JFrame frmChatProgram;
private JTextField textField;
private static JTextArea textArea = new JTextArea();
private Client client;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
ClientWindow window = new ClientWindow();
window.frmChatProgram.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public ClientWindow() throws UnknownHostException {
initialize();
String name = JOptionPane.showInputDialog("Enter name:");
String servIp = JOptionPane.showInputDialog("Enter server IP address:");
String port = JOptionPane.showInputDialog("Enter server port:");
int parsedPort = Integer.parseInt(port);
//InetAddress inetAddress = InetAddress.getLocalHost();
client = new Client(name, servIp, parsedPort);
}
private void initialize() {
frmChatProgram = new JFrame();
frmChatProgram.setResizable(false);
frmChatProgram.setTitle("Chat Program");
frmChatProgram.setBounds(100, 100, 620, 446);
frmChatProgram.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frmChatProgram.setBackground(Color.WHITE);
frmChatProgram.getContentPane().setBackground(Color.DARK_GRAY);
frmChatProgram.getContentPane().setLayout(new BorderLayout(0, 0));
textArea.setForeground(Color.WHITE);
textArea.setBackground(Color.DARK_GRAY);
JScrollPane scrollPane = new JScrollPane(textArea);
frmChatProgram.getContentPane().add(scrollPane, BorderLayout.CENTER);
JPanel panel = new JPanel();
panel.setBackground(Color.GRAY);
frmChatProgram.getContentPane().add(panel, BorderLayout.SOUTH);
panel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
textField = new JTextField();
textField.setBackground(Color.WHITE);
panel.add(textField);
textField.setColumns(40);
JButton btnNewButton = new JButton("Send");
btnNewButton.addActionListener(e ->{
if (!textField.getText().equals("")) {
client.send(textField.getText());
textField.setText("");
}
});
panel.add(btnNewButton);
frmChatProgram.setLocationRelativeTo(null);
}
public static void printToConsole(String message) {
textArea.setText(textArea.getText() + message + "\n");
}
}
Client
Creates a client connection and listener for that client
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import server.ClientInfo;
public class Client {
private DatagramSocket socket;
private InetAddress address;
private int port;
private boolean running;
private String name;
public Client(String name, String address, int port) {
try {
this.name = name;
this.address = InetAddress.getByName(address);
this.port = port;
socket = new DatagramSocket();
socket.connect(this.address, port);
running = true;
listen();
send("\\con:" + name);
} catch (Exception e) {
e.printStackTrace();
}
}
public void send(String message) {
try {
if (!message.startsWith("\\")) {
message = name+": "+message;
}
message += "\\e";
byte[] data = message.getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
socket.send(packet);
System.out.println("Sent message to," + address.getHostAddress() + ":" + port);
} catch (Exception e) {
e.printStackTrace();
}
}
private void listen() {
Thread listenThread = new Thread("ChatProgram Listener Thread") {
public void run() {
try {
while (running) {
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
socket.receive(packet);
String message = new String(data);
// Identify where the end of the message is
message = message.substring(0, message.indexOf("\\e"));
// Manage message
if (!isCommand(message, packet)) {
ClientWindow.printToConsole(message);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}; listenThread.start();
}
private static boolean isCommand(String message, DatagramPacket packet) {
if (message.startsWith("\\con:")) {
// RUN CONNECTION CODE
return true;
}
if (message.startsWith("\\dis:")) {
// RUN DISCONNECTION CODE
return true;
}
return false;
}
}
I assume that you are asking about the possible reasons that your code is not be working:
1) I might have incorrectly constructed the server's receiving/sending sockets.
That is possible. You should be able to tell that by using a debugger to examine the packets on the server side.
2) I might have incorrectly constructed the client's receiving/sending sockets
Ditto ...
3) Something is getting wonky in the packets being sent between client and server
This is the most likely:
It could be a packet-level routing problem ... though that is likely to have broader effects.
It could be firewall issue
It could be an MTU issue
It could even be a broken switch, etc.
4) Somehow IP addresses for the sockets are being defined incorrectly
That is possible.
There are various things you could do to diagnose / eliminate the various possibilities. But this is not really a StackOverflow problem.

How do I set up my Java chatroom program to work with port forwarding?

I watched a tutorial on simple Java networking, and the tutorial showed the server and client application running on the same computer and it worked, I was wondering if there's a way to make it work on different computers in different homes using port forwarding or something else; Here is my code:
Server.java:
package com.cloud.server;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
/**
* The Server class extends JFrame and contains all of the code pertaining to the GUI and the server
*
* #author mcjcloud
*
*/
public class Server extends JFrame
{
private JTextField userInput;
private JTextArea convo;
private ObjectOutputStream output;
private ObjectInputStream input;
private ServerSocket server; // establishes server
private Socket connection; // establishes connection with other computer
/**
* Constructor (basically just sets up the GUI and actionListener(s)
*/
public Server()
{
super("Cloud Messenger");
userInput = new JTextField();
userInput.setEditable(false);
userInput.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
sendMessage(e.getActionCommand());
userInput.setText("");
}
});
add(userInput, BorderLayout.SOUTH);
// set up convo (JTextArea)
convo = new JTextArea();
add(new JScrollPane(convo));
setSize(500, 700);
setLocationRelativeTo(null);
setVisible(true);
}
/**
* startServer() method waits for connection, sets up connection and manages chat
*/
public void startServer()
{
try
{
server = new ServerSocket(6789, 100, InetAddress.getByName("0.0.0.0")); // (port number, backlog) backlog (aka qlength) = "how many people can connect at a time"
while(true) // infinite loop
{
try
{
waitForConnection(); // first set up the connection
setupStreams(); // set up the streams
chat(); // enable the chat and things
}
catch(EOFException eofe) // EOF = EndOfStream (meaning the input/output stream ended)
{
showMessage("Connection terminated.");
}
finally
{
cleanUpConnection();
}
}
}
catch(IOException io)
{
io.printStackTrace();
}
}
/**
* waitForConnection() method will wait for the connection, then display connection info
*
* #throws IOException
*/
private void waitForConnection() throws IOException
{
showMessage("Waiting for connection...");
connection = server.accept(); // listens for a connection
showMessage("Now connected to " + connection.getInetAddress().getHostName());
}
/**
* setupStream() method gets a stream to send/recieve data
*/
private void setupStreams() throws IOException
{
// setup output stream
output = new ObjectOutputStream(connection.getOutputStream()); // create pathway to allow us to connect to the computer the socket is connected to
output.flush();
// setup input stream
input = new ObjectInputStream(connection.getInputStream()); // create pathway to receive messages
showMessage("Stream setup success.");
}
/**
* chat() method code runs during conversation
*/
private void chat() throws IOException
{
String message = "Chatting enabled";
showMessage(message);
setCanType(true);
do
{
try
{
message = (String) input.readObject();
showMessage(message);
}
catch(ClassNotFoundException cnfe)
{
showMessage("Message recieve failed (Other person's problem)");
}
}
while(!message.equals("CLIENT - /terminate"));
}
/**
* cleanUpConnection() method cleans up the stream and things after the chat has ended
*/
private void cleanUpConnection()
{
showMessage("Closing connection...");
setCanType(false);
try
{
output.close();
input.close();
connection.close();
}
catch(IOException io)
{
io.printStackTrace();
}
}
/**
* sendMessage(String) method sends whatever message you type
*
* #param message is what is going to be shown
*/
private void sendMessage(String message)
{
try
{
output.writeObject("SERVER - " + message); // write the message to the outputstream
output.flush();
showMessage("SERVER - " + message);
}
catch(IOException io)
{
convo.append("ERROR: Message can't be sent.");
}
}
/**
* showMessage(String)shows whatever needs to be shown on the JTextArea
*/
private void showMessage(String message)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
convo.append(" " + message + "\n");
}
});
}
/**
* setCanType() method decides whether or not a user can type
*
* #param canType
*/
private void setCanType(boolean canType)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
userInput.setEditable(canType);
}
});
}
}
InvokeServer.java:
package com.cloud.server;
import javax.swing.JFrame;
public class InvokeServer
{
public static void main(String[] args)
{
Server server = new Server();
server.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
server.startServer();
}
}
Client.java:
package com.cloud.client;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
/**
* Client class is the sister of the Server class in the server application, contains all the code to build GUI and send info to server and receive
*
* #author mcjcloud
*
*/
public class Client extends JFrame
{
private JTextField userInput;
private JTextArea convo;
private ObjectOutputStream output;
private ObjectInputStream input;
private String message = "";
private String serverIP; // connecting to a specific server
private Socket connection;
public Client(String host)
{
super("Cloud Messenger");
serverIP = host;
userInput = new JTextField();
userInput.setEditable(false);
userInput.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
sendMessage(e.getActionCommand());
userInput.setText("");
}
});
add(userInput, BorderLayout.SOUTH);
convo = new JTextArea();
add(new JScrollPane(convo));
setSize(500, 700);
setLocationRelativeTo(null);
setVisible(true);
}
/**
* startClient() method invokes the whole conversation
*/
public void startClient()
{
try
{
boolean connected = false;
showMessage("Connecting to server...");
while(!connected)
{
try
{
connected = connectToServer();
}
catch(ConnectException ce)
{
// do nothing
}
}
setupStreams();
chat();
}
catch(EOFException eofe)
{
sendMessage("Connection terminated.");
}
catch(IOException ioe)
{
ioe.printStackTrace();
}
finally
{
cleanUpConnection();
}
}
/**
* connectToServer() establishes connection with the server application
*
* #throws IOException
*/
private boolean connectToServer() throws IOException
{
connection = new Socket(InetAddress.getByName(serverIP), 6789);
showMessage("Now connected to " + connection.getInetAddress().getHostName());
return true;
}
/**
* setupStream() method gets a stream to send/recieve data
*/
private void setupStreams() throws IOException
{
// setup output stream
output = new ObjectOutputStream(connection.getOutputStream()); // create pathway to allow us to connect to the computer the socket is connected to
output.flush();
// setup input stream
input = new ObjectInputStream(connection.getInputStream()); // create pathway to receive messages
showMessage("Stream setup success.");
}
/**
* chat() method code runs during conversation
*/
private void chat() throws IOException
{
String message = "Chatting enabled";
showMessage(message);
setCanType(true);
do
{
try
{
message = (String) input.readObject();
showMessage(message);
}
catch(ClassNotFoundException cnfe)
{
showMessage("Message recieve failed (Other person's problem)");
}
}
while(!message.equals("SERVER - /terminate"));
}
/**
* cleanUpConnection() method cleans up the stream and things after the chat has ended
*/
private void cleanUpConnection()
{
showMessage("Closing connection...");
setCanType(false);
try
{
output.close();
input.close();
connection.close();
}
catch(IOException io)
{
io.printStackTrace();
}
}
/**
* sendMessage(String) method sends whatever message you type
*
* #param message is what is going to be shown
*/
private void sendMessage(String message)
{
try
{
output.writeObject("CLIENT - " + message); // write the message to the outputstream
output.flush();
showMessage("CLIENT - " + message);
}
catch(IOException io)
{
convo.append("ERROR: Message can't be sent.");
}
}
/**
* showMessage(String) shows whatever needs to be shown on the JTextArea
*/
private void showMessage(String message)
{
SwingUtilities.invokeLater(new Runnable() // USE THIS RUNNABLE TO UPDATE GUI
{
public void run()
{
convo.append(" " + message + "\n");
}
});
}
/**
* setCanType() method decides whether or not a user can type
*
* #param canType
*/
private void setCanType(boolean canType)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
userInput.setEditable(canType);
}
});
}
}
InvokeClient.java:
package com.cloud.client;
import javax.swing.JFrame;
public class InvokeClient
{
public static void main(String[] args)
{
Client client = new Client("99.25.233.116");
client.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
client.startClient();
}
}
Note:
I tried setting up port forwarding on my home network with the port 6789 to one of my laptops, and that's the laptop I run the server application on.
I solved it. I just undid and redid the port forwarding again and it worked with the public IP address. Thank you all for your help

Creating a Multi-threaded Java Server Chat App.

I am trying to implement a multithreaded server chat application in Java.
This program created a thread and waits for a client to connect. Once a client is connected, it creates another thread and waits for another client to connect.
This is my ChatServer.java
package com.chat.server;
import java.io.InputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.InetAddress;
import java.net.ServerSocket;
/**
* <p>The chat server program</p>
* This class is a Thread that recieves connection
* from different clients and handles them is separate
* Thread.
*
* #author Aditya R.Singh
* #version 1.0.0
* #since 1.0.0
*/
class ChatServer extends Thread {
private int port; // The port number to listen at.
private String ip; // To store the IP address.
private Socket socket; // Socket connection with different clients.
private InetAddress inet; // Handling Client Address.
private ServerSocket serverSocket; // The server socket used by clients to connect.
/**
* This is solely intended for instantiation purpose.
*
* #param PORT - The port number to listen for client requests
*/
ChatServer(final int PORT) {
/* Initiallizing all instance variables to null. */
ip = null;
inet = null;
socket = null;
serverSocket = null;
/* Initiallizing the port number. */
port = PORT;
}
/**
* This method creates a connection between server and client.
*
* #throws java.io.IOException
*/
private void createConnection() throws IOException {
serverSocket = new ServerSocket(port); // Listen to the required port.
socket = serverSocket.accept(); // Accept the client connection.
}
/**
* This method sets the IP address.
*/
private void setIP() {
inet = socket.getInetAddress();
ip = new String(inet.getHostAddress());
}
/**
* This method returns the IP address.
*
* #return IP address.
*/
public String getIP() {
return ip;
}
/**
* This method checks if the socket has been connected
* with any client.
*
* #return True if the client has been connected, else false
*/
public boolean isConnected() {
if(socket == null)
return false;
return true;
}
/**
* This method returns the InputStream
* from the Socket.
*
* #return InputStream if Socket has been connected to the client, else null
* #see java.io.InputStream
*/
public InputStream getInputStream() throws IOException {
if(socket == null)
return null;
return socket.getInputStream();
}
#Override
public void run() {
try {
createConnection();
setIP();
} catch(IOException exception) {
exception.printStackTrace();
}
}
}
And this is my Server.java:
package com.chat.server;
/**
* <p>The Server app</p>
* This is the controller for accepting connections.
*
* #author Aditya R.Singh
* #version 1.0.0
* #since 1.0.0
*/
public class Server {
/**
* The port at which clients will connect.
*/
public static final int PORT = 6005;
/**
* For instantiation purpose.
*/
public Server() {
}
public static void main(String[] args) {
/* Keep accepting connections. */
while(true) {
ChatServer chat = new ChatServer(PORT); // Connecting port.
chat.start();
while(!chat.isConnected())
/* This is a false loop. Intended to keep running unless another client is not requesting to connect. */;
System.out.println("We connected to: "+chat.getIP());
}
}
}
The code compiles fine.
On running the code as:
java com.chat.server.Server
it seems that the program is listening for a client to connect. But after it connects to a client, it is expected to print the IP address of the client and then create another thread for another client. But it doesn't print the IP of the client.
This is my Client.java:
package com.chat.client;
import java.net.Socket;
import java.io.IOException;
public class Client {
public static void main(String[] args) {
Socket socket = null;
try {
socket = new Socket("127.0.0.1", 6005);
System.out.println("Socket connected.");
} catch(IOException ex) {
ex.printStackTrace();
}
}
}
The client ones connects to the server, must print Socket connected. The client does that. The client works fine:
java com.chat.client.Client
Socket connected.
But the server app doesn't print the IP address of the client. Why so?
This is not complete code
package demo;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class MultithreadedServer {
public static final int PORT = 10000;
public static void main(String[] args) {
try(ServerSocket server = new ServerSocket(PORT)) {
while(true){
Socket connection = server.accept();
Thread client = new ClientThread(connection);
client.start();
}
} catch (IOException ex) {
System.out.println("Error start server");
}
}
}
class ClientThread extends Thread {
private Socket connection;
public ClientThread(Socket connection) {
this.connection = connection;
}
#Override
public void run(){
//Do communication with client
}
}
It's a race condition. The line socket = serverSocket.accept(); causes the while(!chat.isConnected()) loop to terminate before the method 'setIP()' is been called. A quick way to verify that this is the cause of the problem is by changing this method:
public boolean isConnected() {
if(socket == null)
return false;
return true;
}
to
public boolean isConnected() {
if(socket == null || ip == null)
return false;
return true;
}
In order to fix the problem, you should make sure that the code that sets the IP and the code that checks whether it's connected use the synchronized keyword. Also, notice that the while(!chat.isConnected()) loop runs with no pauses, meaning that it'd take as much CPU as it's available... which is definitely not good.
Check out the link that #Michael Petch posted for a proper implementation of a chat server.

How to restrict Eclipse-RCP application to a single instance?

I would like to restrict my Eclipse-RCP application to a single instance. By this, I mean that once a user opens the application for the first time, it listens on a port and for the second access it should open the previous instance instead of showing a warning message like "already an instance is running"
My RCP Application code:
ApplicationInstanceListener.java interface code
public interface ApplicationInstanceListener
{
public void newInstanceCreated();
}
ApplicationInstanceManager.java code
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
public class ApplicationInstanceManager {
private static ApplicationInstanceListener subListener;
/** Randomly chosen, but static, high socket number */
public static final int SINGLE_INSTANCE_NETWORK_SOCKET = 2020;
/** Must end with newline */
public static final String SINGLE_INSTANCE_SHARED_KEY = "$$NewInstance$$\n";
/**
* Registers this instance of the application.
*
* #return true if first instance, false if not.
*/
public static boolean registerInstance() {
// returnValueonerror should be true if lenient (allows app to run on network error) or false if strict.
boolean returnValueonerror = true;
// try to open network socket
// if success, listen to socket for new instance message, return true
// if unable to open, connect to existing and send new instance message, return false
try {
final ServerSocket socket = new ServerSocket(SINGLE_INSTANCE_NETWORK_SOCKET, 10, InetAddress
.getLocalHost());
System.out.println("Listening for application instances on socket " + SINGLE_INSTANCE_NETWORK_SOCKET);
Thread instanceListenerThread = new Thread(new Runnable() {
public void run() {
boolean socketClosed = false;
while (!socketClosed) {
if (socket.isClosed()) {
socketClosed = true;
} else {
try {
Socket client = socket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
String message = in.readLine();
if (SINGLE_INSTANCE_SHARED_KEY.trim().equals(message.trim())) {
System.out.println("Shared key matched - new application instance found");
fireNewInstance();
}
in.close();
client.close();
} catch (IOException e) {
socketClosed = true;
}
}
}
}
});
instanceListenerThread.start();
// listen
} catch (UnknownHostException e) {
System.out.println(e.getMessage());
return returnValueonerror;
} catch (IOException e) {
System.out.println("Port is already taken. Notifying first instance.");
try {
Socket clientSocket = new Socket(InetAddress.getLocalHost(), SINGLE_INSTANCE_NETWORK_SOCKET);
OutputStream out = clientSocket.getOutputStream();
out.write(SINGLE_INSTANCE_SHARED_KEY.getBytes());
out.close();
clientSocket.close();
System.out.println("Successfully notified first instance.");
return false;
} catch (UnknownHostException e1) {
System.out.println(e.getMessage());
return returnValueonerror;
} catch (IOException e1) {
System.out.println("Error connecting to local port for single instance notification");
System.out.println(e1.getMessage());
return returnValueonerror;
}
}
return true;
}
public static void setApplicationInstanceListener(ApplicationInstanceListener listener) {
subListener = listener;
}
private static void fireNewInstance() {
if (subListener != null) {
subListener.newInstanceCreated();
}
}
}
Application.java code
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.PlatformUI;
/**
* This class controls all aspects of the application's execution
*/
public class Application implements IApplication {
/*
* (non-Javadoc)
*
* #see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.
* IApplicationContext)
*/
public Object start(IApplicationContext context) throws Exception {
if (!ApplicationInstanceManager.registerInstance()) {
// instance already running.
System.out
.println("Another instance of this application is already running. Exiting.");
MessageDialog
.openInformation(new Shell(), "Information",
"Another instance of this application is already running. Exiting.");
System.exit(0);
}
Display display = PlatformUI.createDisplay();
try {
int returnCode = PlatformUI.createAndRunWorkbench(display,
new ApplicationWorkbenchAdvisor());
if (returnCode == PlatformUI.RETURN_RESTART)
return IApplication.EXIT_RESTART;
else
return IApplication.EXIT_OK;
} finally {
display.dispose();
}
}
/*
* (non-Javadoc)
*
* #see org.eclipse.equinox.app.IApplication#stop()
*/
public void stop() {
if (!PlatformUI.isWorkbenchRunning())
return;
final IWorkbench workbench = PlatformUI.getWorkbench();
final Display display = workbench.getDisplay();
display.syncExec(new Runnable() {
public void run() {
if (!display.isDisposed())
workbench.close();
}
});
}
}
I've taken a simple RCP application with view as a template.
The above code works fine but doesn't open previous instance like skype or windows media player despite it shows an alert like below
How can I show or open the previous instance upon second run of the application?
I have an app that does this same thing. The trick is that the new instance can't bring the old instance to the front. But, the old instance can bring itself to the front after it contacts the new instance.
So your old instance needs to call
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell().forceActive();
after it notifies the new instance. For my app, the new instance doesn't show an error message, it just closes transparently and the old instance pops itself back up.
Have a look at this article: Single instance of RCP application. The author describes the same pattern of using a server socket which you are asking about.
i think you should just alternate to you already running instance.
i don't know if this or this link could help, but thats all i got
really hope it helps
Basically you can have functionality like eclipse. Eclipse maintains a .lock file to lock the workspace. You can similarly create an empty .lock file in your workspace.
On starting every instance, you should check if .lock file is present and then proceed further accordingly. If file is not present you should create it so that other instance will find that workspace is locked.

Using Threads to Handle Sockets

I am working on a java program that is essentially a chat room. This is an assignment for class so no code please, I am just having some issues determining the most feasible way to handle what I need to do. I have a server program already setup for a single client using threads to get the data input stream and a thread to handle sending on the data output stream. What I need to do now is create a new thread for each incoming request.
My thought is to create a linked list to contain either the client sockets, or possibly the thread. Where I am stumbling is figuring out how to handle sending the messages out to all the clients. If I have a thread for each incoming message how can I then turn around and send that out to each client socket.
I'm thinking that if I had a linkedlist of the clientsockets I could then traverse the list and send it out to each one, but then I would have to create a dataoutputstream each time. Could I create a linkedlist of dataoutputstreams? Sorry if it sounds like I'm rambling but I don't want to just start coding this, it could get messy without a good plan. Thanks!
EDIT
I decided to post the code I have so far. I haven't had a chance to test it yet so any comments would be great. Thanks!
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.ServerSocket;
import java.util.LinkedList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class prog4_server {
// A Queue of Strings used to hold out bound Messages
// It blocks till on is available
static BlockingQueue<String> outboundMessages = new LinkedBlockingQueue<String>();
// A linked list of data output streams
// to all the clients
static LinkedList<DataOutputStream> outputstreams;
// public variables to track the number of clients
// and the state of the server
static Boolean serverstate = true;
static int clients = 0;
public static void main(String[] args) throws IOException{
//create a server socket and a clientSocket
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(6789);
} catch (IOException e) {
System.out.println("Could not listen on port: 6789");
System.exit(-1);
}// try{...}catch(IOException e){...}
Socket clientSocket;
// start the output thread which waits for elements
// in the message queue
OutputThread out = new OutputThread();
out.start();
while(serverstate){
try {
// wait and accept a new client
// pass the socket to a new Input Thread
clientSocket = serverSocket.accept();
DataOutputStream ServerOut = new DataOutputStream(clientSocket.getOutputStream());
InputThread in = new InputThread(clientSocket, clients);
in.start();
outputstreams.add(ServerOut);
} catch (IOException e) {
System.out.println("Accept failed: 6789");
System.exit(-1);
}// try{...}catch{..}
// increment the number of clients and report
clients = clients++;
System.out.println("Client #" + clients + "Accepted");
}//while(serverstate){...
}//public static void main
public static class OutputThread extends Thread {
//OutputThread Class Constructor
OutputThread() {
}//OutputThread(...){...
public void run() {
//string variable to contain the message
String msg = null;
while(!this.interrupted()) {
try {
msg = outboundMessages.take();
for(int i=0;i<outputstreams.size();i++){
outputstreams.get(i).writeBytes(msg + '\n');
}// for(...){...
} catch (IOException e) {
System.out.println(e);
} catch (InterruptedException e){
System.out.println(e);
}//try{...}catch{...}
}//while(...){
}//public void run(){...
}// public OutputThread(){...
public static class InputThread extends Thread {
Boolean threadstate = true;
BufferedReader ServerIn;
String user;
int threadID;
//SocketThread Class Constructor
InputThread(Socket clientSocket, int ID) {
threadID = ID;
try{
ServerIn = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
user = ServerIn.readLine();
}
catch(IOException e){
System.out.println(e);
}
}// InputThread(...){...
public void run() {
String msg = null;
while (threadstate) {
try {
msg = ServerIn.readLine();
if(msg.equals("EXITEXIT")){
// if the client is exiting close the thread
// close the output stream with the same ID
// and decrement the number of clients
threadstate = false;
outputstreams.get(threadID).close();
outputstreams.remove(threadID);
clients = clients--;
if(clients == 0){
// if the number of clients has dropped to zero
// close the server
serverstate = false;
ServerIn.close();
}// if(clients == 0){...
}else{
// add a message to the message queue
outboundMessages.add(user + ": " + msg);
}//if..else...
} catch (IOException e) {
System.out.println(e);
}// try { ... } catch { ...}
}// while
}// public void run() { ...
}
public static class ServerThread extends Thread {
//public variable declaration
BufferedReader UserIn =
new BufferedReader(new InputStreamReader(System.in));
//OutputThread Class Constructor
ServerThread() {
}//OutputThread(...){...
public void run() {
//string variable to contain the message
String msg = null;
try {
//while loop will continue until
//exit command is received
//then send the exit command to all clients
msg = UserIn.readLine();
while (!msg.equals("EXITEXIT")) {
System.out.println("Enter Message: ");
msg = UserIn.readLine();
}//while(...){
outboundMessages.add(msg);
serverstate = false;
UserIn.close();
} catch (IOException e) {
System.out.println(e);
}//try{...}catch{...}
}//public void run(){...
}// public serverThread(){...
}// public class prog4_server
I have solved this problem in the past by defining a "MessageHandler" class per client connection, responsible for inbound / outbound message traffic. Internally the handler uses a BlockingQueue implementation onto which outbound messages are placed (by internal worker threads). The I/O sender thread continually attempts to read from the queue (blocking if required) and sends each message retrieved to the client.
Here's some skeleton example code (untested):
/**
* Our Message definition. A message is capable of writing itself to
* a DataOutputStream.
*/
public interface Message {
void writeTo(DataOutputStream daos) throws IOException;
}
/**
* Handler definition. The handler contains two threads: One for sending
* and one for receiving messages. It is initialised with an open socket.
*/
public class MessageHandler {
private final DataOutputStream daos;
private final DataInputStream dais;
private final Thread sender;
private final Thread receiver;
private final BlockingQueue<Message> outboundMessages = new LinkedBlockingQueue<Message>();
public MessageHandler(Socket skt) throws IOException {
this.daos = new DataOutputStream(skt.getOutputStream());
this.dais = new DataInputStream(skt.getInputStream());
// Create sender and receiver threads responsible for performing the I/O.
this.sender = new Thread(new Runnable() {
public void run() {
while (!Thread.interrupted()) {
Message msg = outboundMessages.take(); // Will block until a message is available.
try {
msg.writeTo(daos);
} catch(IOException ex) {
// TODO: Handle exception
}
}
}
}, String.format("SenderThread-%s", skt.getRemoteSocketAddress()));
this.receiver = new Thread(new Runnable() {
public void run() {
// TODO: Read from DataInputStream and create inbound message.
}
}, String.format("ReceiverThread-%s", skt.getRemoteSocketAddress()));
sender.start();
receiver.start();
}
/**
* Submits a message to the outbound queue, ready for sending.
*/
public void sendOutboundMessage(Message msg) {
outboundMessages.add(msg);
}
public void destroy() {
// TODO: Interrupt and join with threads. Close streams and socket.
}
}
Note that Nikolai is correct in that blocking I/O using 1 (or 2) threads per connection is not a scalable solution and typically applications might be written using Java NIO to get round this. However, in reality unless you're writing an enterprise server which thousands of clients connect to simultaneously then this isn't really an issue. Writing bug-free scalable applications using Java NIO is difficult and certainly not something I'd recommend.

Categories

Resources