I'm writing a Connection class that sends and receives data going both ways via Commands and CommandResults. However, when multiple requests are sent quickly over the Connection, some do not make it through properly.
It sound like a race condition of sorts, but I feel like I've prepared for that by:
Locking and unlocking writes to the socket,
having a table of sent Commands whose CommandResults haven't been received,
and locking and unlocking changes to said table.
Commands are received and processed on a single thread, so that shouldn't be the issue.
I've looked over the code enough times that I feel like the problem has to be elsewhere, but my team is very confident that Connection is the culprit.
This sample is a little long, but this was as small as I feel I could make a complete example. I did make sure it was well documented though. The important things to know are:
AwaitWrappers are just a Future. Getting the resource will block until it is actually filled in,
Messages just wrap requests and responses,
a Serializer is basically a gson wrapper,
Commands and CommandResults are tracked with a common UUID,
and ICommandHandlers take in a Command and output a CommandResult. The contents of Commands and CommandResults shouldn't matter for this.
Connection.java:
public class Connection {
private Socket socket;
private ICommandHandler handler;
private Serializer ser;
private Lock resultsLock;
private Lock socketWriteLock;
private Map<UUID,AwaitWrapper<CommandResult>> reservations;
public Connection(Socket socket) {
ser = new Serializer();
reservations = new TreeMap<UUID,AwaitWrapper<CommandResult>>();
handler = null;
this.socket = socket;
// Set up locks
resultsLock = new ReentrantLock();
socketWriteLock = new ReentrantLock();
}
public Connection(String host, int port) throws UnknownHostException, IOException {
socket = new Socket(host, port);
ser = new Serializer();
reservations = new TreeMap<UUID,AwaitWrapper<CommandResult>>();
handler = null;
// Set up locks
resultsLock = new ReentrantLock(true);
socketWriteLock = new ReentrantLock(true);
}
/* Sends a command on the socket, and waits for the response
*
* #param com The command to be sent
* #return The Result of the command operation.
*/
public CommandResult sendCommand(Command com) {
try {
AwaitWrapper<CommandResult> delayedResult = reserveResult(com);
write(new Message(com));
CommandResult res = delayedResult.waitOnResource();
removeReservation(com);
return res;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/* Sets handler for incoming Commands. Also starts listening to the socket
*
* #param handler The handler for incoming Commands
*/
public void setCommandHandler(ICommandHandler handler) {
if (handler == null) return;
this.handler = handler;
startListening();
}
/* Starts a thread that listens to the socket
*
* Note: don't call this until handler has been set!
*/
private void startListening() {
Thread listener = new Thread() {
#Override
public void run() {
while (receiveMessage());
handler.close();
}
};
listener.start();
}
/* Recives all messages (responses _and_ results) on a socket
*
* Note: don't call this until handler has been set!
*
* #return true if successful, false if error
*/
private boolean receiveMessage() {
InputStream in = null;
try {
in = socket.getInputStream();
Message message = (Message)ser.deserialize(in, Message.class);
if (message == null) return false;
if (message.containsCommand()) {
// Handle receiving a command
Command com = message.getCommand();
CommandResult res = handler.handle(com);
write(new Message(res));
} else if (message.containsResult()) {
// Handle receiving a result
CommandResult res = message.getResult();
fulfilReservation(res);
} else {
// Neither command or result...?
return false;
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
//--------------------------
// Thread safe IO operations
private void write(Message mes) throws IOException {
OutputStream out = socket.getOutputStream();
socketWriteLock.lock();
ser.serialize(out, mes);
socketWriteLock.unlock();
}
//----------------------------------
//Thread safe reservation operations
private AwaitWrapper<CommandResult> reserveResult(Command com) {
AwaitWrapper<CommandResult> delayedResult = new AwaitWrapper<CommandResult>();
resultsLock.lock();
reservations.put(com.getUUID(), delayedResult);
resultsLock.unlock();
return delayedResult;
}
private void fulfilReservation(CommandResult res) {
resultsLock.lock();
reservations.get(res.getUUID()).setResource(res);
resultsLock.unlock();
}
private void removeReservation(Command com) {
resultsLock.lock();
reservations.remove(com.getUUID());
resultsLock.unlock();
}
//-------------------------------------------------------------------
// A Message wraps both commands and results for easy deserialization
private class Message {
...
}
}
When monitoring the receiving side of the Connection, the handler never gets triggered for some of the Commands sent. It should be triggered by and process every incoming Command.
I'm considering ditching the reservation table and locking writes to the socket until the the response has been received, but I'm expecting that that won't come without significant performance penalties.
Am I missing some crucial step that would prevent race conditions?
EDIT: Adding the Serializer and ICommandHandler classes for those who are curious.
Serializer.java:
public class Serializer {
private Gson gson;
public Serializer() {
gson = new Gson();
}
public Object deserialize(InputStream is, Class type) throws IOException {
JsonReader reader = new JsonReader(new InputStreamReader(is, StandardCharsets.UTF_8));
reader.setLenient(true);
if (reader.hasNext()) {
Object res = gson.fromJson(reader, type);
return res;
}
return null;
}
public void serialize(OutputStream os, Object obj) throws IOException {
JsonWriter writer = new JsonWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8));
gson.toJson(obj, obj.getClass(), writer);
writer.flush();
}
}
ICommandHandler:
public interface ICommandHandler {
public CommandResult handle(Command com);
public void close();
}
The locks do nothing in your case, there is no race-condition there, if the same handler is used for multiple sockets then the locks need to be inside the handler, you're using a single-threaded client so locks do nothing there, Note: when using locks use a try-finally.
If you're just starting with Sockets you probably don't know this but SocketChannel is a lot more efficient than the Socket class which is extremely old.
I can't help you more than that without seeing Serializer and ICommandHandler.
It is most likely a problem in the Serializer.
Related
im making a networked game that has a server which creates a clientHandler thread every time a client joins. I want to ask the first client that joined if it wants to start the game every time a new client joins, giving it the current number of players connected. Writting through the clientHandlers printwritter gives a nullPointerException, even though ive started the thread before doing this. what could be the problem?
Here is the server code:
`public class Server implements Runnable{
private ArrayList<ClientHandler> handlers = new ArrayList<>();
private ArrayList<Player> players = new ArrayList<>();
private Game game;
private boolean start;
public static void main(String[] args) {
Server server = new Server();
Thread s = new Thread(server);
s.start();
}
public void login(String name){
//todo
for (ClientHandler c : handlers){
if (c.getName().equals(name)){
alreadyTaken(name);//todo
}
else{
players.add(new HumanPlayer(name,c));//todo
}
}
}
public void setStart(){
start = true;
}
private void alreadyTaken(String name) {
}
public void setTurn(ServerHandler sh){
//todo
}
public void updateView(){
}
public String hello() {
return "Hello"; //?
}
public void place(String s){
}
#Override
public void run() {
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(1800);
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println("----Server----");
while (!serverSocket.isClosed()) {
try {
Socket socket = serverSocket.accept();
ClientHandler handler = new ClientHandler(socket,handlers,this);
handlers.add(handler);
Thread h = new Thread(handler);
h.start();
System.out.println("A new client has connected");
System.out.println(handlers.get(0));
handlers.get(0).out.println("START? "+ handlers.size());
if (start){
System.out.println("start request works");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
`
And here's the client handler code:
`public class ClientHandler implements Runnable{
private Socket socket;
private ArrayList<ClientHandler> handlers;
private Server server;
public PrintWriter out;
private BufferedReader in;
private String name;
public ClientHandler(Socket socket, ArrayList<ClientHandler> handlers, Server server){
this.socket = socket;
this.handlers = handlers;
this.server = server;
}
public void broadcastMessage(String msg){
System.out.println("Broadcasting");
for (ClientHandler s : this.handlers){
s.out.println("Player: " + msg);
}
}
public static String removePrefix(String s, String prefix)
{
if (s != null && s.startsWith(prefix)) {
return s.split(prefix, 2)[1];
}
return s;
}
public String getName(){
return name;
}
#Override
public void run() {
try {
out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
new Thread(() -> {
while(socket.isConnected()){
String msg;
try {
msg = in.readLine();
while(msg!=null){
switch (msg.split(" ")[0]){
case "LOGIN":
name = removePrefix(msg,"LOGIN ");
server.login(name);//todo
break;
case "HELLO":
server.hello();//todo
break;
case "PLACE":
server.place(removePrefix(msg,"PLACE "));
break;
case "QUIT":
//todo
break;
case "STOP":
//todo
break;
case "START":
server.setStart();
default:
broadcastMessage(msg);
break;
}
msg = in.readLine();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}`
I tried making a method in the client handler class which does the same thing. The server would just call that instead of writting directing through the PrintWriter, but i got the same error.
Starting a thread does not mean it is guaranteed to actually finish executing the first statement in its run() method before start() returns. In fact,
Usually it won't - starting a thread takes some time, and start() returns as soon as it can.
A JVM that runs a few statements in the thread you just started before start() returns is 'correct' - that is fine. A JVM that doesn't is also fine. Generally you don't want threads, because nothing is predictable anymore. At the very least you want to keep 'inter-thread comms' down to a minimum. Anytime a single field is used from more than one thread, things get very tricky.
What you need is synchronized or other tools to insert predictability in this code.
First, fix a bug
Your ClientHandler's run() code starts another thread for no reason. Take all that out, your run() method in ClientHandler should set up out and in and then immediately do while (socket.isConnected())
Synchronizing
At the very basic level, make a locker object and use notify/wait:
private final Object lock = new Object();
#Override public void run() {
try {
synchronized (lock) {
out = ...;
in = ...;
lock.notifyAll();
}
while (socket.isConnected()) { ... }
out definitely cannot be public here, you can't refer to a stream from multiple threads and expect things to work out!
Just 'fixing' your code involves then using something like:
public OutputStream getOutputStream() {
synchronized (lock) {
while (out == null) {
lock.wait();
}
}
return out;
}
Which will ensure that any thread that wants the out will wait for the other thread to get far enough, but, really, this is just setting you up for another 20 threading problems down the line. Instead, you want one object responsibile for all communication (both outgoing and incoming), and a concurrency-capable queue (there are various collections in the java.util.concurrent package good for this). Then:
Any other threads that want to just send data dump their message in the queue.
You have either 1 thread doing all comms, or 2 (one doing incoming, and one doing outgoing), both dedicated. The outgoing one just loops forever, grabbing objects from the queue and sending them.
If a thread wants to send a message and wait for the response, you need to use .wait() or nicer API from e.g. java.util.concurrent, or, use callback hell - you pass a closure with the code to run once the result is received.
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.
So I'm working on a server that handles a few commands and one small problem is trying to remove the list of active clients when a user decides to log out. Each client is handled in a thread and once the command is done this active client gets removed however its not removing.
Below is an example of removing an active client, this thread is
public class serverHandlerThread implements Runnable
{
private Socket socket;
//private BufferedWriter clientOut;
private ObjectOutputStream toClient;
private MainServer server;
private Users user;
//Constructor
serverHandlerThread(MainServer server, Socket socket)
{
this.server = server;
this.socket = socket;
}
private ObjectOutputStream getWriter()
{
return toClient;
}
private void deleteClient(serverHandlerThread obj)
{
synchronized (server.clients)
{
server.clients.remove(obj);
}
}
#Override
public void run ()
{
try
{
//Setup I/O
toClient = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream fromClient = new ObjectInputStream(socket.getInputStream());
while(!socket.isClosed())
{
//If server has received a message
if(fromClient.available() > 0)
{
//Reads message and objects from client
String input = fromClient.readUTF();
Object obj = fromClient.readObject();
//logger(input);
switch (input)
{
//Logout the user
case ".logout":
//Set the user to being logged out and print the log
user = (Users) obj;
deleteClient(this);
for (int i = 0; i < server.usersList.size(); i++)
{
if (user.getUserName().equals(server.usersList.get(i).getUserName()))
{
server.usersList.get(i).setLoggedIn(false);
logger(user.getUserName() + " has logged out");
}
}
break;
//Push message received to other clients
default:
logger("Sending message to clients");
user = (Users) obj;
deleteClient(this);
logger("clients size is: " + String.valueOf(server.clients.size()));
for (serverHandlerThread thatClient : server.getClients())
{
ObjectOutputStream thatClientOut = thatClient.getWriter();
if (thatClientOut != null)
{
thatClientOut.writeUTF(user.getUserName() + ": " + input + "\r\n");
thatClientOut.flush();
}
}
break;
}
}
}
}
catch (IOException | ClassNotFoundException e)
{
e.printStackTrace();
}
}
}
server is of type MainServer which contains the list of clients and is written as List<ServerHandlerThread> clients. MainServer calls serverHandlerThread when a new client is accepted ie. making the server multithread.
The problem is when the clients requests to logout it should delete the user from the active client list. It doesn't and so when the server tries to push messages to all clients it also tried to write a message to the client who's socket has been closed(user who logged out) and so the server spits out a broken pipe error. Any ideas?
*Edit
More information on the mainServer class, omitted a few things but this should be enough information
public class MainServer
{
//Static variables
private static final int portNumber = 4444;
//Variables
private int serverPort;
private List<serverHandlerThread> clients;
/**
* Very basic logger that prints out
* the current time and date
* #param msg used when printing the log
*/
private void logger(String msg)
{
System.out.println(LocalDate.now()+ " " +LocalTime.now() + " - " +msg);
}
private List<serverHandlerThread> getClients()
{
return clients;
}
//Starts the server and begins accepting clients
private void startServer()
{
clients = new ArrayList<>();
ServerSocket serverSocket;
try
{
serverSocket = new ServerSocket(serverPort);
acceptClients(serverSocket);
}
catch (IOException e)
{
logger("Could not listen on port: " + serverPort);
System.exit(1);
}
}
//Continuously accept clients
private void acceptClients(ServerSocket serverSocket)
{
logger("Server starts port = " + serverSocket.getLocalSocketAddress());
while (true)
{
try
{
Socket socket = serverSocket.accept();
//logger("Accepts: " + socket.getRemoteSocketAddress());
serverHandlerThread client = new serverHandlerThread(this, socket);
Thread thread = new Thread(client);
thread.setDaemon(true);
thread.start();
synchronized(clients)
{
clients.add(client);
}
}
catch (IOException e)
{
System.err.println("Accept failed on:" + serverPort);
}
}
}
public MainServer(int portNumber)
{
this.serverPort = portNumber;
}
public static void main(String[] args)
{
MainServer server = new MainServer(portNumber);
server.startServer();
}
}
*Edit 2
So I've made a little method that synchronizes the client list accross all threads and edited the mainServer to do this as-well but the problem persists
private void deleteClient(serverHandlerThread obj)
{
synchronized (server.clients)
{
server.clients.remove(obj);
}
}
You should probably refactor your code using a client manager pattern to avoid the problem you currently have:
You are managing your clients from N threads, one of which being the server.
You have access to one list with various form of synchronization which may be lead to synchronization issues because the code is all over.
Given this pattern, here is an example (I used synchronized, but other form of synchronization may work):
class ClientManager {
private final List<Client> clients;
public ClientManager() {
this.clients = new ArrayList<>();
}
public synchronized void add(Client client) {
this.clients.add(client);
}
public synchronized void remove(Client client) {
this.clients.remove(client);
}
public synchronized List<Client> list() {
return new ArrayList<>(this.clients);
}
}
Both Client (ServerHandlerThread) and Server (MainServer) will play the ClientManager: my point is that this class is doing all the synchronization work and not the Server/Client.
I use a copy of the list to minimize the lock time (otherwise, client would wait for other thread calling list()). This means that a Client logout may occurs here when you send your message: you'll need to use a flag (alive, etc) indicating if the Client is still there.
You may also check it in the sendMessage and return a status indicating if the message was sent or not.
class Server {
private final ClientManager manager = new ClientManager();
// register new client
Client newClient() {
Client client = new Client(manager);
manager.add(client);
return client;
}
void sendMessageToAll(String msg) {
for (Client client : manager.list()) {
// isAlive returns true except if the client was logged out.
// It should probably be synchronized too.
if (client.isAlive()) {
client.sendMessage(msg);
}
}
}
}
class Client {
private final ClientManager manager;
public Client(ClientManager manager) {
this.manager = manager;
}
public void logoff() {
manager.remove(this);
}
}
Edit: to answer your comment, I added an example of the Client and how server and client use the manager.
Just to clarify you discuss the client having a play in the client
manager which confuses me. My server accepts a client (socket =
serversocket.accept() ) and sends this socket to a new thread, this
thread handles all communication with the client (messaging and
commands). After the thread is started the thread is added to the
client list. The problem is in the thread when the client sends a
command the thread should run the command then delete itself from the
client list (clientlist.remove(this)). Will your solution still work
here? Cause you discussion about Client confused me
Briefly, yes.
Simple: you are using a List, which is by default not synchronized (you could use a Vector or Collections::synchronizedList for that). Because you do that in several thread, there are synchronization issues (aka "random effect" :)).
Instead of using directly the list, you should rather use a class dedicated to this usage: that's what the ClientManager is for. An object which will manage a list of Client.
This has also another advantages: instead of having several synchronized blocks across your code, all code are in one place.
Im facing one problem in streaming data capture for reading the broadcast data during multithreading, pls help or suggest,
Actually there is one class which is reading data from one of the udp socket. Another class accepts the tcp connection from every client request, creates a thread for every client and request the same udp class for data. The thing is working with 1st thread which gets created. But when i request with another client from another pc/ip the packets get losted to the 2nd client/thread
I have made a workaround by creating a list where im storing the Threads outputstream object
and looping it to send the data to all the client. But this is just temporary as it ll delay the packets if clients/connections gets increased.
code for reading UDP Data
public class EventNotifier
{
private InterestingEvent ie;
public DatagramSocket clientSocket;
public String[] split_str;
byte[] receiveData;
HashMap<String, String> secMap = new HashMap<String, String>();
public EventNotifier(InterestingEvent event)
{
ie = event;
clientSocket = new DatagramSocket(9050);
receiveData = new byte[500];
}
public String getDataFeed(String client_id)
{
try
{
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
clientSocket.receive(receivePacket);
String s = new String(receivePacket.getData());
String split_str = s.split(",");
if(secMap.containsValue(split_str[0]))
return s;
else
return "";
} catch(Exception e3) {}
}
}// end of eventNotifier class
code for multithreading handling client requests
public class multiServer
{
static protected List<PrintWriter> writers = new ArrayList<PrintWriter>();
static String client_id = "";
public static void main(String[] args)
{
try
{
ServerSocket servsock = new ServerSocket(8858);
Socket incoming;
while(true)
{
incoming = servsock.accept();
multiServerThread connection = new multiServerThread(incoming);
Thread t1 = new Thread(connection);
t1.start();
}
}
catch(IOException e)
{
System.out.println("couldnt make socket");
}
}
}
class multiServerThread extends Thread implements InterestingEvent
{
Socket incoming;
PrintWriter out=null;
PrintWriter broad=null;
BufferedReader in = null;
String cliString=null;
private EventNotifier en;
int id;
public static String udp_data;
public void interestingEvent(String str1)
{
this.udp_data = str1;
}
public String getUdpData()
{
String _udp_data = this.udp_data;
return _udp_data;
}
multiServerThread(Socket incoming)
{
this.incoming=incoming;
en = new EventNotifier(this);
}
public void run()
{
try
{
out = new PrintWriter(incoming.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(incoming.getInputStream()));
cliString = in.readLine();
multiServer.writers.add(out);
while(true)
{
try
{
udp_data = en.getDataFeed(cliString);
if(udp_data!=null && udp_data.length()>0)
{
//workaround for serving the data to all cleints who are connected
for (int i=0; i<multiServer.writers.size();i++)
{
broad=multiServer.writers.get(i);
broad.println(udp_data.trim());
}
//else will directly write to the outputstream object for every thread which is connected
// out.println(udp_data.trim());
}
}
catch (Exception e)
{
System.out.println("exception "+e);
}
Thread.sleep(1);
}
} catch(IOException e)
{
System.out.print("IO Exception :: "+ e);
}
catch(InterruptedException e)
{
System.out.print("exception "+ e);
}
}
}
You need mutual exclusion (or a different design).
For example, what will happen if two threads call multiServer.writers.add(out); concurrently?
From the ArrayList Javadocs
Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements, or [...])
Another problem is two calling udp_data = en.getDataFeed(cliString); concurrently. The second thread might overwrite the result of the first. You'll loose data!
What happens if one thread calls for (int i=0; i<multiServer.writers.size();i++) while another thread is busy doing multiServer.writers.add(out);? The size may have increased, before out has actually been added to the list!
public class multiServer
{
private List<PrintWriter> writers = new ArrayList<PrintWriter>();
public synchronized void addWriter(PrintWrite out) {
writers.add(out);
}
public synchronized void serveAllWriters(String data) {
for (int i=0; i<multiServer.writers.size();i++)
{
broad=multiServer.writers.get(i);
broad.println(data);
}
}
}
Now when a thread tries to add a writer, the synchronizeds will make sure no other thread is adding or printing. So multiServerThread should be fixed to use the new methods:
class multiServerThread extends Thread implements InterestingEvent
{
//...
private String udp_data;
//...
myMultiServer.addWriter(out);
//...
udp_data = en.getDataFeed(cliString);
if(udp_data!=null && udp_data.length()>0)
myMultiServer.serveAllWriters(udp_data.trim());
//...
}
There might be more problems, not sure I don't fully understand your code. The question you must ask yourself is, can another thread read and/or write the same data or object? Yes? Then you'll need proper synchronization.
I am writing a Java multi-threaded network application and having real difficulty coming up with a way to unit test the object which sends and receives communication from network clients.
The object sends out a message to a number of clients and then waits for responses from the clients.
As each client responds, a dashboard-style GUI is updated.
In more detail...
A Message object represents a text message to be sent and contains an array of Clients which should receive the message.
The Message object is responsible for dispatching itself to all the appropriate clients.
When the dispatch() method is invoked on a Message object, the object spawns a new thread (MessageDispatcher) for each client in the Client array.
Each MessageDispatcher:
opens a new TCP socket (Socket) to the client
delivers the message to its client... PrintWriter out.println(msg text)
creates a 'Status' object which is passed to a Queue in the Message object and then on to the GUI.
Each Status object represents ONE of the following events:
Message passed to Socket (via Printwriter out.println() )
Display receipt received from client (via BufferedReader/InputStreamReader in.readline()... blocks until network input is received )
User acknowledge receipt received from client (via same method as above)
So.. I want to unit test the Message object. (using JUnit)
The unit test is called MessageTest.java (included below).
My first step has been to set up a Message object with a single recipient.
I then used JMockit to create a mock Socket object which can supply a mock OutputStream object (I am using ByteArrayOutputStream which extends OutputStream) to PrintWriter.
Then, when the MessageDispatcher calls (PrintWriter object).out, the message text will be ideally passed to my mock Socket object (via the mock OutputStream) which can check that the message text is OK.
And the sample principle for the InputStreamReader.... The mock Socket object also supplies a mock InputStreamReader object which supplies a mock BufferedReader which is called by the MessageDispatcher (as mentioned previously, MessageDispatcher blocks on in.readLine() ). At this point the mock BufferedReader should supply a fake confirmation to the MessageDispatcher...
// mock Socket
Mockit.redefineMethods(Socket.class, new Object()
{
ByteArrayOutputStream output = new ByteArrayOutputStream();
ByteArrayInputStream input = new ByteArrayInputStream();
public OutputStream getOutputStream()
{
return output;
}
public InputStream getInputStream()
{
return input;
}
});
If this wasn't multi-threaded, this should all work OK. However I have no idea how to do this with multiple threads. Can anyone give me any advice or tips?
Also if you have any input on the design (eg. Message object responsible for its own delivery rather than a separate delivery object.. "dependency injection"-style / separate thread for each client delivery) then I would be interested to hear that too.
UPDATE: here is the code:
Message.java
public class Message {
Client[] to;
String contents;
String status;
StatusListener listener;
BlockingQueue<Status> statusQ;
public Message(Client[] to, String contents, StatusListener listener)
{
this.to = to;
this.contents = contents;
this.listener = listener;
}
public void dispatch()
{
try {
// open a new thread for each client
// keep a linked list of socket references so that all threads can be closed
List<Socket> sockets = Collections.synchronizedList(new ArrayList<Socket>());
// initialise the statusQ for threads to report message status
statusQ = new ArrayBlockingQueue<Status>(to.length*3); // max 3 status objects per thread
// dispatch to each client individually and wait for confirmation
for (int i=0; i < to.length; i++) {
System.out.println("Started new thread");
(new Thread(new MessageDispatcher(to[i], contents, sockets, statusQ))).start();
}
// now, monitor queue and empty the queue as it fills up.. (consumer)
while (true) {
listener.updateStatus(statusQ.take());
}
}
catch (Exception e) { e.printStackTrace(); }
}
// one MessageDispatcher per client
private class MessageDispatcher implements Runnable
{
private Client client;
private String contents;
private List<Socket> sockets;
private BlockingQueue<Status> statusQ;
public MessageDispatcher(Client client, String contents, List<Socket> sockets, BlockingQueue<Status> statusQ) {
this.contents = contents;
this.client = client;
this.sockets = sockets;
this.statusQ = statusQ;
}
public void run() {
try {
// open socket to client
Socket sk = new Socket(client.getAddress(), CLIENTPORT);
// add reference to socket to list
synchronized(sockets) {
sockets.add(sk);
}
PrintWriter out = new PrintWriter(sk.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(sk.getInputStream()));
// send message
out.println(contents);
// confirm dispatch
statusQ.add(new Status(client, "DISPATCHED"));
// wait for display receipt
in.readLine();
statusQ.add(new Status(client, "DISPLAYED"));
// wait for read receipt
in.readLine();
statusQ.add(new Status(client, "READ"));
}
catch (Exception e) { e.printStackTrace(); }
}
}
}
.... and the corresponding unit test:
MessageTest.java
public class MessageTest extends TestCase {
Message msg;
static final String testContents = "hello there";
public void setUp() {
// mock Socket
Mockit.redefineMethods(Socket.class, new Object()
{
ByteArrayOutputStream output = new ByteArrayOutputStream();
ByteArrayInputStream input = new ByteArrayInputStream();
public OutputStream getOutputStream()
{
return output;
}
public InputStream getInputStream()
{
return input;
}
});
// NB
// some code removed here for simplicity
// which uses JMockit to overrides the Client object and give it a fake hostname and address
Client[] testClient = { new Client() };
msg = new Message(testClient, testContents, this);
}
public void tearDown() {
}
public void testDispatch() {
// dispatch to client
msg.dispatch();
}
}
Notice that the sending of multiple messages (multicast) can be achieved in a single blocking method through the NIO API (java.nio) as well, without the creation of new threads. NIO is quite complex, though.
I would start by writing the tests first, with a test-defined StatusListener implementation which stores all update events in a list. When the dispatch() method returns, the test can execute asserts on the state of the event list.
Using threads or NIO is an implementation detail for the Message class. So, unless you don't mind coupling the tests to this implementation detail, I would recommend introducing a helper class that would be responsible for sending multiple asynchronous messages and notifying the Message object upon any asynchronous replies. Then, you can mock the helper class in the unit tests, without coupling them to either threads or NIO.
I successfully implemented a test for the case of sending a message to one client. I also made some changes to the original production code, as follows:
public class Message
{
private static final int CLIENT_PORT = 8000;
// Externally provided:
private final Client[] to;
private final String contents;
private final StatusListener listener;
// Internal state:
private final List<Socket> clientConnections;
private final BlockingQueue<Status> statusQueue;
public Message(Client[] to, String contents, StatusListener listener)
{
this.to = to;
this.contents = contents;
this.listener = listener;
// Keep a list of socket references so that all threads can be closed:
clientConnections = Collections.synchronizedList(new ArrayList<Socket>());
// Initialise the statusQ for threads to report message status:
statusQueue = new ArrayBlockingQueue<Status>(to.length * 3);
}
public void dispatch()
{
// Dispatch to each client individually and wait for confirmation:
sendContentsToEachClientAsynchronously();
Status statusChangeReceived;
do {
try {
// Now, monitor queue and empty the queue as it fills up (consumer):
statusChangeReceived = statusQueue.take();
}
catch (InterruptedException ignore) {
break;
}
}
while (listener.updateStatus(statusChangeReceived));
closeRemainingClientConnections();
}
private void closeRemainingClientConnections()
{
for (Socket connection : clientConnections) {
try {
connection.close();
}
catch (IOException ignore) {
// OK
}
}
clientConnections.clear();
}
private void sendContentsToEachClientAsynchronously()
{
for (Client client : to) {
System.out.println("Started new thread");
new Thread(new MessageDispatcher(client)).start();
}
}
// One MessageDispatcher per client.
private final class MessageDispatcher implements Runnable
{
private final Client client;
MessageDispatcher(Client client) { this.client = client; }
public void run()
{
try {
communicateWithClient();
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
private void communicateWithClient() throws IOException
{
// Open connection to client:
Socket connection = new Socket(client.getAddress(), CLIENT_PORT);
try {
// Add client connection to synchronized list:
clientConnections.add(connection);
sendMessage(connection.getOutputStream());
readRequiredReceipts(connection.getInputStream());
}
finally {
connection.close();
}
}
// Send message and confirm dispatch.
private void sendMessage(OutputStream output)
{
PrintWriter out = new PrintWriter(output, true);
out.println(contents);
statusQueue.add(new Status(client, "DISPATCHED"));
}
private void readRequiredReceipts(InputStream input) throws IOException
{
BufferedReader in = new BufferedReader(new InputStreamReader(input));
// Wait for display receipt:
in.readLine();
statusQueue.add(new Status(client, "DISPLAYED"));
// Wait for read receipt:
in.readLine();
statusQueue.add(new Status(client, "READ"));
}
}
}
public final class MessageTest extends JMockitTest
{
static final String testContents = "hello there";
static final String[] expectedEvents = {"DISPATCHED", "DISPLAYED", "READ"};
#Test
public void testSendMessageToSingleClient()
{
final Client theClient = new Client("client1");
Client[] testClient = {theClient};
new MockUp<Socket>()
{
#Mock(invocations = 1)
void $init(String host, int port)
{
assertEquals(theClient.getAddress(), host);
assertTrue(port > 0);
}
#Mock(invocations = 1)
public OutputStream getOutputStream() { return new ByteArrayOutputStream(); }
#Mock(invocations = 1)
public InputStream getInputStream()
{
return new ByteArrayInputStream("reply1\nreply2\n".getBytes());
}
#Mock(minInvocations = 1) void close() {}
};
StatusListener listener = new MockUp<StatusListener>()
{
int eventIndex;
#Mock(invocations = 3)
boolean updateStatus(Status status)
{
assertSame(theClient, status.getClient());
assertEquals(expectedEvents[eventIndex++], status.getEvent());
return eventIndex < expectedEvents.length;
}
}.getMockInstance();
new Message(testClient, testContents, listener).dispatch();
}
}
The JMockit test above uses the new MockUp class, not yet available in the latest release. It can be replaced with Mockit.setUpMock(Socket.class, new Object() { ... }), though.
perhaps instead of redefining the methods getOutputStream and getInputStream, you can instead use an AbstractFactory in your Message class which creates output and input streams. In normal operation the factory will use a Socket to do that. However, for testing give it a factory which gives it streams of your choosing. That way you have more control over exactly what is happening.