I have been trying to find for some time why my Handler does not trigger the events at all but I had no luck in finding it. I will post the entire class and hopefully someone notices a stupid mistake or something.
The class is only managing the lifespan of some sockets (I am doing that because the android devices seem to be very moody when it comes to reopening ports shortly after closing them). The class is a singleton as I want to have awareness of what ports I use everywhere.
package com.ltd.jeefo.alex.routeassistant.wifi.sync.utils;
import android.annotation.SuppressLint;
import android.os.Handler;
import android.support.annotation.Nullable;
import com.ltd.jeefo.alex.routeassistant.logging.ILog;
import com.ltd.jeefo.alex.routeassistant.logging.ScopedLogger;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.HashMap;
/**
* Created by Alex on 16/02/18.
*/
public class SocketsManager {
private static final SocketsManager ourInstance = new SocketsManager();
private static final Handler handler = new Handler();
public static SocketsManager getInstance() {
return ourInstance;
}
#SuppressLint("UseSparseArrays")
private static final HashMap<Integer, ManagedServerSocket> serverSockets = new HashMap<>();
private SocketsManager() {
}
/**
* Method used for obtaining the server socket for a given port
* #param port the port where we want the socket to be opened
* #return the server socket to be used or null if we couldn't start a server socket at that port
*/
public ManagedServerSocket getSocketAtPort(final int port) {
ManagedServerSocket managedServerSocket;
if (serverSockets.containsKey(port)) {
managedServerSocket = serverSockets.get(port);
} else {
managedServerSocket = new ManagedServerSocket(port, handler);
serverSockets.put(port, managedServerSocket);
}
return managedServerSocket;
}
/**
* Closes all the sockets after some delay
*
* #param msDelay the delay in milliseconds
*/
public void closeAllSocketsAfterDelay(final long msDelay) {
for (final ManagedServerSocket managedSocket : serverSockets.values()) {
managedSocket.RequestSocketClosingAfterDelay(msDelay);
}
}
enum ServerSocketStatus {
OPEN_NOT_REQUESTED,
OPEN_FAILED,
OPENED, // there is always a close request started by default, just in case
USER_CLOSE_REQUESTED,
CLOSED
}
public class ManagedServerSocket {
private final ILog logger;
private final static long DEFAULT_CLOSING_TIME_MILLS = 3600000; // one hour
private final static long DEFAULT_RETRY_TIME_MILLS = 15000; // 15 seconds
private final Handler mHandler;
private final int mPort;
private ServerSocket mSocket;
private SocketsManager.ServerSocketStatus serverSocketStatus;
private Long closingTime;
private Runnable defaultClosingRunnable;
private Runnable explicitClosingRunnable;
private ManagedServerSocket(final int port, final Handler handler) {
this.mPort = port;
this.mHandler = handler;
logger = new ScopedLogger(null, ManagedServerSocket.class, "Port", Integer.toString(mPort));
serverSocketStatus = SocketsManager.ServerSocketStatus.OPEN_NOT_REQUESTED;
defaultClosingRunnable = new Runnable() {
#Override
public void run() {
try {
logger.Warn("Server socket closing automatically at port %s after long delay",
Integer.toString(mPort));
mSocket.close();
serverSocketStatus = SocketsManager.ServerSocketStatus.CLOSED;
closingTime = new Date().getTime();
} catch (IOException e) {
e.printStackTrace();
logger.Error(e, "Failed to close server socket at port %s after delay", Integer.toString(mPort));
}
}
};
explicitClosingRunnable = new Runnable() {
#Override
public void run() {
try {
logger.Debug("Socket closing after delay");
mSocket.close();
serverSocketStatus = SocketsManager.ServerSocketStatus.CLOSED;
closingTime = new Date().getTime();
} catch (IOException e) {
e.printStackTrace();
logger.Error(e, "Failed to close server socket after delay");
}
}
};
}
public void RequestSocketClosingAfterDelay(long delayMills) {
logger.Debug("Close socket after delay %s ms", Long.toString(delayMills));
switch (serverSocketStatus) {
case OPEN_NOT_REQUESTED:
logger.Warn("Closing port that was never opened");
return;
case OPEN_FAILED:
logger.Error("Cannot close a socket that failed to open");
return;
case CLOSED:
logger.Warn("Cannot request closing an already closed socket");
return;
case OPENED:
mHandler.removeCallbacks(defaultClosingRunnable);
break;
case USER_CLOSE_REQUESTED:
logger.Warn("Requested to close a socket that was already requested for closing");
mHandler.removeCallbacks(explicitClosingRunnable);
break;
}
mHandler.postDelayed(explicitClosingRunnable, delayMills);
serverSocketStatus = SocketsManager.ServerSocketStatus.USER_CLOSE_REQUESTED;
}
#Nullable
private ServerSocket RequestSocketForUsage() {
boolean socketIsOpened = true;
switch (serverSocketStatus) {
case USER_CLOSE_REQUESTED:
mHandler.removeCallbacks(explicitClosingRunnable);
break;
case OPENED:
mHandler.removeCallbacks(defaultClosingRunnable);
break;
case CLOSED:
case OPEN_FAILED:
case OPEN_NOT_REQUESTED:
socketIsOpened = tryOpenServerSocket();
break;
}
if (!socketIsOpened) {
serverSocketStatus = SocketsManager.ServerSocketStatus.OPEN_FAILED;
logger.Error("Failed to open the socket server and return it");
return null;
}
// Add back the default closing handler now
mHandler.postDelayed(defaultClosingRunnable, DEFAULT_CLOSING_TIME_MILLS);
serverSocketStatus = SocketsManager.ServerSocketStatus.OPENED;
return mSocket;
}
#Nullable
public Socket accept() throws IOException {
ServerSocket socket = RequestSocketForUsage();
if (socket != null) {
return socket.accept();
} else {
return null;
}
}
public SocketsManager.ServerSocketStatus getStatus() {
return serverSocketStatus;
}
private boolean tryOpenServerSocket() {
int attempt = 0;
do {
++attempt;
try {
this.mSocket = new ServerSocket(mPort);
break;
} catch (Exception e) {
logger.Warn(e, "Failed to start ServerSocket on attempt: %s", Integer.toString(attempt));
synchronized (this) {
try {
wait(DEFAULT_RETRY_TIME_MILLS);
} catch (InterruptedException e1) {
logger.Error("Retry wait() interrupted");
e1.printStackTrace();
}
}
}
} while (attempt < 6);
if (this.mSocket == null) {
logger.Error("Failed to start ServerSocket");
return false;
} else {
logger.Info("ServerSocket started");
return true;
}
}
}
}
----------------------------------------- LATER EDIT -----------------------------------------
I am always triggering the getSocketAtPort() method inside asyncTasks which seems to be part of the problem.
I now opened a socket (using getSocketAtPort()) inside an activity (inside onCreate()) before calling it in asyncTasks. When I do that all the handlers start working (including the ones requested inside the asyncTask). My guess is that the problem has something to do with threading.
Related
I'm trying to set up a peer to peer connection in java.
I'm trying to set up my program to listen for an incoming connection while outwardly being able to connect to a different client.
How can I instantiate my socket connection: socketConnection as whatever is connected to the program. Ideally like so:
if(socketConnection.isConnectedToExternalPeer()){
//do stuff
} else if (socketConnection.hasAnIncomingConnection()){
//do stuff
}
After consulting #L.Spillner 's solution I've put together the following code below, this only issue is that I can't quite grasp how to go about accepting a connection, this is evident from the fact that when I try to set up streams the program ends up in a loop while waiting for the peer's reply:
public class Client implements AutoCloseable {
// Any other ThreadPool can be used as well
private ExecutorService cachedExecutor = null;
private ExecutorService singleThreadExecutor = null;
// port this client shall listen on
private int port = 0;
// Name of the client
private String name = null;
// indicates that a connection is ongoing
private boolean isConnected = false;
// the socket the Client is currently connected with
private Socket activeConenctionSocket = null;
// The ServerSocket which will be listening for any incoming connection
private ServerSocket listener = null;
// The socket which has been accepted by the ServerSocket
private Future<Socket> acceptedSocket;
private ObjectInputStream inputStream = null;
private ObjectOutputStream outputStream = null;
private BloomChain bloomChain = null;
/**
* #param port Port number by which this client shall be accessed.
* #param name The name of this Client.
*/
public Client( int port, String name )
{
this.port = port;
this.name = name;
this.bloomChain = new BloomChain();
this.cachedExecutor = Executors.newCachedThreadPool();
this.singleThreadExecutor = Executors.newSingleThreadExecutor();
this.listener = createListeningSocket();
startListening();
}
private ServerSocket createListeningSocket()
{
ServerSocket temp = null;
try
{
temp = new ServerSocket( this.port );
}
catch ( IOException e )
{
e.printStackTrace();
}
return temp;
}
private void startListening()
{
if ( !this.isConnected )
{
this.listener = createListeningSocket();
this.acceptedSocket = this.cachedExecutor.submit( new ServAccept( this.listener ) );
}
}
/**
* Attempts to connect to any other socket specified by the hostname and the targetport.
*
* #param host The hostname of the target to connect.
* #param targetport The port of the target.
*/
public void connect( String host, int targetport )
{
try
{ System.out.println(host);
System.out.println(targetport);
this.activeConenctionSocket = new Socket( InetAddress.getByName( host ), targetport );
setUpStreams(this.activeConenctionSocket);
this.isConnected = true;
System.out.println(InetAddress.getAllByName(host));
}
catch ( IOException e )
{
e.printStackTrace();
}
try
{
this.listener.close();
}
catch ( IOException e )
{
// this will almost certainly throw an exception but it is intended.
}
}
public void setUpStreams(Socket socket) throws IOException {
this.outputStream = new ObjectOutputStream(socket.getOutputStream());
this.outputStream.flush();
this.inputStream = new ObjectInputStream(socket.getInputStream());
}
#Override
public void close() throws Exception
{
// close logic (can be rather nasty)
}
public void sendMessage(String message){
if(bloomChain.size()<1){
bloomChain.addBlock(new Block(message, "0"));
} else {
bloomChain.addBlock(new Block(message, bloomChain.get(bloomChain.size()-1).getPreviousHash()));
}
try {
this.outputStream.writeObject(bloomChain);
this.outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public String mineMessage(){
final String[] receivedMessage = {null};
final Block tempBlock = this.bloomChain.get(this.bloomChain.size()-1);
this.singleThreadExecutor.submit(()->{
tempBlock.mineBlock(bloomChain.getDifficulty());
receivedMessage[0] = tempBlock.getData();
});
return receivedMessage[0];
}
public String dataListener(){
if(isConnected) {
try {
BloomChain tempChain = (BloomChain) this.inputStream.readObject();
if (tempChain.isChainValid()) {
this.bloomChain = tempChain;
return mineMessage();
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return null;
}
public ServerSocket getListener() {
return this.listener;
}
public boolean isConnected(){
return isConnected;
}
public ObjectOutputStream getOutputStream(){
return this.outputStream;
}
public ObjectInputStream getInputStream(){
return this.inputStream;
}
}
EDIT 2:
I tried to await for acceptedSocket.get() to return a socket in a separate thread as follows:
new Thread(()->{
setupStreams(this.acceptedSocket.get());
//try-catch blocks omitted
}).start();
This successfully wait for acceptedSocket to return a connected socket however when I try to connect to another locally running client i get the following error: java.net.SocketException: socket closed
Okay after some tinkering I finally figured out a neat little solution:
We want to be able to listen and connect at the same time so we need a ServerSocket and issue an ServerSocket#accept call to accept incoming cnnections.
However this method is blocking the thread so in order to being able to proceed with our programm we have to outsource this call into another thread and luckly the default Java API does provide a simple way to do so.
The following codesample is not finished but provides the core functionality:
Client.java:
public class Client
implements AutoCloseable
{
// Any other ThreadPool can be used as well
private ExecutorService es = Executors.newCachedThreadPool();
// port this client shall listen on
private int port;
// Name of the client
private String name;
// indicates that a connection is ongoing
private boolean isConnected = false;
// the socket the Client is currently connected with
private Socket activeConenctionSocket;
// The ServerSocket which will be listening for any incoming connection
private ServerSocket listener;
// The socket which has been accepted by the ServerSocket
private Future<Socket> acceptedSocket;
/**
* #param port Port number by which this client shall be accessed.
* #param name The name of this Client.
*/
public Client( int port, String name )
{
this.port = port;
this.name = name;
this.listener = createListeningSocket();
startListening();
}
private ServerSocket createListeningSocket()
{
ServerSocket temp = null;
try
{
temp = new ServerSocket( port );
}
catch ( IOException e )
{
e.printStackTrace();
}
return temp;
}
private void startListening()
{
if ( !isConnected )
{
listener = createListeningSocket();
acceptedSocket = es.submit( new ServAccept( listener ) );
}
}
/**
* Attempts to connect to any other socket specified by the hostname and the targetport.
*
* #param host The hostname of the target to connect.
* #param targetport The port of the target.
*/
public void connect( String host, int targetport )
{
isConnected = true;
try
{
activeConenctionSocket = new Socket( InetAddress.getByName( host ), targetport );
}
catch ( IOException e )
{
e.printStackTrace();
}
try
{
listener.close();
}
catch ( IOException e )
{
// this will almost certainly throw an exception but it is intended.
}
}
#Override
public void close() throws Exception
{
// close logic (can be rather nasty)
}
}
Let's walk through there step by step on how we instantiate a new Client object:
When we instantiate our object we create a new ServerSocket
We start listenting by creating a new Thread of a Callable<V> Object which I've named ServAccept for example purposes.
Now we have a Future<T> object which will contain a socket if any connection gets accepted.
A positive side effect of the startListening() method is, that you can make it public and call it once more if the connection has dropped.
The conenct(...) method almost works the same way as your setupConnection() method but with a small twist. The ServerSocket, which is still listening in another thread, will be close. The reason for this is, that there is no other way to exit the accept() method the other thread is stuck in.
The last thing (which you have to figure out) is when to check if the Future object is already done.
ServAccept.java
public class ServAccept
implements Callable<Socket>
{
ServerSocket serv;
public ServAccept( ServerSocket sock )
{
this.serv = sock;
}
#Override
public Socket call() throws Exception
{
return serv.accept();
}
}
EDIT:
As a matter of fact I have to admit that my approach might not be a very well rounded approach for the task so I decided to change tweak some things. This time instead of using a Future Object I decided to go with Events / a custom EventListener which is just sitting there and listening for a connection to receive. I tested the connection functionality and it works just fine but I haven't implemented a solution to determine if a Client really conncted to a peer. I just made sure that a client can only hold one connection at a time.
The changes:
ServerAccept.java
import java.io.IOException;
import java.net.ServerSocket;
public class ServAccept implements Runnable
{
private ServerSocket serv;
private ConnectionReceivedListener listener;
public ServAccept( ServerSocket sock,ConnectionReceivedListener con )
{
this.serv = sock;
this.listener = con;
}
#Override
public void run()
{
try
{
listener.onConnectionReceived( new ConnectionReceivedEvent( serv.accept() ) );
} catch (IOException e)
{
// planned exception here.
}
}
}
Does no longer implement Callable<V> but Runnable the only reason for that change is that we do not longer await any return since we will work with a listener and some juicy events. Anyway in order to do so we need to create and pass a listener to this object. But first we should take a look at the listener / event structure:
ConnectionReceivedListener.java
import java.util.EventListener;
#FunctionalInterface
public interface ConnectionReceivedListener extends EventListener
{
public void onConnectionReceived(ConnectionReceivedEvent event);
}
Just a simple interface from what we build some anonymous classes or lambda expressions. Nothing to fancy. It doen't even need to extend the EventListener interface but I love to do that to remind me what the purpose of the class is.
ConnectionReceivedEvent.java
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class ConnectionReceivedEvent
{
private Socket accepted;
public ConnectionReceivedEvent( Socket sock )
{
this.accepted = sock;
}
public Socket getSocket()
{
return accepted;
}
public OutputStream getOutput() throws IOException
{
return accepted.getOutputStream();
}
public InputStream getInput() throws IOException
{
return accepted.getInputStream();
}
public int getPort()
{
return accepted.getPort();
}
}
Nothing to fancy as well, just passing a Socket as a constructor parameter and defining some getters from which most will not be used in this example.
But how to we use it now?
private void startListening()
{
if (!isConnected)
{
closeIfNotNull();
listener = createListeningSocket();
es.execute( new ServAccept( listener, event -> setAccepted( event.getSocket() ) ) );
}
}
private void setAccepted( Socket socket )
{
if (!isConnected)
{
this.activeConenctionSocket = socket;
setUpStreams( socket );
} else
{
sendError( socket );
}
}
We still make use of our ExecutorService and creating a new Thread with the ServAccept class. However since we do not expect any return I changed from ExecutorService#submit to ExecutorService#execute (just a matter of opinion and taste).
But ServAccept needs two arguments now. The ServerSocket and the Listener to use. Fortunately we can use annonymous classes and since our Listener does only feature one method we can even use a lambda expression. event -> setAccepted(event.getSocket()).
As an answer to your 2nd edit: I did a logical mistake. Not the ServerSocket#close method does throw the exception whe interrupting a ServerSocket#accept call but rather the accept() call itself throws the exception. In other words the exception you got was intended and i suppressed another one by mistake.
I am working on a project trying to make several people be able to control a robot arm. For this they have to connect to a Java server that then sends the commands to a robot screen for video conferencing.
I am trying to have a thread for each client and then I want to be able to switch between the different clients based on sound, because I want the speaker to be able to control the robot.
The clients all provide positional data and the level of sound taken by the kinect, and sent to the server in the form of a string.
I am having problems with performing the switch. Currently they seem to be switching back and forth and it makes the robot go haywire.
Is there a good way of comparing the threads to each other, find the appropriate one, switch to that, all the while checking the other threads to see if or when they become the most appropriate one? While also checking in case other clients try to connect to the server?
Thank you for your help.
I also include my code in case you want to look through it and get a better idea.
This is the server class:
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Hashtable;
public class MultiThreadedServer implements Runnable {
protected int serverPort = 8888;
protected ServerSocket serverSocket = null;
protected boolean isStopped = false;
protected Thread runningThread = null;
protected Thread clientThread = null;
protected Thread threadThread = null;
private Hashtable<Long, WorkerRunnable> Users = new Hashtable<Long, WorkerRunnable>();
private ArrayList<Thread> ClientThreads = new ArrayList<Thread>();
private WorkerRunnable client = null;
private ThreadHandler threadHandler = null;
private int sound_max = 0;
private boolean once = true;
public MultiThreadedServer (int port) {
this.serverPort = port;
}
public void run() {
synchronized(this) {
this.runningThread = Thread.currentThread();
}
openServerSocket();
threadHandler = new ThreadHandler();
while( !isStopped() ) {
Socket clientSocket = null;
try {
System.out.println(InetAddress.getLocalHost());
clientSocket = this.serverSocket.accept(); // Connect to clients
} catch (SocketTimeoutException e) {
} catch (IOException e) {
if( isStopped() ) {
System.out.println("Server Stopped");
return;
}
throw new RuntimeException("Error accepting client connection", e);
}
client = new WorkerRunnable(clientSocket, "Multithreaded Server");//Class does client work
clientThread = new Thread(client); // Make a thread for each client
clientThread.start(); // start thread
threadHandler.setUp(client, clientThread); // Set up the thread handler
if ( once == true) { // make sure the threadHandler thread is only created once
threadThread = new Thread(threadHandler);
threadThread.start();
once = false;
}
}
System.out.println("Server Stopped");
}
/**
* Check if the socket is stopped
* #return true if the socket is stopped
*/
private synchronized boolean isStopped() {
return this.isStopped;
}
/**
* Stop and close the socket
*/
public synchronized void stop() {
this.isStopped = true;
try {
this.serverSocket.close();
} catch (IOException e) {
throw new RuntimeException("Error closing server", e);
}
}
/**
* Open server socket
*/
private void openServerSocket() {
try {
this.serverSocket = new ServerSocket(this.serverPort);
} catch (IOException e) {
throw new RuntimeException("Cannot open port 8888", e);
}
}
}
This is the Worker class, that handles the data from the clients:
import gnu.io.NoSuchPortException;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class WorkerRunnable implements Runnable {
protected Socket clientSocket = null;
protected String serverText = null;
private BufferedReader inFromClient;
private DataOutputStream outToClient;
private int[] currentPos = new int[6];
private boolean connected = false;
static TwoWaySerialComm serialCom = null;
static MultiServoState mState;
static int sound_average;
int[] degrees = new int[7];
int count = 0;
public WorkerRunnable(Socket clientSocket, String serverText) {
this.clientSocket = clientSocket;
this.serverText = serverText;
initCurrentPos();
if (serialCom == null) {
serialCom = new TwoWaySerialComm();
}
try {
if (!serialCom.isConnected("COM5")) {
try {
serialCom.connect("COM5");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mState = new MultiServoState(serialCom);
}
} catch (NoSuchPortException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void run() {
try {
work();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
}
}
public void work() throws InterruptedException {
try {
InputStream input = clientSocket.getInputStream();
OutputStream output = clientSocket.getOutputStream();
inFromClient = new BufferedReader(new InputStreamReader(input));
outToClient = new DataOutputStream(output);
long time = System.currentTimeMillis();
updateData();
String message = null;
long endTime = System.currentTimeMillis() + 2000;
while ((message = (String) inFromClient.readLine()) != null) {
System.out.println("Message Received: " + message);
parse(message);
sound_average = degrees[6];
//
// Send the positional data to the robot
//
mState.runServo(degrees[0], degrees[1], degrees[2],
degrees[3], degrees[4], degrees[5]);
//
// Send a response information to the client application
//
currentPos[0] = mState.getCurrentPos(0);
currentPos[1] = mState.getCurrentPos(1);
currentPos[2] = mState.getCurrentPos(2);
currentPos[3] = mState.getCurrentPos(3);
currentPos[4] = mState.getCurrentPos(4);
try {
updateData();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("Request processed: " + time);
} catch (IOException e) {
// report exception somewhere
e.printStackTrace();
}
}
/**
* Initiate the robot's starting position.
*/
public void initCurrentPos()
{
currentPos[0] = 100;
currentPos[1] = 100;
currentPos[2] = 100;
currentPos[3] = 100;
currentPos[4] = 100;
currentPos[5] = 0;
}
/**
* Send the data to the client
*
* #throws IOException
*/
public void updateData() throws IOException {
String sentence = Integer.toString(currentPos[0]) + ", " +
Integer.toString(currentPos[1]) + ", " +
Integer.toString(currentPos[2]) + ", " +
Integer.toString(currentPos[3]) + ", " +
Integer.toString(currentPos[4]) + "." + "\n";
outToClient.flush();
outToClient.writeBytes(sentence);
}
/**
* Get the clients sound average
* #param message
*/
public int getSoundAverage() {
return sound_average;
}
public void parse(String message) {
if (message != null) {
char c;
StringBuilder sb = new StringBuilder(4);
int j = 0;
boolean help = false;
for (int i = 0; i < message.length(); i++) {
c = message.charAt(i);
if (Character.isDigit(c)) {
sb.append(c);
help = true;
}
if (!Character.isDigit(c) && help == true) {
degrees[j] = Integer.parseInt(sb.toString());
j++;
help = false;
sb.delete(0, sb.length());
}
}
}
System.out.println("Waiting for client message...");
}
/**
* Close all connections
*/
public void close() {
if (connected) {
synchronized (this) {
connected = false;
}
if (outToClient != null) {
try {
outToClient.close();
synchronized (this) {
outToClient = null;
}
} catch (IOException e) {
// there is nothing we can do: ignore it
}
}
if (inFromClient != null) {
try {
inFromClient.close();
synchronized (this) {
inFromClient = null;
}
} catch (IOException e) {
// there is nothing we can do: ignore it
}
}
if (clientSocket != null) {
try {
clientSocket.close();
synchronized (this) {
clientSocket = null;
}
} catch (IOException e) {
// there is nothing we can do: ignore it
}
}
}
}
public void returnThread() {
return;
}
}
The final class is the thread handler where I try to compare sound levels and yield all threads except the loudest one:
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.Hashtable;
import com.research.aserver.WorkerRunnable;
public class ThreadHandler implements Runnable {
protected boolean isStopped = false;
protected Thread runningThread = null;
protected Thread clientThread = null;
private Hashtable<Long, WorkerRunnable> Users = new Hashtable<Long, WorkerRunnable>();
private ArrayList<Thread> ClientThreads = new ArrayList<Thread>();
private WorkerRunnable client = null;
private int sound_max = 0;
private int index = 0;
public ThreadHandler() {
}
public void setUp(WorkerRunnable client, Thread clientThread) {
this.client = client;
this.clientThread = clientThread;
Users.put(clientThread.getId(), this.client); // Place clients in a list with its thread ID as key
ClientThreads.add(this.clientThread); // List of client threads
}
#Override
public void run() {
long endTime = System.currentTimeMillis() + 2000; // Help variable to check every 2 sec
while (!Users.isEmpty() && !ClientThreads.isEmpty()) {
for (int i = 0; i < ClientThreads.size(); i++) { // Remove clients and threads if no longer active
if (!ClientThreads.get(i).isAlive()) {
Users.remove(ClientThreads.get(i).getId());
ClientThreads.get(i).interrupt();
ClientThreads.remove(i);
}
}
if(System.currentTimeMillis() >= endTime) { // Do work every 2 sec
for (int i = 0; i < ClientThreads.size(); i++) { // Get the client with the loudest sound
if (sound_max < Users.get(ClientThreads.get(i).getId()).getSoundAverage()) {
sound_max = Users.get(ClientThreads.get(i).getId()).getSoundAverage();
index = i;
}
}
for (int i = 0; i < ClientThreads.size(); i++) { // yield all threads that are not the loudest
if (Users.get(ClientThreads.get(index).getId()) != Users.get(ClientThreads.get(i).getId())){
ClientThreads.get(i).yield();
index = 0;
}
}
endTime = System.currentTimeMillis() + 2000; // update time
}
sound_max = 0;
}
}
}
One idea might be to use a PriorityBlockingQueue and define a quality value for each input, which is then sort by quality automatically inside the list.
Using this your consumer thread can simply fetch the first one in line and process it, knowing that it is the most appropriate one, while the generator threads can simply throw all input in the Queue.
I'm trying to implement WebSockets with a Javascript-based client and a Java-based server. I think I've done all the correct steps, but for an unknown reason, I can't establish the connection with both.
When the server socket receives a connection, it handles to form a websocket-accept response, and it sends back to the client, but the connection in the client socket instantly close, weird that there's no handshake problem.
Does anyone have an idea what might be the problem?
Here's my server code implemented in java:
package server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import server.message.Message;
import server.message.SpeakMessage;
public class Server implements ConnectionListener {
private static final int PORT = 1509;
private MessageDispatcher dispatcher = new MessageDispatcher();
private List<ConnectionManager> clients = new ArrayList<>();
public void listen() {
try (ServerSocket server = new ServerSocket(PORT)) {
System.out.printf("Listening on port %d...%n", PORT);
while (true) {
System.out.println("Waiting for connection...");
Socket client = server.accept();
System.out.println("Incoming connection - Attempting to establish connection...");
ConnectionManager manager = new ConnectionManager(client, dispatcher, this);
manager.start();
}
} catch (IOException e) {
System.out.println("Unable to start server");
e.printStackTrace();
}
System.exit(0);
}
public void execute() {
try {
while (true) {
if (dispatcher.isEmpty()) {
Thread.sleep(100);
continue;
}
Message msg = dispatcher.read();
if (msg instanceof SpeakMessage)
broadcast(MessageEncoder.spoke(((SpeakMessage) msg).getText()));
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
public static void main(String[] args) {
final Server server = new Server();
new Thread(new Runnable() {
#Override
public void run() {
server.listen();
}
}).start();
server.execute();
}
public synchronized void broadcast(byte[] message) {
for (ConnectionManager client : clients) {
client.send(message);
}
}
#Override
public synchronized void clientConnected(ConnectionManager who) {
clients.add(who);
System.out.println("Connected client " + clients.size());
}
#Override
public synchronized void clientDisconnected(ConnectionManager who) {
clients.remove(who);
}
}
Heres subclass ConnectionManager of server:
package server;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.Socket;
import java.security.MessageDigest;
import java.util.Properties;
import server.message.HandshakeMessage;
import server.message.Message;
public class ConnectionManager {
private static final int CLIENT_VERSION = 1;
private Socket socket;
private MessageDecoder decoder = new MessageDecoder();
private MessageDispatcher dispatcher;
private ConnectionListener listener;
public ConnectionManager(Socket connection, MessageDispatcher dispatcher, ConnectionListener listener) {
socket = connection;
this.dispatcher = dispatcher;
this.listener = listener;
}
public void start() {
Thread t = new Thread(new ChannelReader());
t.setName("Client thread");
t.setDaemon(true);
t.start();
}
public void send(byte[] data) {
if (socket == null)
return;
try {
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.write(data);
dos.flush();
} catch (IOException e) {
disconnect("Client closed the connection");
}
}
private class ChannelReader implements Runnable {
private boolean accepted = false;
private String ret = null;
#Override
public void run() {
try {
DataInputStream in = new DataInputStream(socket.getInputStream());
while (socket != null && socket.isConnected()) {
int len = in.readShort();
if (len < 0) {
disconnect("Invalid message length.");
}
String s;
readLine(in);
Properties props = new Properties();
while((s=readLine(in)) != null && !s.equals("")) {
String[] q = s.split(": ");
props.put(q[0], q[1]);
}
if(props.get("Upgrade").equals("websocket") && props.get("Sec-WebSocket-Version").equals("13")) { // check if is websocket 8
String key = (String) props.get("Sec-WebSocket-Key");
String r = key + "" + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // magic key
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.reset();
md.update(r.getBytes());
byte[] sha1hash = md.digest();
String returnBase = base64(sha1hash);
ret = "HTTP/1.1 101 Switching Protocols\r\n";
ret+="Upgrade: websocket\r\n";
ret+="Connection: Upgrade\r\n";
ret+="Sec-WebSocket-Accept: "+returnBase;
} else {
disconnect("Client got wrong version of websocket");
}
Message msg = decoder.decode((String) props.get("Sec-WebSocket-Protocol"));
if (!accepted) {
doHandshake(msg);
} else if (dispatcher != null) {
dispatcher.dispatch(msg);
}
}
} catch (Exception e) {
disconnect(e.getMessage());
e.printStackTrace();
}
}
private void doHandshake(Message msg) {
if (!(msg instanceof HandshakeMessage)) {
disconnect("Missing handshake message");
return;
}
HandshakeMessage handshake = (HandshakeMessage) msg;
if (handshake.getVersion() != CLIENT_VERSION) {
disconnect("Client failed in handshake.");
return;
}
send(ret.getBytes());
accepted = true;
listener.clientConnected(ConnectionManager.this);
}
private String base64(byte[] input) throws ClassNotFoundException,
SecurityException, NoSuchMethodException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> c = Class.forName("sun.misc.BASE64Encoder");
java.lang.reflect.Method m = c.getMethod("encode", new Class<?>[]{byte[].class});
String s = (String) m.invoke(c.newInstance(), input);
return s;
}
private String readLine(InputStream in) {
try{
String line = "";
int pread;
int read = 0;
while(true) {
pread = read;
read = in.read();
if(read!=13&&read!=10)
line += (char) read;
if(pread==13&&read==10) break;
}
return line;
}catch(IOException ex){
}
return null;
}
}
public synchronized void disconnect(String message) {
System.err.println(message);
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
}
}
socket = null;
listener.clientDisconnected(ConnectionManager.this);
}
}
And the MessageDispatcher:
package server;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingDeque;
import server.message.Message;
public class MessageDispatcher {
Queue<Message> messageQueue = new LinkedBlockingDeque<>();
public void dispatch(Message message) {
messageQueue.offer(message);
}
public Message read() {
return messageQueue.poll();
}
public boolean isEmpty() {
return messageQueue.isEmpty();
}
}
And heres my client code implemented in javascript:
var canvas, // Canvas DOM element
ctx, // Canvas rendering context
socket; // Socket connection
function init() {
// Initialise the canvas
canvas = document.getElementById("gameCanvas");
ctx = canvas.getContext("2d");
// Maximise the canvas
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Initialise socket connection
if (window.WebSocket) {
socket = new WebSocket("ws://localhost:1509/", ["1", "YURI"]);
socket.onopen = onSocketConnected();
socket.onclose = onSocketDisconnect();
socket.onmessage = onSocketMessage();
socket.onerror = onSocketError();
} else {
alert("The browser does not support websocket.");
}
};
// Socket message
function onSocketMessage(message) {
console.log('Message: ' + message.data);
};
// Socket error
function onSocketError(error) {
console.log('Error: ' + error.data);
};
// Socket connected
function onSocketConnected() {
console.log("Connected to socket server");
};
// Socket disconnected
function onSocketDisconnect() {
console.log("Disconnected from socket server");
};
I think, it is because you are using the Socket Package on the Java Server Side and the WebSocket API on the Client Side. Your idea is really good but the wrong technology.
Keep the WebSocket on the Client Side (Javascript) becaue you don't have lots of other possibilities, but try JWebSocket on the Server side (Java). In Fact WebSocket is using TCP/IP but its own communication protocol over TCP/IP. The Java Socket Package is purely TCP/IP. Re-write your server with JWebSocket, all details about JWebSocket can be found at:
http://jwebsocket.org/.
I hope my answer will help you.
you must specify end of return packet with "\r\n\r\n"
ret = "HTTP/1.1 101 Switching Protocols\r\n";
ret+="Upgrade: websocket\r\n";
ret+="Connection: Upgrade\r\n";
ret+="Sec-WebSocket-Accept: "+returnBase + "\r\n\r\n";
and for create accept key i use
public class WSKeyGenerator {
private final static String MAGIC_KEY =
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
public static String getKey(String strWebSocketKey) throws
NoSuchAlgorithmException {
strWebSocketKey += MAGIC_KEY;
MessageDigest shaMD = MessageDigest.getInstance("SHA-1");
shaMD.reset();
shaMD.update(strWebSocketKey.getBytes());
byte messageDigest[] = shaMD.digest();
BASE64Encoder b64 = new BASE64Encoder();
return b64.encode(messageDigest);
}
}
I recommend that use the http://websocket.org/echo.html to check the server's websocket functionality
I have Vector of threads, and i wanna check all items in this vector. Everyone item is connection of user to server. I wanna "clean" all dead connections.
I can't find where I'm wrong.
Here is it my code :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package server;
import java.util.Iterator;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import server.ServerCore.Clients;
/**
*
* #author pisio
*/
public class GrimReaper extends Thread {
private int timeout = LoadSettings.Init().getConfigInt("grimreaper") * 1000; // 1000 ms = 1 sec
public GrimReaper() {
super();
}
public void cleanUserThreads() {
Vector users = ServerCore.users;
if (users.size() < 1) {
return;
}
Iterator iteratr = users.iterator();
while (iteratr.hasNext()) {
Clients user = (Clients) iteratr.next();
System.out.println(user.isAlive());
if (user.getClient().isClosed()) {
user.interrupt();
if (user.isInterrupted()) {
System.out.println("Beshe kiknat");
}
iteratr.remove();
// if (PublicVaribles.Init().systemLevelMesseging() == 2) {
System.out.println("+\t Kicked user ");
// }
}//if is dead
}//while
}//cleanUserThreads;
#Override
public void run() {
try {
while (ServerCore.getServerRunning()) {
cleanUserThreads();
sleep(timeout);
System.out.println("nani na shinigami");
}
} catch (InterruptedException ex) {
Logger.getLogger(GrimReaper.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
package server;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import server.DB.DataBase;
public class ServerCore extends Thread {
private static ServerCore sc = null;
private int port = LoadSettings.Init().getConfigInt("port");
private int max_connections = LoadSettings.Init().getConfigInt("max_connections");
private String ipServer = LoadSettings.Init().getConfig("ipServer");
private ServerSocket socket;
private static boolean serverRuning = false;
public static Vector users = new Vector();
public GrimReaper shinigami = new GrimReaper();// Shinigami from Japanice is Grim Reaper!
private ServerCore() {
}
#Override
public void run() {
shinigami.start();
try {
socket = new ServerSocket(port, max_connections);
System.out.println("+++\t Server was started at address:" + socket.getLocalSocketAddress() + " with posible max users " + max_connections);
serverRuning = true;
while (serverRuning) {
Socket client = socket.accept();
shinigami.cleanUserThreads();
if (users.size() < max_connections) {
Clients cls = new Clients(client);
cls.start();
users.add(cls);
System.out.println("++\tClient was connected:" + client.toString());
} else {
Clients cls = new Clients(client);
cls.start();
cls.getOutput().println("sorry_no_avable_slot");
cls.getOutput().flush();
cls.interrupt();
}
}
} catch (IOException ex) {
// Logger.getLogger(ServerCore.class.getName()).log(Level.SEVERE, null, ex);
}
}
//run method
public void sendUserMsg() {
Scanner input = PublicVaribles.Init().inputKeyBord();
System.out.print("Enter UserID/user connection port:");
int userID = input.nextInt();
Iterator iterator = users.iterator();
while (iterator.hasNext()) {
Clients cls = (Clients) iterator.next();
/// System.out.println("Passed user:" + cls.getUserId());
if (cls.getUserId() == userID) {
System.out.print("\nEnter msg:");
String str = input.next();
cls.getOutput().println(str);
System.out.println("+\t" + cls.getUserId() + " get msg :" + str);
}
}
}
//SendUserMsg
public void stopServer() {
statusServer();
serverRuning = false;
try {
socket.close();
} catch (IOException ex) {
Logger.getLogger(ServerCore.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("+++\t SERVER WAS STOPED !");
}
//Stop server
public void statusServer() {
if (serverRuning) {
System.out.println("+++\t Server running at port:" + port + " with connected users :" + users.size() + "/" + max_connections);
} else {
System.out.println("+++\t Server IS NOT RUNNING!");
}
}
//Status server
public static boolean getServerRunning() {
// function for GrimReaper .... R.I.P :D
return ServerCore.serverRuning;
}
public static ServerCore Init() {
if (ServerCore.sc == null) {
ServerCore.sc = new ServerCore();
}
return ServerCore.sc;
}
// SingleTon
public class Clients extends Thread {
private Socket client;
private int userID;
private Scanner input;
private PrintWriter output;
public Clients(Socket socket) {
client = socket;
userID = socket.getPort();
try {
input = new Scanner(client.getInputStream());
output = new PrintWriter(client.getOutputStream(), true);
} catch (IOException ioEx) {
System.out.println(ioEx.toString());
}
}
public int getUserId() {
return userID;
}
public Scanner getInput() {
return input;
}
public PrintWriter getOutput() {
return output;
}
public Socket getClient() {
return client;
}
}//Clients Class
}
Note: I'm assuming Clients extends Thread.
It looks like you might be using interrupt() incorrectly. After calling user.interrupt(), it's up to that thread to check that it has been interrupted by calling Thread.interrupted() and terminate itself if true. Here's an extremely basic example:
class Clients extends Thread {
#Override
public void run() {
while (!Thread.interrupted()) {
//do work
}
//thread has been interrupted and quits
}
}
I have two threads that I'm dealing with Java NIO for non-blocking sockets. This is what the threads are doing:
Thread 1:
A loop that calls on the select() method of a selector. If any keys are available, they are processed accordingly.
Thread 2:
Occasionally registers a SocketChannel to the selector by calling register().
The problem is, unless the timeout for select() is very small (like around 100ms), the call to register() will block indefinitely. Even though the channel is configured to be nonblocking, and the javadocs state that the Selector object is thread safe (but it's selection keys are not, I know).
So anyone have any ideas on what the issue could be? The application works perfectly if I put everything in one thread. No problems occur then, but I'd really like to have separate threads. Any help is appreciated. I've posted my example code below:
Change the select(1000) to select(100) and it'll work. Leave it as select() or select(1000) and it won't.
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class UDPSocket
{
private DatagramChannel clientChannel;
private String dstHost;
private int dstPort;
private static Selector recvSelector;
private static volatile boolean initialized;
private static ExecutorService eventQueue = Executors.newSingleThreadExecutor();
public static void init()
{
initialized = true;
try
{
recvSelector = Selector.open();
}
catch (IOException e)
{
System.err.println(e);
}
Thread t = new Thread(new Runnable()
{
#Override
public void run()
{
while(initialized)
{
readData();
Thread.yield();
}
}
});
t.start();
}
public static void shutdown()
{
initialized = false;
}
private static void readData()
{
try
{
int numKeys = recvSelector.select(1000);
if (numKeys > 0)
{
Iterator i = recvSelector.selectedKeys().iterator();
while(i.hasNext())
{
SelectionKey key = i.next();
i.remove();
if (key.isValid() && key.isReadable())
{
DatagramChannel channel = (DatagramChannel) key.channel();
// allocate every time we receive so that it's a copy that won't get erased
final ByteBuffer buffer = ByteBuffer.allocate(Short.MAX_VALUE);
channel.receive(buffer);
buffer.flip();
final SocketSubscriber subscriber = (SocketSubscriber) key.attachment();
// let user handle event on a dedicated thread
eventQueue.execute(new Runnable()
{
#Override
public void run()
{
subscriber.onData(buffer);
}
});
}
}
}
}
catch (IOException e)
{
System.err.println(e);
}
}
public UDPSocket(String dstHost, int dstPort)
{
try
{
this.dstHost = dstHost;
this.dstPort = dstPort;
clientChannel = DatagramChannel.open();
clientChannel.configureBlocking(false);
}
catch (IOException e)
{
System.err.println(e);
}
}
public void addListener(SocketSubscriber subscriber)
{
try
{
DatagramChannel serverChannel = DatagramChannel.open();
serverChannel.configureBlocking(false);
DatagramSocket socket = serverChannel.socket();
socket.bind(new InetSocketAddress(dstPort));
SelectionKey key = serverChannel.register(recvSelector, SelectionKey.OP_READ);
key.attach(subscriber);
}
catch (IOException e)
{
System.err.println(e);
}
}
public void send(ByteBuffer buffer)
{
try
{
clientChannel.send(buffer, new InetSocketAddress(dstHost, dstPort));
}
catch (IOException e)
{
System.err.println(e);
}
}
public void close()
{
try
{
clientChannel.close();
}
catch (IOException e)
{
System.err.println(e);
}
}
}
import java.nio.ByteBuffer;
public interface SocketSubscriber
{
public void onData(ByteBuffer data);
}
Example usage:
public class Test implements SocketSubscriber
{
public static void main(String[] args) throws Exception
{
UDPSocket.init();
UDPSocket test = new UDPSocket("localhost", 1234);
test.addListener(new Test());
UDPSocket test2 = new UDPSocket("localhost", 4321);
test2.addListener(new Test());
System.out.println("Listening...");
ByteBuffer buffer = ByteBuffer.allocate(500);
test.send(buffer);
buffer.rewind();
test2.send(buffer);
System.out.println("Data sent...");
Thread.sleep(5000);
UDPSocket.shutdown();
}
#Override
public void onData(ByteBuffer data)
{
System.out.println("Received " + data.limit() + " bytes of data.");
}
}
The Selector has several documented levels of internal synchronization, and you are running into them all. Call wakeup() on the selector before you call register(). Make sure the select() loop works correctly if there are zero selected keys, which is what will happen on wakeup().
I ran into the same issue today (that is "wakeupAndRegister" not being available). I hope my solution might be helpful:
Create a sync object:
Object registeringSync = new Object();
Register a channel by doing:
synchronized (registeringSync) {
selector.wakeup(); // Wakes up a CURRENT or (important) NEXT select
// !!! Might run into a deadlock "between" these lines if not using the lock !!!
// To force it, insert Thread.sleep(1000); here
channel.register(selector, ...);
}
The thread should do the following:
public void run() {
while (initialized) {
if (selector.select() != 0) { // Blocks until "wakeup"
// Iterate through selected keys
}
synchronized (registeringSync) { } // Cannot continue until "register" is complete
}
}