Communication management in client/server application - java

Let me explain the purpose of my application so you can guide me about the best possible approach.
The idea is building a web application to remotely manage some particular equipments that my company manufactures. These equipments will periodically connect to the remote server to send/receive certain data (through simple socket communication but they don't use Java); this data will be stored in the corresponding data base and will be available through the web application for the different users.
In the same way, when you access through the web interface, each client will be able to see their equipments and perform different changes in the configuration. At this point there are 2 possible options and this is the reason of this post:
The easiest but not the best option: the user performs some changes and I save those changes in the data base. When the equipment later establish communication to the server, then it'll read those changes and update its configuration.
The ideal solution: as soon as the user save the changes through the web interface and push the "send" button, those changes are sent to the corresponding equipment.
As mentioned above, these equipments will periodically open a socket communication (let's say every 5 minutes) to the server to send their configuration. At this moment, in order to implement the "ideal solution", the only option I can think of is not to close that socket so I can use it to immediately send information back to the equipment when a certain user makes any changes.
If this application grows along the time, I'm afraid that too many open sockets/threads can crash my application.
Let me illustrate with some code I was playing around. I know this is far from the final solution, it's just to help you understand what I'm looking for.
First of all, I register the socket server during the start-up of the web server (Tomcat in this case):
package org.listeners;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.sockets.KKMultiServer;
public class ApplicationListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
KKMultiServer kKMultiServer = new KKMultiServer();
Thread serverThread = new Thread(kKMultiServer);
serverThread.start();
event.getServletContext().setAttribute("PlainKKMultiServer", kKMultiServer);
}
public void contextDestroyed(ServletContextEvent event) { }
}
This is the main socket server class that listens for new connections:
public class KKMultiServer implements Runnable {
private Map<Long, KKMultiServerThread_v2> createdThreads = new HashMap<Long, KKMultiServerThread_v2>();
#Override
public void run() {
boolean listening = true;
try (ServerSocket serverSocket = new ServerSocket(5000)) {
while (listening) {
KKMultiServerThread_v2 newServerThread = new KKMultiServerThread_v2(serverSocket.accept(), this);
Thread myThread = new Thread(newServerThread);
myThread.start();
Long threadId = myThread.getId();
System.out.println("THREAD ID: " + threadId);
}
} catch (IOException e) {
System.err.println("Could not listen on port " + 5000);
System.exit(-1);
}
}
public Map<Long, KKMultiServerThread_v2> getCreatedThreads() {
return createdThreads;
}
}
And the thread class created with every single petition from each of the equipments (dispensers) to handle the socket communication:
public class KKMultiServerThread_v2 implements Runnable {
private Socket socket = null;
PrintWriter out = null;
BufferedReader in = null;
private long dispenserCode;
private KKMultiServer kKMultiServer;
public KKMultiServerThread_v2(Socket socket, KKMultiServer kKMultiServer) {
this.socket = socket;
this.kKMultiServer = kKMultiServer;
}
public void run() {
try {
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
readDataFromDispenser();
}
private void readDataFromDispenser() {
String inputLine;
try {
while ((inputLine = in.readLine()) != null) {
if (inputLine.equals("Bye")) {
break;
}
if (dispenserCode == 0) {
dispenserCode = 1111; // this code will be unique per equipment
this.kKMultiServer.getCreatedThreads().put(dispenserCode, this);
}
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendDataToDispenser(String dataToSend) {
if (!socket.isClosed() && socket.isConnected()) {
out.println(dataToSend);
} else {
this.kKMultiServer.getCreatedThreads().remove(this);
}
}
}
Now that the socket is created and alive I can use it directly from the web application to send messages back to the equipment (Struts Action in this case)
public class HelloWorldAction extends ActionSupport {
private static final long serialVersionUID = 1L;
public String sendMessageToDispenser() throws Exception {
ServletContext context = ServletActionContext.getServletContext();
KKMultiServer kKMultiServer = (KKMultiServer) context.getAttribute("PlainKKMultiServer");
Map<Long, KKMultiServerThread_v2> currentThreads = kKMultiServer.getCreatedThreads();
Iterator<Long> it = currentThreads.keySet().iterator();
while (it.hasNext()) {
Long key = (Long) it.next();
KKMultiServerThread_v2 currentThread = currentThreads.get(key);
currentThread.sendDataToDispenser("DATA TO YOU!");
}
return SUCCESS;
}
}
Do you think it's possible to perform this solution? I mean, keeping these connections open so I can access my equipments whenever necessary (without waiting for the periodically connections). What's the best approach? If you have any other suggestions please let me know.
Thank you very much.

To my mind it clearly depends on how many equipment will be connected to your system. Sockets are not always sending data so it can have low effect on the overall performance. Though, Socket are know to be a little slow, if you have a lot of data to send to/from your equipments, you should consider this.
If you want to have send data from your server to your client you have few solutions
Your server knows all your equipment after registering for example. When starting you equipment connect to the server. (be careful about local network redirection)
Your equipment and server use sockets to communicate
I don't think there is another solution but I can be wrong. If your equipment request your server every X seconds, it will never be exactly perfeclty on time.

Related

Handling packets through server causes big CPU increase

I have an UDP server which receives audio streams from multiple clients and it must be rerouted to the correct client. The problem which I have is that when 2 clients exchange audio stream with each other then it increases my computer CPU usage by a lot. Even 6 clients that exchange audio between each other makes my CPU usage go as high as 50% and can go all the way up to 100% and a server like this should handle over hundred clients easily.
Because we stream audio it will receive probably thousands of udp packets per second and in order for the packets to receive at their correct client destination I loop through an hashmap for each client so it arrives correctly. Now I suspect that this is probably the cause of the huge CPU usage, is there another better way to know the client destination instead of iterating through an hashmap for each client?
Below is my code where I redistribute my packets:
for (Map.Entry<Session, Integer> entry : sessions.entrySet()) {
Session key = entry.getKey();
int value = entry.getValue(); // session ID
// send packets from client1 to client2
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
if (key.player1.getIp() == ip) {
DatagramPacket dp = new DatagramPacket(buffer, buffer.length,
key.player2.getIp(), key.player2.getPort());
try {
socket1.send(dp);
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
thread.start();
// send packets from client2 to client1
if (key.player2.getIp() == ip) {
DatagramPacket dp = new DatagramPacket(buffer, buffer.length,
key.player1.getIp(), key.player1.getPort());
try {
socket1.send(dp);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Session:
class Session {
//two initialized player objects per session
Player1 player1 = new Player1();
Player2 player2 = new Player2();
class Player1 {
private InetAddress ip;
private int port;
public InetAddress getIp() {
return ip;
}
public void setIp(InetAddress ip) {
this.ip = ip;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}
class Player2 {
private InetAddress ip;
private int port;
public InetAddress getIp() {
return ip;
}
public void setIp(InetAddress ip) {
this.ip = ip;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}
}
Problem 1
You "loop through a hashmap". The sole point of using a hashmap is to be able to look things up by hashing.
Arrange your data structures to fit the requirements of your algorithm. It seems you're looking up by IP. OK, so you need a hash map from IP to something.
Your current hashmap looks on the surface to be designed backwards. The key is a 'session', which I suppose to be a moderately complex structure. The value for that key is a simple integer session id. You don't need a map at all for that: store the id in the session object.
A map from id to session would make some sort of sense, although doesn't seem to quite meet your current needs. But there's no rule that you must have only one map. It's legitimate, and possibly useful, to keep id-to-session and IP-to-session maps. Perhaps even an IP-to-session-partner map.
Data structure design is often more important than code.
Problem 2
You create a thread for every entry in the hashmap. Most of those threads will be 'not the intended IP' and do nothing. The check should be before creating the thread.
Thread creation is expensive and you don't want to be running hundreds of threads to service hundreds of clients. You need a better model, for example with some reasonable number of threads servicing queued requests.
If I were designing this, I think the core of the solution would look like this, in pseudocode:
/**
* Process datagram from 'addr' by sending to partner
*/
void forward(datagram dg, ipaddr from) {
session sess = addr_to_session_map.get(from);
session other = sess.other;
send_datagram(dg, from, other.ipaddr);
}
That is to say, when I receive a datagram, I find its session object from a single map lookup on source address. The session is cross-linked to its partner (it seems from your description that they are paired one-to-one, though that's a simple enough change if I'm wrong).
No loops needed. The overhead is constant for any given client.

Calling Matlab by JADE agent as multi-threading for multiple connections

I am working on JADE (Java) project that connects Matlab by a TCP connection with client-server sockets. Here, JADE creates a server socket and Matlab creates a client socket. I am retrieving some data from Matlab to Java (JADE). The following is my code where I am calling Matlab by JADE through Agent. (1) The issue is I cannot re-run it without re-starting the program again. I believe that I require a multithread java instance with multithread Matlab instance that could connect and synchronize each other. However, I found that Matlab is a single thread. The program throws binding error.
WARNING: Error adding ICP jade.imtp.leap.JICP.JICPPeer#1dbb27d[Cannot bind server socket to localhost port 1099].
jade.core.AgentContainerImpl joinPlatform
SEVERE: Communication failure while joining agent platform: No ICP active
jade.core.IMTPException: No ICP active
I want to run it multiple times without manually re-starting. Here is my JADE code (took help from https://docs.oracle.com/javase/tutorial/networking/sockets/clientServer.html):
public class MatlabComAgent extends Agent
{
ServerSocket srvr = null;
Socket skt = null;
BufferedReader in;
PrintWriter out;
String ip = "localhost";
String filePath;
int port = 1234;
protected void setup()
{
// Get arguments
Object[] args = getArguments();
filePath = (String) args[0];
// Create the TCP connection
try
{
// Create server and socket
srvr = new ServerSocket(port);
skt = srvr.accept();
// Create writer and reader to send and receive data
out = new PrintWriter(skt.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(skt.getInputStream()));
}
catch (IOException e)
{
e.printStackTrace();
}
// Send a message to the tester to say its can start sending requests
sendMessage("Tester","","start-now",ACLMessage.INFORM);
// Run behavior
CommWithMatlab commWithMatlab = new CommWithMatlab();
addBehaviour(commWithMatlab);
} // End setup
Code for Matlab connection:
% Create TCP/IP object 't'. Specify server machine and port number.
% Open the connection with the server
t = tcpip('localhost', 1234);
set(t, 'InputBufferSize', 30000);
set(t, 'OutputBufferSize', 30000);
pause(0.1)
fopen(t);
disp('Connection with JADE established')
I found interesting notes on "socket server which allows multiple connections via threads and Java" Creating a socket server which allows multiple connections via threads and Java page, however, I am not able to do it completely what is said here. May be I am missing something here. (2) I am confused should I edit my Matlab code and/or JADE code for multi-threading.
Here is my code that I tried:
protected void setup()
{
// Get arguments
Object[] args = getArguments();
filePath = (String) args[0];
// Create the TCP connection
try
{
srvr = new ServerSocket(port);
Runnable connectionHandler = new ConnectionHandler(skt);
new Thread(connectionHandler).start();
}
catch (IOException e)
{
e.printStackTrace();
}
Here is new ConnectionHandler class:
public class ConnectionHandler implements Runnable {
private Socket sk=null; //initialize in const'r
BufferedReader in;
PrintWriter out;
public ConnectionHandler(ServerSocket skt) throws IOException
{
sk = skt.accept();
out = new PrintWriter(sk.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(sk.getInputStream()));
}
public void run() {
try
{
// Create writer and reader to send and receive data
out = new PrintWriter(sk.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(sk.getInputStream()));
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
But I got some erorr "java.lang.NullPointerException". Can someone help me to properly code it, what I am missing. Also, (3) this run() in ConnectionHandler class will be invoked automatically? I was confused so I create writer and reader inside Connectionhandler class and its run(). Can I simply make my MatlabComAgent class as multithread without adding any new class. I can make my class as
public class MatlabComAgent extends Agent implements Runnable
{....
....
}
Should I also put the following inside ConnectionHandler class?
// Send a message to the tester to say its can start sending requests
sendMessage("Tester","","start-now",ACLMessage.INFORM);
// Run behavior
CommWithMatlab commWithMatlab = new CommWithMatlab();
addBehaviour(commWithMatlab);
Here, CommWithMatlab class extends SimpleBehavior containg required actions that further passes commands from Matlab to PowerWorld (using another connection). One example is like:
class CommWithMatlab extends SimpleBehaviour
{
private static final long serialVersionUID = 8966535884137111965L;
#Override
public void action()
{
// Wait for a message from another agent requesting something
ACLMessage msg = blockingReceive();
// If this is to open a case
if(msg.getConversationId().equals(OPEN_CASE))
{
openCase(msg.getContent());
}
}
I can simple pass arguments to addagent() and can call runJade(). The following are my JADE run functions using agents:
//Runs JADE and starts the initial agents
public static void runJade() throws ControllerException
{
// Launch JADE platform
Runtime rt = Runtime.instance();
Profile p;
p = new ProfileImpl();
cController = rt.createMainContainer(p);
rt.setCloseVM(true);
// Launch Powerworld interface agent
addAgent(PWRWORLD_NAME, PWRWORLD_CLASS, null);
addAgent(PWRWORLD_TESTER_NAME, PWRWORLD_TESTER_CLASS, null);
//addAgent(PWRWORLD_TESTER_NAME2, PWRWORLD_TESTER_CLASS2, null);
}
private static void addAgent(String name, String type, String arg) throws ControllerException
{
Object[] argsObj = {arg};
AgentController ac = cController.createNewAgent(name, type, argsObj);
ac.start();
}
(4) I have a different program that also creates the same connection. When I try to run one program when other is running, it again throws binding error. However, these programs are completely separate. One program uses port 1234 and other 1239. However, system always assign local port to 1099 to both programs, hence throw binding error in this case.
Any help is appreciable!
Unfortunately, it is not possible to use matlabcontrol over a distributed network. I checked.

Netty 4.0.23 multiple hosts single client

My question is about creating multiple TCP clients to multiple hosts using the same event loop group in Netty 4.0.23 Final, I must admit that I don't quite understand Netty 4's client threading business, especially with the loads of confusing references to Netty 3.X.X implementations I hit through my research on the internet.
with the following code, I establish a connection with a single server, and send random commands using a command queue:
public class TCPsocket {
private static final CircularFifoQueue CommandQueue = new CircularFifoQueue(20);
private final EventLoopGroup workerGroup;
private final TcpClientInitializer tcpHandlerInit; // all handlers shearable
public TCPsocket() {
workerGroup = new NioEventLoopGroup();
tcpHandlerInit = new TcpClientInitializer();
}
public void connect(String host, int port) throws InterruptedException {
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.remoteAddress(host, port);
b.handler(tcpHandlerInit);
Channel ch = b.connect().sync().channel();
ChannelFuture writeCommand = null;
for (;;) {
if (!CommandQueue.isEmpty()) {
writeCommand = ch.writeAndFlush(CommandExecute()); // commandExecute() fetches a command form the commandQueue and encodes it into a byte array
}
if (CommandQueue.isFull()) { // this will never happen ... or should never happen
ch.closeFuture().sync();
break;
}
}
if (writeCommand != null) {
writeCommand.sync();
}
} finally {
workerGroup.shutdownGracefully();
}
}
public static void main(String args[]) throws InterruptedException {
TCPsocket socket = new TCPsocket();
socket.connect("192.168.0.1", 2101);
}
}
in addition to executing commands off of the command queue, this client keeps receiving periodic responses from the serve as a response to an initial command that is sent as soon as the channel becomes active, in one of the registered handlers (in TCPClientInitializer implementation), I have:
#Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.writeAndFlush(firstMessage);
System.out.println("sent first message\n");
}
which activates a feature in the connected-to server, triggering a periodic packet that is returned from the server through the life span of my application.
The problem comes when I try to use this same setup to connect to multiple servers,
by looping through a string array of known server IPs:
public static void main(String args[]) throws InterruptedException {
String[] hosts = new String[]{"192.168.0.2", "192.168.0.4", "192.168.0.5"};
TCPsocket socket = new TCPsocket();
for (String host : hosts) {
socket.connect(host, 2101);
}
}
once the first connection is established, and the server (192.168.0.2) starts sending the designated periodic packets, no other connection is attempted, which (I think) is the result of the main thread waiting on the connection to die, hence never running the second iteration of the for loop, the discussion in this question leads me to think that the connection process is started in a separate thread, allowing the main thread to continue executing, but that's not what I see here, So what is actually happening? And how would I go about implementing multiple hosts connections using the same client in Netty 4.0.23 Final?
Thanks in advance

Java Kryonet servers, client not receiving server response

I am trying to teach myself some networking in Java using the Kryonet library. The following code is almost identical to the code in the kyronet tutorial. https://code.google.com/p/kryonet/#Running_a_server
The client is successfully sending the message "Here is the request!" to the server (the server is printing it out) however the client is not receiving any response from the server even though the server is sending one.
I've tried unsuccessfully to fix it, can anyone see or suggest a possible problem/solution with the code?
(The code follows)
Client
public class Client_test {
Client client = new Client();
public Client_test() {
Kryo kryo = client.getKryo();
kryo.register(SomeRequest.class);
kryo.register(SomeResponse.class);
client.start();
try {
client.connect(50000, "127.0.0.1", 54555, 54777);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
client.addListener(new Listener() {
public void received (Connection connection, Object object) {
if (object instanceof SomeResponse) {
SomeResponse response = (SomeResponse)object;
System.out.println(response.text);
}
}
});
SomeRequest request = new SomeRequest();
request.text = "Here is the request!";
client.sendTCP(request);
}
}
Server
public class ServerGame {
Server server = new Server();
public ServerGame() {
Kryo kryo = server.getKryo();
kryo.register(SomeRequest.class);
kryo.register(SomeResponse.class);
server.start();
try {
server.bind(54555, 54777);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
server.addListener(new Listener() {
public void received (Connection connection, Object object) {
if (object instanceof SomeRequest) {
SomeRequest request = (SomeRequest)object;
System.out.println(request.text);
SomeResponse response = new SomeResponse();
response.text = "Thanks!";
connection.sendTCP(response);
}
}
});
}
}
Response & Request classes
public class SomeRequest {
public String text;
public SomeRequest(){}
}
public class SomeResponse {
public String text;
public SomeResponse(){}
}
After many hours watching youtube videos and sifting through the web I found the answer. Which I will post on here as it seems that quite a few people have had this problem so I would like to spread the word.
Basically the client would shut down immediately, before it could receive and output the message packet. This is because "Starting with r122, client update threads were made into daemon threads, causing the child processes to close as soon as they finish initializing.", the solution is "Maybe you could use this? new Thread(client).start();".
So basically instead of using
client.start();
to start the client thread you must use
new Thread(client).start();
Which I believe stops the thread being made into a daemon thread which therefore stops the problem.
Source: https://groups.google.com/forum/?fromgroups#!topic/kryonet-users/QTHiVmqljgE
Yes, inject a tool like Fiddler in between the two so you can see the traffic going back and forth. It's always easier to debug with greater transparency, more information.

How to override a loop fetching messages in a method

I have a Bluetooth server receiving data from a client, a mobile phone. The code I'm using looks the following
#Override
public void run() {
try {
this.localDevice = LocalDevice.getLocalDevice();
this.localDevice.setDiscoverable(DiscoveryAgent.GIAC);
this.server = (StreamConnectionNotifier) Connector.open(URL);
while(true) {
if(this.connection == null) {
this.connection = this.server.acceptAndOpen();
System.out.println("INFO: Bluetooth client connected");
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.openInputStream()));
this.writer = new BufferedWriter(new OutputStreamWriter(connection.openOutputStream()));
String line;
while((line = reader.readLine()) != null) {
if(line.equals("--#do:disconnect")) {
break;
}
System.out.println("INFO: Received from Bluetooth: " + line);
}
System.out.println("INFO: Client disconnected");
}
}
} catch(BluetoothStateException ex) {
ex.printStackTrace();
} catch(IOException ex) {
ex.printStackTrace();
}
}
As you can see, I have an infinitive loop receiving messages until it is told to stop. At the moment the loop receives all the messages. There is a problem with that. The class where the code is used is a model class in MVC. In the class I also have a method called getContacts(). It is used to receive contacts from the phone over Bluetooth. The phone is told to send the contacts when the server sends --#do:getcontacts.
What I need to do is to get the contacts in an ArrayList in the getContacts() method and return it as the return value of the method so that the controller can handle the contacts.
public ArrayList<Contact> getContacts() {
ArrayList<Contact> contacts = new ArrayList<>();
// How do I get the contacts in the ArrayList?
return contacts;
}
I'll have you some suggestions. My examples are no working code, just a working base for you.
First, I strongly suggest you to use threads in a server. Everytime a clients connects to the server, you create a new thread, with parameters containing all data needed to start it:
boolean running = true; //this class variable will allow you to shut down the server correctly
public void stopServer(){ //this method will shut down the server
this.running = false;
}
public void run() {
...
while(running) {
// if(this.connection == null) { // I removed this line since it's unnecessary, or even harmful!
StreamConnection connection = this.server.acceptAndOpen(); //This line will block until a connection is made...
System.out.println("INFO: Bluetooth client connected");
Thread thread = new ServerThread(connection);
thread.start() //don't forget exception handling...
}
}
And in the class ServerThread you implement these lines handling the clients (non-compiled code, without exception handling!):
Class ServerThread extends Thread {
StreamConnection connection;
public ServerThread(StreamConnection connection){
this.connection = connection;
}
public void run() {
...
connection.close(); //closing the connection...don't forget exception handling!
System.out.println("INFO: Client disconnected");
}
}
What is the advantage of this code? Your server is now able to handle a thousand clients at the same moment. You've got parallelisation, and that's how server usually work! A server without threads is like socks without shoes...
Second, if you have a Java client and a Java server, you can use a much easier way to send your objects to the server: ObjectOutputStream/ObjectInputStream. You just send the array (I'll use an ArraList as it would be usual) containing the contacts to the server, and then you read the array. Here is the code for the server (again uncompiled and without any exception handling):
Class ServerThread extends Thread {
StreamConnection connection;
public ServerThread(StreamConnection connection){
this.connection = connection;
}
public void run() {
BufferedInputStream bis = new BufferedInputStream(this.connection.openInputStream());
ObjectInputStream ois = new ObjectInputStream(bis);
ArrayList contacts = (ArrayList) ois.readObject(); //this is a cast: don't forget exception handling!
//You could also try the method ois.readUTF(); especially if you wanna use other non-Java clients
System.out.println("INFO: Received from Bluetooth: " + contacts);
this.connection.close(); //closing the connection...don't forget exception handling!
//ois.close(); //do this instead of "this.connection.close()" if you want the connection to be open...i.e. to receive more data
System.out.println("INFO: Client disconnected");
//here you do whatever you wanna do with the contacts array, maybe add to your other contacts?
}
}
In Java, every class is an object, including ArrayList. And since the end of the object will be regarded as the disconnection, you won't need to do anything else.
Third: You use above server not only for bluetooth connections, but also for WLAN connections, aso. Then you could easily start different threads, like in pseudo code if(connection.isBluetooth()){//create a thread from BluetoothThread} else if(connection.isWLAN()){//create a thread from WLANsThread}. I don't know what your app is about, but maybe one day you would like to expand it to desktop PC, so using WLAN would be the right thing. Also because you anyway need to build in a verification in the client ("which contacts are going to be sent to which server?"), no matter if it's bluetooth or WLAN, because the low range of buetooth can't give you any security. ;)
Fourth, finally about your question: To get something, you need to have a data source and/or a class variable. Here a short example with a file that stores the contacts (but it could also be a database...local or somewhere else!):
public class MyApp(){
ArrayList contacts;
...
public void run(){ //this happens when we start our app
this.contacts = new ArrayList();
FileReader fr = new FileReader ("C:\WhereverYourFileIs\Contacts.file");
BufferedReader br = new BufferedReader(fr);
//here you use a loop to read the contacts via "br" from the file and fill them into your array...I can't provide you more code, since the exact data structure is up to you.
}
//now we want to send our contacts array to the already connected server:
public sendArrayToServer() {
BufferedOutputStream bos = new BufferedOutputStream (this.connection.openOutputStream());
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this.contacts);
//If you use readUTF() in the server, you need to call here something like oos.writeUTF(this.contacts.toString()); or even need to use another parser method which exactly creates the string you want.
this.connection.close(); //closing the connection...don't forget exception handling!
//oos.close(); //do this instead of "this.connection.close()" if you want the connection to stay open...
}
}
Now in the server you just read out the contacts array as already I described above. What you do with those contacts, remains up to you.
Hope this helps you to understand your problems and find a solution. Programming is all about trial & error..and improving your code.
EDIT:
After our discussion I finally found out what you need: You need a one-thread server called BluetoothManager that interacts with another thread called GUIController. Now since I anyway did the implementation in my head, I can post it for you, together with some explanations. Just note that in this case you don't need to initialize another thread in the server, since the BluetoothManager is already a thread, and you anyway need only one connection at the same moment (the question remains, if that is a "server", I would rather call it a "receiver"):
Public class BluetoothManager extends Thread{
boolean running = true; //this class variable will allow you to shut down the server correctly
GUIController controller;
public BluetoothManager(GUIController controller){
this.controller = controller; //this registers the GUIController in the BluetoothManager
}
public void stop(){ //this method will shut down the "server"
this.running = false;
}
public void run() {
this.localDevice = LocalDevice.getLocalDevice();
this.localDevice.setDiscoverable(DiscoveryAgent.GIAC);
this.server = (StreamConnectionNotifier) Connector.open(URL);
while(running){
StreamConnection connection = this.server.acceptAndOpen(); //This line will block until a connection is made...or running==false!
System.out.println("INFO: Bluetooth client connected");
BufferedInputStream bis = new BufferedInputStream(this.connection.openInputStream());
ObjectInputStream ois = new ObjectInputStream(bis);
ArrayList contacts = (ArrayList) ois.readObject(); //this is a cast: don't forget exception handling!
System.out.println("INFO: Received from Bluetooth: " + contacts);
this.connection.close(); //closing the connection...don't forget exception handling!
System.out.println("INFO: Client disconnected");
this.controller.refreshContacts(contacts);
}
}
}
public class GUIController extends Thread implements Runnable {
ArrayList contacts; //also a HashMap may be appropriate
BluetoothManager manager;
public void run(){
this.contacts = new ArrayList();
FileReader fr = new FileReader ("C:\WhereverYourFileIs\Contacts.file");
BufferedReader br = new BufferedReader(fr);
//here you use a loop to read the contacts via "br" from the file and fill them into your array...I can't provide you more code, since the exact data structure is up to you.
}
public void startBluetoothManager(){ //starting the BluetoothManager
this.manager = new BluetoothManager(this);
this.manager.start();
}
public void abortBluetoothManager(){ //call this when clicking on the "Abort" button
this.manager.stop();
//now the next 2 lines you normally don't need...still may use it if you've problems shutting down the thread:
// try{ this.manager.interrupt(); } //we want to be 100% sure to shut down our thread!
// catch(Exception e){}
this.manager = null; //now the garbage collector can clean everything...byebye
}
public void refreshContacts(ArrayList contacts) {
// synchronize(this.contactArray){ //no synchronisation needed if you have a GUI pop-up with an "Abort"-button!
Iterator i = this.contacts.iterator();
while(i.hasNext()){
this.contacts.add(i.next());
}
//At the end you need remove the "Receiving message" pop-up together with the "Abort Receiving"-button, these are all class variables!
// important note: If you have unique entries, you may need to replace them! In this case I suggest storing all contact objects better in a HashMap contacts, and use the unique ID as a key to find the element. And then you may prompt the user, if there are identical entries, to overwrite each entry or not. These things remain all up to you.
}
}
//As always: This is no compiled code!!
The GUIController first runs the BluetoothManager with startBluetoothManager() and does nothing else, except showing a notification "Receiving contacts" and an "Abort Reveiving"-button. And when the BluetoothManager is finished, he just adds the new contacts into the existing contacts-array inside the GUIController by calling refreshContacts(...). If you push the "Abort Reveiving"-button, you immediately call the abortBluetoothManager() method, which sets running=false in the BluetoothManager to end the server and finish the thread.
The main problem this solution solves: It's not possible for two threads to directly communicate with each other! Once you call thread.start(), every thread is on its own. That's why there is no possibility for the BluetoothManager-thread to tell the GUIController-thread "I've finished!". The only thing those threads can do, is share the same ressource(s), and communicate via this ressource(s). In our case, it's the contacts-ArrayList in the GUIController, which first I thought needs to be synchronized and can be updated by both threads (but not on the same time). And - kind of funnyness - there is a second shared ressource, it's actually the running flag in the class BluetoothManager which can shut it down (but there is never any synchronisation of running needed, this variable is only changed by the GUIController).
Now about the synchronisation: I thought about this problem more and understood, that you can solve your problem also without any "synchronized(...)" call. So, if you don't want to synchronize the ArrayList, you must do it like this: While the server is running, you only show the "Receiving Contacts" pop-up and the "Abort Reveiving"-button. While this happens, you just never access the contact-ArrayList inside the GUIController. This is somehow an "intrinsic synchronisation" which needs no real Java synchronisation. Still you may implement synchronisation, just to be 100% sure that nothing happens if you expand the app in the future.
First of all there are few things in you code need to be reviewed/fixed
1- the ArrayList<Contact> contacts should be defined in your class, so the thread can have access to it and populate it not as a local variable within getContacts() method
public ArrayList<Contact> getContacts() {
//ArrayList<Contact> contacts = new ArrayList<>();
return contacts;
}
2- You should avoid using infinite loop within the run method to be able to stop the thread when you want to.
//while(true)
while(isRunning) { // a flag that is set to true by default
}
3- Checking that the connection equal without setting it to null after disconnecting means that the connection will be accepted only from the first client (assuming that connection is originally set to null) and afterwards your will just have an infinite loop but the code this.connection = this.server.acceptAndOpen(); will not be reachable anymore
if(this.connection == null) {
while((line = reader.readLine()) != null) {
if(line.equals("--#do:disconnect")) {
// You have to set it to null if you want to continue listening after disconnecting
this.connection = null
break;
}
}
}
Or simply remove this check altogether, I see it is useless.
Now back to your question:
You can define your contact list as a class member to be accessible by both run() and getContacts() methods. You can make it final if needed. Then populate this list within the run() method; that's all.
e.g.
class MyServerThread implements Runnable {
private boolean isRunning = true;
ArrayList<Contact> contacts = new ArrayList<>();
public ArrayList<Contact> getContacts(){
// Make sure that your not currently updating the contacts when this method is called
// you can define a boolean flag and/or use synchronization
return contacts;
}
public void run() {
...
while(isRunning ) {
this.connection = this.server.acceptAndOpen();
System.out.println("INFO: Bluetooth client connected");
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.openInputStream()));
this.writer = new BufferedWriter(new OutputStreamWriter(connection.openOutputStream()));
// You need to remove previously received contacts before issuing a new --#do:getcontacts command
contacts.clear();
String line;
while((line = reader.readLine()) != null) {
if(line.equals("--#do:disconnect")) {
break;
}
// Here you can parse the contact information
String contactName = ...
String contactPhone = ...
contacts.add(new Contact(contactName,contactPhone));
}
System.out.println("INFO: Client disconnected");
}
} catch(BluetoothStateException ex) {
ex.printStackTrace();
} catch(IOException ex) {
ex.printStackTrace();
}
}
}
You do not have to use Object serialization, you can construct a simple protocol to send contacts from the phone to the PC, something similar to the commands you send e.g. --#C:name$phone

Categories