How to implement the Observer pattern with Java RMI? - java

I have a client that starts a long running process on the server. At regular intervals, I'd like to show the user what's happening in the background. The most simple approach is to poll the server but I'm wondering if there wasn't a way to implement the Observer pattern for this. Unfortunately, I'm using RMI to talk to the server and I fear that I have to turn my client into an RMI server for this.
Is there another way that I'm missing?

http://sites.google.com/site/jamespandavan/Home/java/sample-remote-observer-based-on-rmi

RMI can in general support two way communication. (And yeah, RMI is a PITA to set up, and do anything else with.)
However, the HTTP transport that works over a CGI script(!) does not support it.

Consolidating all the answers here, I implemented 2 way RMI between client and server with server exposing its stub using Registry
The client gets a stub of the server from rmi registry
Then the client puts its stub as Observer to the server's addObserver method
The server notifies the clients using this stub
The following code will gives a better idea
import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;
import java.util.Observable;
import java.util.Observer;
import java.net.*;
import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.rmi.ssl.SslRMIServerSocketFactory;
interface ReceiveMessageInterface extends Remote
{
/**
* #param x
* #throws RemoteException
*/
void receiveMessage(String x) throws RemoteException;
/**
* #param observer
* #throws RemoteException
*/
void addObserver(Remote observer) throws RemoteException;
}
/**
*
*/
class RmiClient extends UnicastRemoteObject
{
/**
* #param args
*/
static public void main(String args[])
{
ReceiveMessageInterface rmiServer;
Registry registry;
String serverAddress = args[0];
String serverPort = args[1];
String text = args[2];
System.out.println("sending " + text + " to " + serverAddress + ":" + serverPort);
try
{ // Get the server's stub
registry = LocateRegistry.getRegistry(serverAddress, (new Integer(serverPort)).intValue());
rmiServer = (ReceiveMessageInterface) (registry.lookup("rmiServer"));
// RMI client will give a stub of itself to the server
Remote aRemoteObj = (Remote) UnicastRemoteObject.exportObject(new RmiClient(), 0);
rmiServer.addObserver(aRemoteObj);
// call the remote method
rmiServer.receiveMessage(text);
// update method will be notified
}
catch (RemoteException e)
{
e.printStackTrace();
}
catch (NotBoundException e)
{
System.err.println(e);
}
}
public void update(String a) throws RemoteException
{
// update should take some serializable object as param NOT Observable
// and Object
// Server callsbacks here
}
}
/**
*
*/
class RmiServer extends Observable implements ReceiveMessageInterface
{
String address;
Registry registry;
/**
* {#inheritDoc}
*/
public void receiveMessage(String x) throws RemoteException
{
System.out.println(x);
setChanged();
notifyObservers(x + "invoked me");
}
/**
* {#inheritDoc}
*/
public void addObserver(final Remote observer) throws RemoteException
{
// This is where you plug in client's stub
super.addObserver(new Observer()
{
#Override
public void update(Observable o,
Object arg)
{
try
{
((RmiClient) observer).update((String) arg);
}
catch (RemoteException e)
{
}
}
});
}
/**
* #throws RemoteException
*/
public RmiServer() throws RemoteException
{
try
{
address = (InetAddress.getLocalHost()).toString();
}
catch (Exception e)
{
System.out.println("can't get inet address.");
}
int port = 3232;
System.out.println("this address=" + address + ",port=" + port);
try
{
registry = LocateRegistry.createRegistry(port);
registry.rebind("rmiServer", this);
}
catch (RemoteException e)
{
System.out.println("remote exception" + e);
}
}
/**
*
* #param args
*/
static public void main(String args[])
{
try
{
RmiServer server = new RmiServer();
}
catch (Exception e)
{
e.printStackTrace();
System.exit(1);
}
}
}

I don't think you're missing anything. The only two ways are to either periodically call the server and check the status (polling) or register a callback which the server periodically calls (your client must expose a method). IMO, polling is a perfectly reasonable way to handle this.

Related

How to do naming system with RMI in different hosts?

