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
Related
I have a Service class that communicates with my another process, let's say process_A, by local socket.
My Service class is as follows:
public class MyService extends Service {
private LocalSocket localSock;
private LocalSocketAddress localSockAddr;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals(START_SERVICE_ACTION)) {
localSock = new LocalSocket();
localSockAddr = new LocalSocketAddress(LOCAL_SOCK_ADDR, LocalSocketAddress.Namespace.ABSTRACT);
try {
localSock.connect(localSockAddr);
} catch (IOException e) {
// Ignore
}
if (localSock.isConnected()) {
new LocalSockInitTask().execute(localSock);
}
} else if (intent.getAction().equals(STOP_SERVICE_ACTION)) {
new LocalSockTermTask().execute(localSock);
}
}
}
The behaviour should be as follows:
When my service is being started by user, the service uses LocalSocket.connect() to connect with process_A. Once connected successfully, the service executes an AsyncTask to send an INIT message to process_A and wait for an INIT message from process_A.
When my service is being stopped by user, the service executes another AsyncTask to send a TERM message to process_A and wait for a TERM message from process_A.
LocalSockInitTask.java:
public class LocalSockInitTask extends AsyncTask<LocalSocket, Void, Boolean> {
#Override
protected Boolean doInBackground(LocalSocket... params) {
LocalSocket localSock = params[0];
FileChannel inChannel;
FileChannel outChannel;
ByteBuffer sendBuf, recvBuf;
byte[] bytes;
String result, recvMsg;
int attempt;
try {
inChannel = new FileInputStream(localSock.getFileDescriptor()).getChannel();
outChannel = new FileOutputStream(localSock.getFileDescriptor()).getChannel();
// Send INIT Message
sendBuf = ByteBuffer.wrap(MSG_INIT.getBytes());
outChannel.write(sendBuf);
// Wait for INIT Message
recvBuf = ByteBuffer.allocate(BUFFER_SIZE);
attempt = 0;
while (inChannel.read(recvBuf) < 0) {
attempt++;
if(attempt == 5)
return false;
Thread.sleep(1000);
}
recvBuf.flip();
bytes = new byte[recvBuf.remaining()];
recvBuf.get(bytes);
result = new String(bytes);
if(!result.equals(MSG_INIT))
return false;
inChannel.close();
outChannel.close();
return true;
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
return false;
}
}
LocalSockTermTask.java is nearly doing the same as LocalSockInitTask.java, the major difference is just the message being send and receive is "MSG_TERM".
The Init task is doing perfectly, both write and read are successful. However, when executing the second AsyncTask (which is LocalSockTermTask), seems both write and read are unsuccessful. I've done some testing on this line:
inChannel.read(recvBuf);
In the first AsyncTask execution (LocalSockInitTask), if nothing can be read, this method will immediately return -1 and that's why I set a while loop and count the attempt.
In the second AsyncTask execution (LocalSockTermTask), if nothing can be read, this method will be blocked, and this makes my while loop and attempt count become useless. This cause the AsyncTask never complete. Also, My process_A is waiting for "MSG_TERM" to terminate, and it remains running, that's why I think outChannel.write(sendBuf) also failed in Term task.
Currently I am passing the LocalSocket object to both AsyncTask and create a pair of in/out FileChannel in the AsyncTask. I've also tried to create a pair of in/out FileChannel in the service and pass the two FileChannel to AsyncTask, but still facing the same problem.
Any help is greatly appreciated!
OK, I just found out that this is my careless mistake. The problem is solved.
My another process handles the TERM message incorrectly, so it just simply ignore the TERM message sent by my AsyncTask, and therefore it continues to run and wait for messages.
Since it ignores the TERM message, it won't send back a TERM message to my AsyncTask, and this cause the inChannel.read(recvBuf) has nothing to read.
The blocking behavior of inChannel.read(recvBuf) is absolutely normal, returning -1 should be the case that I use BufferedReader before I changed to use FileChannel.
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.
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.
I'm writing a server to exchange messages among clients. One issue left to be solved is how to release a channel when a client happens to be closed. What I do is to start a monitor thread in which the all-Clients map is monitored, and I attempt to remove() a channel if an exception been detected when trying write() to it. However, after closing a client, the write() method in monitor thread don't throw an exception so the useless channel will never be released. Anybody know why?
public class ServerMonitor extends Thread{
private Map<String, SocketChannel> allClients;
private Set set;
private Iterator it;
private Entry entry;
private SocketChannel channel;
private ByteBuffer buf;
public ServerMonitor(Map<String, SocketChannel> allClients) {
this.allClients = allClients;
buf = ByteBuffer.allocateDirect(10);
byte b = 0;
buf.put(b);
buf.flip();
}
public void run(){
while(true) {
if(!allClients.isEmpty()) {
set = allClients.entrySet();
it = set.iterator();
while(it.hasNext()) {
entry = (Entry) it.next();
channel = (SocketChannel) entry.getValue();
try{
channel.write(buf);
} catch(Exception e) {
allClients.remove(entry.getKey());
//set.remove(entry);
}
}
}
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Writing to a TCP socket is buffered locally and put on the wire asynchronously. So you can't rely on the first write after the peer closes to fail. You can rely on a subsequent write failing, but it could take a number of writes to get there.
I've run into this issue when writing applications that send data over TCP. You've discovered that the only real way to know if a client has closed the connection is by the IOException on a call to write(...). This is pretty much the way it works.
There is a cleaner solution. First of all, you must always handle the case that a client disconnects without you knowing, and properly remove them when you get the IOException on write(...). However, if the client sends a message telling the server it is disconnecting, you can use that to close the connection when you see it.
I'm kinda new to the world of threading, and I'm making a game server, assigning every client who connects to another thread so I can accept multiple clients on the same port. However, I'm having an issue with clients connecting to the server, but not being able to send data (in the format of an ObjectOutputStream to the server). Any pointers on what could be going wrong?
Here's my code for my MatchmakingServer.java
try {
listenForPlayers = new ServerSocket(portNumber);
System.out.println("Port opened. Searching for players");
while (true){
Socket clientSocket = listenForPlayers.accept();
Runnable r = new PlayerHandlerForServer(clientSocket);
new Thread(r).start();
}
} catch (Exception e) { }
My PlayerHandler object implements Runnable and here's its run method.
private Player player;
private ObjectInputStream getPlayerData;
private static PrintWriter sendPlayerData;
private Socket socket;
public void run() {
try {
getPlayerData = new ObjectInputStream(socket.getInputStream());
player = (Player) getPlayerData.readObject();
//do stuff with the player object, this code get executed.
sendPlayerData = new PrintWriter(socket.getOutputStream(),true);
updatePlayersFound(sendPlayerData);
} catch (Exception e) { }
}
As pointed in the comments log the exceptions, they will provide a clue as to what might be causing this problem.
A wild guess would be that your Player class does not implement the Serializable interface.
I'm wondering why you're reading serialized objects from the Socket, but writing out data using a PrintWriter. I would suggest using the ObjectOutputStream and being consistent.
Sending serialized objects might be overkill. There could be more data being sent then you care about. This could cause useless network lag to your game clients! You might want to look at using DataInputStream / DataOutputStream. This would allow you to write / read objects only using what's really necessary.