I am currently trying to grab the basics of RMI by implementing a Server/Client Structure where both the Client can invoke remote Operations on the Server, and the Server can invoke Client functions:
public class Client extends GenericRMI implements ClientInterface {
public ServerInterface server;
public Client() {
try {
String IP = InetAddress.getLocalHost().getHostAddress();
server = (ServerInterface) Naming.lookup("//192.168.2.124/WServer");
int uniqueID = (int) Math.round(Math.random() * 1000);
super.setUpRMI("WClient" + IP + "_" + uniqueID);
server.registerNewClient(IP, uniqueID);
} catch (Exception e) {
e.printStackTrace();
}
}
public void setUserID(int id) {
System.out.println("got my ID from the server: " + id);
}
}
public class Server extends GenericRMI implements ServerInterface {
private List<ClientInterface> clients;
public Server() {
clients = new ArrayList<ClientInterface>();
super.setUpRMI("WServer");
}
public void registerNewClient(String IP, int uID) throws RemoteException {
try {
ClientInterface c = (ClientInterface) Naming.lookup("//" + IP + "/WClient" + IP + "_"
+ uID);
int newID = clients.size();
clients.add(c);
c.setUserID(newID);
} catch (Exception e) {
e.printStackTrace();
}
}
}
and, in the main function:
new Server();
Thread.sleep(1000);
new Client();
Thread.sleep(1000);
new Client();
Thread.sleep(1000);
new Client();
interfaces are defined by
public interface ServerInterface extends Remote...
RMI setup
public class GenericRMI implements Remote, Serializable {
protected Registry registry;
public void setUpRMI(String bindName) {
if (registry == null) {
try {
registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
} catch (ExportException e) {
// client and server on one PC
} catch (RemoteException e) {
e.printStackTrace();
}
}
try {
Naming.rebind(bindName, this);
} catch (RemoteException | MalformedURLException e) {
e.printStackTrace();
}
System.out.println("Started " + bindName);
}
}
however, output is like
Started WServer
Started WClient192.168.2.124_501
got my ID from the server: 0
Started WClient192.168.2.124_655
got my ID from the server: 0
Started WClient192.168.2.124_771
got my ID from the server: 0
even if i debug it, the server has a different ID for each client. I guess I am doing a terrible mistake somewhere, since I used to think that the server would only have ONE instance running. How can I achieve that?
EDIT
the Problem is; if i debug the registerNewClient() function, the respective Server object changes for each Client:
Server#7728992
Server#5fbb71ac
...
Even if i make the clients' list synchronized, it doesn't help. However, making the clients field transient server-side leads to a null pointer exception on it, indicating that it indeed is a new instance.
You do have only one instance running. You only called new Server() once, and in any case there's no way three instances can be bound to the same name in the Registry. More likely you have a concurrency problem on the unsynchronized 'clients' collection.
Related
I have a machine running that sends out status information over TCP to an IP address and port you set on the machine. If I use the command line for the machine on that IP address and run "nc -l " I get the status data from the machine. I am trying to build a Java Spring application to ingest this but all the Java TCP tutorials talk about setting channel names and subscribing to channels? Are channels something that are built on top of TCP and my machine just isn't using channels or is there some default channel listened on when you run the command line "nc -l " command? Please Help I'm very confused
EDIT 1: Adding first attempt code that I can't get to integrate with the Spring application nor can I get the data to store in Spring JPA
public class EchoMultiServer {
private ServerSocket serverSocket;
public void start(int port) {
try {
serverSocket = new ServerSocket(port);
while (true)
new EchoClientHandler(serverSocket.accept()).start();
} catch (IOException e) {
e.printStackTrace();
} finally {
stop();
}
}
public void stop() {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static class EchoClientHandler extends Thread {
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
#Autowired
PowerStationService powerStationService;
//this service connects to the repository to store the data
public EchoClientHandler(Socket socket) {
this.clientSocket = socket;
}
public JSONObject mapJsonInput(String incomingText){
try{
JSONObject json = new JSONObject(incomingText);
return json;
} catch (JSONException e){
System.out.println("JSONException " + e);
return null;
}
}
public JSONObject run() {
try {
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
StringBuilder textBuilder = new StringBuilder();
int c = 0;
int leftCaratCount=0;
int rightCaratCount=0;
while ((c = in.read()) != -1) {
char character = (char) c;
textBuilder.append(character);
if (character == '{') {
leftCaratCount++;
} else if (character == '}') {
rightCaratCount++;
if (rightCaratCount == leftCaratCount) {
System.out.println(textBuilder);
JSONObject registrationJson = mapJsonInput(textBuilder.toString());
System.out.println("we got em");
powerStationService.save(new PowerStation(registrationJson.get("D").toString(), registrationJson.get("G").toString(), Integer.parseInt(registrationJson.get("Y").toString()), Integer.parseInt(registrationJson.get("S").toString()), registrationJson.get("C").toString(), registrationJson.get("Z").toString(), registrationJson.get("V").toString()));
out.println("000250{\"A\":\"45514\",\"C\":\""+registrationJson.get("Y")+"\",\"E\":\"30,5\",\"G\":\""+registrationJson.get("G")+"\",\"H\":\"0\",\"K\":\"1\",\"M\":\"123456\",\"N\":\"" + System.currentTimeMillis() + "\",\"O\":\"13371\",\"P\":\"" + clientSocket.getLocalAddress().getHostAddress() + "\",\"S\":\"60000\",\"U\":\"\",\"V\":\"\",\"W\":\"https://admin.chargenow.top/cdb-socket-api/v1/socketserver/common\",\"X\":\"0\",\"Y\":\"FJC\",\"Z\":\"\"}");
in.close();
out.close();
clientSocket.close();
}
}
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
public static void main(String[] args) {
EchoMultiServer server = new EchoMultiServer();
server.start(13370);
}
}
EDIT 2: Additionally I attempted to use the example from the Spring Examples Github to see if it could receive messages on the port I tried. I'm able to use NetCat to see the ServerOut messages but the application isn't receiving responses back
#SpringBootApplication
#EnableConfigurationProperties(SampleProperties.class)
public class TcpAsyncBiDirectionalApplication {
public static void main(String[] args) {
SpringApplication.run(TcpAsyncBiDirectionalApplication.class, args);
}
}
#Configuration
class ServerPeer {
private final Set<String> clients = ConcurrentHashMap.newKeySet();
#Bean
public AbstractServerConnectionFactory server(SampleProperties properties) {
return Tcp.netServer(properties.getServerPort()).get();
}
#Bean
public IntegrationFlow serverIn(AbstractServerConnectionFactory server) {
return IntegrationFlows.from(Tcp.inboundAdapter(server))
.transform(Transformers.objectToString())
.log(msg -> "received by server: " + msg.getPayload())
.get();
}
#Bean
public IntegrationFlow serverOut(AbstractServerConnectionFactory server) {
return IntegrationFlows.fromSupplier(() -> "seed", e -> e.poller(Pollers.fixedDelay(5000)))
.split(this.clients, "iterator")
.enrichHeaders(h -> h.headerExpression(IpHeaders.CONNECTION_ID, "payload"))
.transform(p -> "sent by server Hello from server")
.handle(Tcp.outboundAdapter(server))
.get();
}
#EventListener
public void open(TcpConnectionOpenEvent event) {
if (event.getConnectionFactoryName().equals("server")) {
this.clients.add(event.getConnectionId());
}
}
#EventListener
public void close(TcpConnectionCloseEvent event) {
this.clients.remove(event.getConnectionId());
}
}
enter code here
enter code here
TCP/IP is just one of those protocols implemented as channel adapters in Spring Integration.
See more info in the theory: https://www.enterpriseintegrationpatterns.com/ChannelAdapter.html
And here is a list of all the supported protocols in Spring Integration:
https://docs.spring.io/spring-integration/docs/current/reference/html/endpoint-summary.html#spring-integration-endpoints
What those tutorials are talking about is a messaging channel internal to your application logic. It has nothing to do with external protocol, like yours TCP/IP. You just need to understand for your self if your application is going to be client for that IP host/port or it is going to be a server opening respective socket:
https://docs.spring.io/spring-integration/docs/current/reference/html/ip.html#ip
I am currently in the process of writing an app which has the phone connect to a server.
Both client and server are using the Kryonet framework.
The problem ist the following :
When the server is running and I then start up the client, the client immediately disconnects from the server but the Programm itself keeps running so it is only possible that the client Thread died for whatever reason.
I am using kryonet-2.21 on both server and client.
I tried my code on Android aswell as on pc.
I also tried to troubleshoot everything I could and tried everything I found searching for my problem.
The Client code :
public class LogicHandler extends Thread {
private Client client;
public LogicHandler() {
}
public Client getClient() {
return client;
}
public void run() {
client = new Client(33554432, 33554432);
new Thread(client).start();
try {
getClient().connect(5000, "localhost", 54555, 54777);
} catch (IOException e) {
e.printStackTrace();
}
Packets.register(getClient());
getClient().addListener(new Listener() {
public void received(Connection connection, Object object) {
System.out.println("received " + object);
if (object instanceof ConnectionResponse) {
}
if (object instanceof ScheduleResponse) {
}
}
public void disconnected(Connection connection) {
}
});
getClient().sendTCP(new ConnectionRequest());
}
public static void main(String[] args) {
new LogicHandler().start();
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("wait");
}
}
}
The Server code :
public class ServerLogicHandler {
private Server server;
private List<Client> clients;
private List<Connection> connections;
public ServerLogicHandler() {
System.out.println("Server is starting!");
server = new Server(33554432, 33554432);
server.start();
try {
server.bind(54555, 54777);
} catch (Exception e) {
server.stop();
System.out.println("Port belegt. Server wird gestoppt!");
System.exit(0);
}
Packets.register(server);
clients = new ArrayList<Client>();
connections = new ArrayList<Connection>();
server.addListener(new Listener() {
public void received(Connection connection, Object object) {
System.out.println("got packet");
if (object instanceof ConnectionRequest) {
System.out.println("size " + connection.sendTCP(new ScheduleResponse()));
}
}
public void disconnected(Connection connection) {
System.out.println("Disco " + connection.getID());
}
public void connected(Connection connection) {
System.out.println(connection.getRemoteAddressTCP().getPort() + " "
+ connection.getRemoteAddressTCP().getAddress());
}
});
System.out.println("Server started!");
}
public Server getServer() {
return server;
}
public static void main(String... args) {
new ServerLogicHandler();
}
}
The client doesn't output anything apart from the 'wait' every second. This means that either the server didn't send any packet or the client closed already. My guess is that the latter happened because the server outputs the following :
Server is starting!
Server started!
54408 /127.0.0.1
Disco 1
When I start a new client the last 2 lines would just repeat e.g. '54890 /127.0.0.1
Disco 2
'
From this I guess that the client closes for whatever reason before even sending any packets. None of my Google searches brang up any success.
I have a chat program. The problem is that I am trying to disallow dupe names. Essentially, whenever a name change request is sent to the server, it is checked against the list of names currently in use and if it is already taken, the person is added to my shitlist (not allowed to post) and they are sent a message that they need to change their name.
I commented the crap out of the code since there is a lot so you can understand it easily.
The problem is that the wrong person is being sent the message that the name is already in use! I have spent the last 8 hours trying to find it and It's bloody driving me mad!
The server side code is long; I'll post the relevant bits and any further will be provided on request. I'll also link to the complete program. (Not the source, the JAR.)
JAR: https://www.mediafire.com/?4t2shjdjf7blpg2
//...Irrelevant bits ommitted...//
public class Server
{
// The server object reference
static Server server;
// Declarations:
private ArrayList<ObjectOutputStream> clientOutputStreams; // out streams
private ArrayList<String> takenNames = new ArrayList<>(); // taken names
private InetAddress ip;
private final int serverPort; // the port the server is running on
private static ObjectOutputStream changer; // the last person to change names
private ArrayList<ObjectOutputStream> shitList = new ArrayList<>();
private HashMap <InetAddress, ObjectOutputStream> ipMap =
new HashMap<>(); // <ip, outputstream>
//...Irrelevant bits ommited...//
// Don't mind this non-indentation, it is supposed to be.
public void tellEveryone(Message message, InetAddress senderIP)
{
// First check some special conditions..
if(message.getType() == Message.TYPE.IN_USE)
{
try
{
changer.writeObject(message);
}
catch (IOException e)
{
e.printStackTrace();
}
}
// If someone is on my shitlist,
if(shitList.contains(ipMap.get(senderIP)))
{
// Warn them of their sins...
Message nopeMessage = new Message(Message.TYPE.SERVER,
"You may not send any messages until you change your name!",
"Server");
try
{
ipMap.get(senderIP).writeObject(nopeMessage);
}
catch(IOException e)
{
e.printStackTrace();
}
}
else
{
// Send message normally to everyone...
// Sync, just to be safe
synchronized(clientOutputStreams)
{
for(ObjectOutputStream oo : clientOutputStreams) // while more clients...
{
try
{
oo.writeObject(message);
oo.flush();
}
catch(IOException e)
{
System.out.println("IOException caught during tellEveryone()");
e.printStackTrace();
}
}
}
System.out.println(getTimeStamp() + ": Message Sent by:".
concat(" " + senderIP + "/ " + message.getSenderName()));
}
}
The server handler inner class...
public class ServerHandler implements Runnable
{
#Override
public void run()
{
// Create a list of client out streams to send stuff...
clientOutputStreams = new ArrayList<>();
try // To establish a connection with clients
{
// Create server socket...
ServerSocket serverSocket = new ServerSocket(serverPort);
while(true) // Will always run! Blocks!
{
// Assign a client socket to any new socket connections...
// (The var used here is temp, but will be passed off soon.)
Socket clientSocket = serverSocket.accept();
// Get's the ip of the client that connected...
ip = clientSocket.getInetAddress();
System.out.println(ip + " " + "connected.");
// Create ooStream to send messages to client...
ObjectOutputStream ooStream =
new ObjectOutputStream(
clientSocket.getOutputStream());
// Add the client oo stream to the list of outputs...
clientOutputStreams.add(ooStream);
// Add user IP data to map of ip's
ipMap.putIfAbsent(ip, ooStream);
// Create new thread to run inner class ClientHandler...
Thread t = new Thread(new ClientHandler(clientSocket));
// Running the thread makes it safe to overwrite the...
// ...clientsocket variable.
t.start();
}
}
catch (IOException e)
{
System.out.println("Exception in server.run()");
// TODO: Revise
e.printStackTrace();
}
}
}
The client handler inner class
public class ClientHandler implements Runnable
{
private ObjectInputStream oInStream; // The client's input stream.
private Socket socket; // Socket to the client
public ClientHandler(Socket clientSocket)
{
try // to create an input stream...
{
socket = clientSocket; // <-- The one passed in to the method
// Potential error from previous version... REMOVE WHEN TESTED
oInStream = new ObjectInputStream(socket.getInputStream());
}
catch(IOException e)
{
System.out.println("Error establishing input stream");
}
}
#Override
public void run()
{
Message message;
try // To process incoming messages...
{
while(socket.isClosed() == false) // If the socket is open...
{
// While there are more messages...
// Also assigns to the message var.
while((message = (Message)oInStream.readObject()) != null)
{
// Passes on the message and sender info.
if(message.getType() == Message.TYPE.NAME_REQUEST)
{
changer = ipMap.get(socket.getInetAddress());
System.out.println(socket.getInetAddress());
System.out.println(changer.toString());
handleNameRequests(message);
}
else
{
tellEveryone(message, ip); // TEST CHANGE- DELETED IF TEST
}
}
// TEST TEST TEST
synchronized(clientOutputStreams)
{
int index =
clientOutputStreams.indexOf(
socket.getOutputStream());
clientOutputStreams.remove(index);
System.out.println("Removed the client in sync");
}
}
// TEST TEST TEST
socket.close(); // TEST CLOSING SOCKET WHEN DONE.
System.out.println("Sock closed after while loop in ch run()");
}
catch(IOException e)
{
System.out.println("IOException caught when "
+ "reading message.");
}
catch (ClassNotFoundException e)
{
System.out.println("Some poor sap is going to have to debug"
+ "this!");
}
finally
{
// THIS WHOLE BLOCK: TEST TEST TEST
try
{
oInStream.close();
System.out.println("just closed oinStream");
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
I FINALLY FOUND IT!
For any future people encountering a similar problem, the problem was that I was assigning the ip variable in the wrong place! This essentially resulted in the list of ip's being all the same! Another bug confounded that problem, in that when I disabled the sending ability of messages when on my shitlist (aren't programmers the darndest?), I disabled ALL types of messages, including those from the server, ect! Oops!
The lesson? Bugs hide in the darndest places. Walk through everything, and doubt what you know to be true. Assume nothing, verify everything. There are never enough print statements when debugging!
i am developing web application and i host it in tomcat server. my requirement is client system wants to know the ip address of the system where the tomcat server located in a network.Before make any request to the server using java.or any possible to set default ip address for the tomcat server.and how can i use the default ip address to make a request if system ip and tomcat ip are different.
I don't know if I understood your question right. So the server is running in a local area network and your client software does not know which address/ip to connect to? You could add a multicast server to your servers app and a multicast receiver to your clients. The server will send a boardcast packet frequently to a specified boardcast address/channel, i.e. 203.0.113.0.
Once your client starts, it will receive the boardcast packet send by the server, as long as he is connected to the same channel. The packet is containing the servers address. You can than use that address to connect to the server.
Update
This is a very simplyfied example of a sender, receiver and a constants class to share the settings. It sends a String (Server is here) as packet data, but in a real world application, you should create an object serialize it at the server and deserialize it at the client. That object could hold more information about the server an could be verified at client side.
Server/Client side shared code:
public final class MulticastConstants {
public static final String MULTICAST_PACKET_DATA = "Server is here!";
public static final String MULTICAST_CHANNEL = "230.0.0.1";
public static final int MULTICAST_PORT = 8881;
public static final int MULTICAST_PACKET_SIZE = MULTICAST_PACKET_DATA.getBytes().length;
}
Server Side code:
The sender is a WebListener and will start and stop with your application.
#WebListener
public class MulticastSender implements ServletContextListener {
private MulticastSocket socket;
private boolean running = true;
private Thread mcss;
public MulticastSender() {
System.out.println("New " + this.getClass().getSimpleName());
try {
socket = new MulticastSocket(MulticastConstants.MULTICAST_PORT);
mcss = new MulticastServerThread();
} catch (IOException e) {
System.out.println("Error creating MulticastSender: " + e.getMessage());
}
}
#Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("Starting " + this.getClass().getSimpleName());
mcss.start();
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("Stopping " + this.getClass().getSimpleName());
this.running = false;
socket.disconnect();
socket.close();
}
private class MulticastServerThread extends Thread {
public MulticastServerThread() {
super("MulticastServer");
}
public void run() {
System.out.println("Start sending multicast packets ...");
while (running) {
System.out.println("Sending multicast packet ...");
try {
byte[] dataBuffer = MulticastConstants.MULTICAST_PACKET_DATA.getBytes();
InetAddress group = InetAddress.getByName(MulticastConstants.MULTICAST_CHANNEL);
DatagramPacket packet = new DatagramPacket(dataBuffer, dataBuffer.length, group, MulticastConstants.MULTICAST_PORT);
socket.send(packet);
System.out.println("Packet send ...");
try {
sleep(2000);
} catch (InterruptedException e) {
}
} catch (IOException e) {
System.out.println("Error sending multicast packet: " + e.getMessage());
running = false;
break;
}
}
socket.close();
}
}
}
Client side code:
The client only receives a single packet for simplicity. You might create a thread to not to freeze your clients gui.
public class MulticastReceiver {
private MulticastSocket socket;
private InetAddress address;
public static void main(String[] args) throws InterruptedException {
new MulticastReceiver();
}
public MulticastReceiver() {
System.out.println("Starting MulticastReceiver ...");
try {
address = InetAddress.getByName(MulticastConstants.MULTICAST_CHANNEL);
socket = new MulticastSocket(MulticastConstants.MULTICAST_PORT);
socket.joinGroup(address);
DatagramPacket packet;
try {
byte[] buf = new byte[MulticastConstants.MULTICAST_PACKET_SIZE];
packet = new DatagramPacket(buf, buf.length);
System.out.println("Waiting for packets ...");
socket.receive(packet);
System.out.println("Received a packet (" + packet.getLength() + " bytes) ...");
// deserialize packet.getData() to your own object (for simplicity a String is used) ...
// check if type and serialVersionId are ok, otherwise dispose packet ...
System.out.println("Server is located at: " + packet.getAddress());
socket.close();
// connect to server ...
} catch (IOException ex) {
ex.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
} catch (UnknownHostException e) {
System.out.println("Could not connect to host \"" + address + "\": " + e.getMessage());
} catch (Exception e) {
System.out.println("Error initializing: " + e.getMessage());
}
}
}
Tested on Glassfish 4, hope that helps.
I'm writing a client-server pair in java using rmi java. I want the server to listen for a connection, and while one client is connected the server should reject any other clients that try to connect.
You would need to start the RMI registry in code using http://download.oracle.com/javase/6/docs/api/java/rmi/registry/LocateRegistry.html#createRegistry%28int,%20java.rmi.server.RMIClientSocketFactory,%20java.rmi.server.RMIServerSocketFactory%29, and write a custom RMIServerSocketFactory that returns a ServerSocket that only accepts a single connection.
EDIT: with a mashup of LocateRegistry.createRegistry and http://download.oracle.com/javase/1.5.0/docs/guide/rmi/hello/Server.java with a little extra code thrown in (note that I didn't compile this, so you will need to sort out any compile errors yourself; it is intended to show you the general usage):
EDIT 2: fixed it to incorporate #EJP's suggestion (for more detail see this).
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Server implements Hello {
public Server() {}
public String sayHello() {
return "Hello, world!";
}
public static void main(String args[]) {
try {
Server obj = new Server();
RMIClientSocketFactory csf = new RMIClientSocketFactory() {
#Override
public Socket createSocket(String host, int port) throws IOException {
InetAddress addr = InetAddress.getByName(host);
if (addr.equals(InetAddress.getLocalHost())) {
return new Socket(addr, port);
} else {
throw new IOException("remote socket bind forbidden.");
}
}
};
RMIServerSocketFactory ssf = new RMIServerSocketFactory() {
#Override
public ServerSocket createServerSocket(int port) throws IOException {
System.out.println("RMIServerSocketFactory().createServerSocket()");
return new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
}
};
Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0, csf, ssf);
// Bind the remote object's stub in the registry
Registry registry = LocateRegistry.createRegistry(uri.getPort(), csf, ssf);
registry.bind("Hello", stub);
System.err.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
One simple home-grown approach would be to pass everything through a single method which keeps track of the number of requesting threads. If there is already a requesting thread in progress, an exception can be thrown. Otherwise, the requesting thread is allowed to proceed.
Some code to illustrate the concept but not necessarily an implementation ...
public class RequestHandler {
private boolean activeRequest = false;
public void handeRequest() throws Exception {
synchronized(this) {
if (activeRequest) {
throw new Exception("Request in progress");
}
else {
activeRequest = true;
}
}
// delegate to something else to perform the request logic
synchronized(this) {
activeRequest = false;
}
}
}
All you have to do here is declare your remote method(s) as synchronized. Then only one client can enter it/them at a time.