I have two java classes one is the client and the other is the server, in the client I have to give the server ip address, but I want to make it dynamique so the client knows the ip of a third machine(naming system) which search for the method (like DNS) and returns the ip address of the specific server who provides this method.
Edited:
classe client :
public class Client {
private Client() {}
public static void main(String[] args) {
String host = (args.length < 1) ? null : args[0];
try {
//Registry registry = LocateRegistry.getRegistry(host);
Registry registry = LocateRegistry.getRegistry("192.168.1.9",1091);
Calculator stub = (Calculator) registry.lookup("Hello");
String response = stub.add(4,2);
System.out.println("response: " + response);
} catch (Exception e) {
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}
}
}
server :
public class Server implements Calculator{
public Server() {}
public String add(int a,int b) {
return "Hello, a+b= "+(a+b);
}
public String sub(int a,int b) {
return "Hello, a-b= "+(a-b);
}
public static void main(String args[]) {
try {
Server obj = new Server();
Calculator stub = (Calculator) UnicastRemoteObject.exportObject(obj, 0);
// Bind the remote object's stub in the registry
Registry registry = LocateRegistry.createRegistry(1091);
registry.bind("Hello", stub);
System.err.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
You can't do this with the RMI Registry. Only a process running in the same host as the Registry can bind to it. You would have to use a different naming service such as LDAP.
However the rest of your description is what RMI already does. You look up a name in the Registry and it returns you a stub that knows how to communicate with the corresponding remote object.

java.lang.VerifyError: Verifier rejected class StreamingRecognizeResponse due to bad method

Hey I am trying to use Google Cloud Speech API into Android but I am getting this error and am unable to get around it.
java.lang.VerifyError: Verifier rejected class com.google.cloud.speech.v1
.StreamingRecognizeResponse due to bad method java.lang.Object com.google
.cloud.speech.v1.StreamingRecognizeResponse.dynamicMethod(com.google.protobuf
.GeneratedMessageLite$MethodToInvoke, java.lang.Object, java.lang.Object)
(declaration of 'com.google.cloud.speech.v1.StreamingRecognizeResponse'
appears in /data/app/com.curieo.podcast-1/base.apk:classes65.dex)
at com.google.cloud.speech.v1.StreamingRecognizeResponse.getDefaultInstance(StreamingRecognizeResponse.java:1095)
at com.google.cloud.speech.v1.SpeechGrpc.<clinit>(SpeechGrpc.java:67)
at com.curieo.podcast.ui.fragment.dates.googlecloud.StreamingRecognizeClient.<init>(StreamingRecognizeClient.java:57)
at com.curieo.podcast.ui.fragment.dates.googlecloud.MicrophoneStreamRecognizeClient.<init>(MicrophoneStreamRecognizeClient.java:54)
at com.curieo.podcast.ui.fragment.dates.RecordFragment$1.run(RecordFragment.java:112)
I searched for this but couldn't find a solution. here is the code where error occurs
private Thread runner = new Thread() {
public void run() {
try {
MicrophoneStreamRecognizeClient client;
synchronized (this) {
try {
client = new MicrophoneStreamRecognizeClient(getResources().openRawResource(R.raw.credential), Self); //crashes here
client.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
Here is the code snippet of MicrophoneStreamRecognizeClient class:
public class MicrophoneStreamRecognizeClient {
private String host = "speech.googleapis.com";
private Integer port = 443;
private ManagedChannel channel;
private StreamingRecognizeClient client;
private final List<String> OAUTH2_SCOPES = Arrays.asList("https://www.googleapis.com/auth/cloud-platform");
/**
*
* #param authorizationFile
* #param host
* #param port
* #return
* #throws IOException
*/
private ManagedChannel createChannel(InputStream authorizationFile, String host, int port) throws IOException {
GoogleCredentials creds = GoogleCredentials.fromStream(authorizationFile);
creds = creds.createScoped(OAUTH2_SCOPES);
return ManagedChannelBuilder.forAddress(host, port)
.intercept(new ClientAuthInterceptor(creds, Executors.newSingleThreadExecutor()))
.build();
}
/**
*
* #param autorizationFile
* #throws IOException
*/
public MicrophoneStreamRecognizeClient(InputStream autorizationFile, IResults screen) throws IOException {
channel = createChannel(autorizationFile, host, port);
client = new StreamingRecognizeClient(channel, screen);
}
/**
*
* #throws IOException
* #throws InterruptedException
*/
public void start() throws IOException, InterruptedException {
client.recognize();
}
/**
*
* #throws InterruptedException
*/
public void stop() throws InterruptedException {
client.shutdown();
}
}
Cleaning out the build folder resolved the problem. Not sure why ART had an issue but Dalvik did not.
Running a gradle clean task was not clearing out my build folder all the way. I had to do it manually, but clean may work for some people.
Reference
java.lang.VerifyError can happen for some reason:
A class tries to extend a class declared as final
A method tries to override a super method that is declared as final
A wrong argument is passed to a method
clint = new MicrophoneStreamRecognizeClient(getResources()
.openRawResource(R.raw.credential), Self); //crashes here
I don't know what is Self. Is that ok?
Try this.
synchronized (this) {
MicrophoneStreamRecognizeClient client;
try {
client = new MicrophoneStreamRecognizeClient(getResources().openRawResource(R.raw.credential), Self); //crashes here
client.start();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Because another issue can be !! Synchronized block inside try / catch block !! cause of java.lang.VerifyError reference
If the java.lang.VerifyError is triggered because library differences between runtime and compiling, as #Enamul also suggested, then you might wanna check differences between com.google.cloud.speech.v1.StreamingRecognizeResponse and com.google.cloud.speech.v1beta1.StreamingRecognizeResponse.
Even so, try with the beta, newer, version of the library. The example that inspired you uses the beta version.

Java RMI: how to make client stub method called on server print message on client screen?

I'm making a chat with rmi in Java. I have one server object and at least two clients objects. When a client send a message to the server calling the method recebeMensagem remotely, the server must print that message on all clients' screen (except the client that sent the message).
The client class has a method printMenssagem(Mensagem msg), that is called remotely by the server. The problem is that that method is printing on server's screen. How do I make to print the message on client's screen instead?
Server.java:
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.function.Predicate;
public class Server implements ChatServer {
private ArrayList<String> listaClientes = new ArrayList<>();
private static int port = 5002;
public static void main(String[] args) {
try {
Server obj = new Server();
ChatServer stub = (ChatServer)
UnicastRemoteObject.exportObject(obj, port);
// Bind the remote object's stub in the registry
Registry registry = LocateRegistry.createRegistry(port);
registry.bind("chat", stub);
System.out.println("Server ready!");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
#Override
public void adicionaCliente(String user) {
this.listaClientes.add(user);
}
#Override
public void retiraCliente(String userName) {
Predicate<String> clientePredicate = cp ->
cp.equals(userName);
listaClientes.removeIf(clientePredicate);
try {
Registry registry = LocateRegistry.getRegistry(port);
registry.unbind(userName);
} catch (RemoteException e) {
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
}
}
#Override
public void recebeMensagem(Mensagem msg) {
try {
Registry registry = LocateRegistry.getRegistry(port);
for(String cliente : listaClientes) {
if (!cliente.equals(msg.getRemetente())) {
Client stub = (Client) registry.lookup(cliente);
stub.printMensagem(msg);
}
}
} catch (RemoteException e) {
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
}
}
public ArrayList<String> getListaClientes() {
return listaClientes;
}
public void setListaClientes(ArrayList<String> listaClientes) {
this.listaClientes = listaClientes;
}
}
Client.java :
import java.io.Serializable;
import java.rmi.Remote;
public class Client implements Remote, Serializable {
private static final long serialVersionUID = 6864579049922061838L;
private static int port = 5002;
private static String host = "127.0.0.1";
public static void main(String[] args) {
new Thread(new ClientInterface(host, port)).start();
}
public void printMensagem(Mensagem mensagem) {
System.out.println(mensagem.getRemetente() + ": " + mensagem.getMensagem());
}
}
how to make client stub method called on server print message on client screen?
The client doesn't have a stub. It isn't a remote object. It is a serializable object and it has been transported to the Registry holus bolus, and it runs in whatever JVM performed the Registry.lookup() to obtain it. This is not what you want. You want it to be a remote object, with a stub, so you have to make it implement a remote interface, and export it, and use it via its remote interface at the peer.
You also need to be aware that your present architecture won't work across more than one host, as you can't bind to a remote Registry. You will need to add a client registration method to the server.

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.

Categories

Resources