Sending and receiving objects using sockets and threads not working properly - java

I am currently creating a service allowing to send objects from a client to a server and vice-versa, but experiencing an issue that I unfortunately cannot explain and fix.
First of all, here are the useful classes (I haven't put all methods such as getters and setters in this post).
/**
* This launcher creates a NetworkInterface, waits for a connection, sends a message to the connected client and waits for an incoming message
*
*/
public class ServerLauncher {
public static void main(String[] args) {
try {
NetworkSystem n = new NetworkSystem(4096);
n.startServerManager();
while (n.getCommunications().isEmpty()) {
// this line is unexpectedly magic
System.out.println("Waiting for a new connection...");
}
do {
n.getCommunications().get(0).send(new String("Hello, are you available?"));
} while (n.getCommunications().get(0).getReceiveManager().getReadObjects().isEmpty());
System.out.println(n.getCommunications().get(0).getReceiveManager().getReadObjects().get(0));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* This launcher creates a NetworkSystem, connects to the server, waits for an incoming message and anwers back
*
*/
public class ClientLauncher {
public static void main(String[] args) {
try {
NetworkSystem n = new NetworkSystem(8192);
n.instanciateCommunication(new Socket(InetAddress.getLocalHost(), 4096));
while (n.getCommunications().get(0).getReceiveManager().getReadObjects().isEmpty()) {
// this line is unexpectedly magic
System.out.println("Waiting for an object...");
}
System.out.println(n.getCommunications().get(0).getReceiveManager().getReadObjects().get(0));
n.getCommunications().get(0).getSendManager().send(new String("No, I am not! We will talk later..."));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* This class handles every incoming messages.
*/
public class ReceiveManager implements Runnable {
private ObjectInputStream inputStream;
private CommunicationManager communicationManager;
private List readObjects;
private boolean receive;
public ReceiveManager(CommunicationManager communicationManager) throws IOException {
this.communicationManager = communicationManager;
this.inputStream = new ObjectInputStream(this.communicationManager.getSocket().getInputStream());
this.readObjects = new ArrayList();
this.receive = true;
}
#Override
public void run() {
Object object = null;
try {
while ((object = this.inputStream.readObject()) != null && this.hasToReceive()) {
this.readObjects.add(object);
}
} catch (ClassNotFoundException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
this.setContinueToReceive(false);
}
}
private boolean hasToReceive() {
return this.receive;
}
public void setContinueToReceive(boolean value) {
this.receive = value;
}
}
/**
* This class allows the user to send messages
*/
public class SendManager {
private ObjectOutputStream outputStream;
private CommunicationManager communicationManager;
public SendManager(CommunicationManager communicationManager) throws IOException {
this.communicationManager = communicationManager;
this.outputStream = new ObjectOutputStream(this.communicationManager.getSocket().getOutputStream());
}
public void send(Object object) throws IOException {
this.outputStream.writeObject(object);
this.outputStream.flush();
}
}
So basically, as you may have noticed in the ServerLauncher and the ClientLauncher, there are two "magic" instructions. When those two lines are commented and I run the server then the client, nothing happens. The server and the client are simply running and never stop. However, when I uncomment these two magic lines, every works like a charm: messages are properly sent and received.
Would you guys know the reason of this unexpected behaviour ?
Oh yeah, I forgot, if you guys want me to upload everything to test the project or whatever, just tell me :-)

You're starving the CPU with those spin loops. You should sleep or wait while the queues are empty, or better still just take()from blocking queues.
NB Your loop condition isn't correct:
readObject() doesn't return null at end of stream. It throws EOFException.
You should also test hasToReceive() before calling readObject() rather than afterwards. Otherwise you always do an extra read.

Related

Java Concurrent Sockets: not being able to share a variable between threads

I happen to have a problem with some attempts at reading the same variable in socket multi-threading, not being able to share it among threads.
It works as an app where an employer assigns work to an employee. Through his interface the employer can add and assignment to an ArrayList inside a class named ListadoPedidos.
When the employer's ServerSocket accepts an employee Socket, it starts a TCP connection and launches the following thread:
public class HiloServer implements Runnable{
private ListadoPedidos peds=new ListadoPedidos();
private ListadoOperarios operarios=new ListadoOperarios();
private ListadoSockets sockets=new ListadoSockets();
private SocketServer s;
public HiloServer(SocketServer sock, JFrame frame, ListadoPedidos pedidos) {
s=sock;
peds=pedidos;
}
/* (non-Javadoc)
* #see java.lang.Runnable#run()
*/
#Override
public void run() {
boolean agregar;
Socket nuevo;
try {
while(true) {
// ACEPTA OPERARIOS QUE DESEEN CONECTARSE
s.aceptar();
nuevo=s.getSocket();
sockets.addSocket(nuevo);
new NuevoCliente();
HiloDatos hd=new HiloDatos(s, nuevo,operarios,peds,sockets);
Thread t=new Thread(hd);
t.start();
}
}
catch (IOException e) {
e.printStackTrace();
}
}
}
*note that I send the object where the assigments added are stored.
Then it starts another thread that will work as a sort of "validation" for a number the employee's have to insert and send through his Swing interface to truly enter the system. This thread is generated everytime a new socket employee makes a TCP connection to the ServerSocket employer. It goes like:
public class HiloDatos implements Runnable {
private int n;
private Socket cliente;
private SocketServer server;
private int opRecibido;
private ListadoOperarios ops;
private ListadoPedidos peds;
private ListadoSockets socks;
public HiloDatos(SocketServer ss, Socket nuevo, ListadoOperarios operarios, ListadoPedidos pedidos, ListadoSockets sockets) {
cliente=nuevo;
server=ss;
ops=operarios;
peds=pedidos;
socks=sockets;
}
#Override
public void run() {
server.setSocket(cliente);
boolean agregar, aceptado=false;
try {
do {
// RECIBE EL NRO OPERARIO Y VERIFICA SU EXISTENCIA
agregar=true;
opRecibido=Integer.parseInt(server.recibir());
for(int c=0;c<ops.getOperarios().size();c++) {
if (opRecibido==ops.getOperarios().get(c)) {
new ErrorRepetido();
agregar=false;break;
}
}
if (agregar==true) {
ops.addOperarios(opRecibido);
server.enviar("Si");
aceptado=true;
}
}while(aceptado==false);
HiloPedidos hp=new HiloPedidos(server,opRecibido,ops,peds,socks);
Thread t=new Thread(hp);
t.start();
}catch (NumberFormatException e) {
new ErrorDatos();
} catch (ConnectException e) {
new ErrorConexion();
} catch (SocketException e) {
try {
socks.getSockets().remove(socks.getSockets().indexOf(cliente));
cliente.close();
} catch (IOException e1) {
new ErrorFlujo();
}
new WarnSocket();
} catch (IOException e) {
try {
socks.getSockets().remove(socks.getSockets().indexOf(cliente));
cliente.close();
} catch (IOException e1) {
new ErrorFlujo();
}
new WarnFlujo();
}
}
}
And lastly it launches yet another Thread that looks for that same validation number from the thread above in the ArrayList of assignments ("pedidos" of class ListadoPedidos) i kept passing from thread to thread, and if it finds a "new" one, it should send it to the connected socket:
public class HiloPedidos implements Runnable {
private Pedido ped;
private SocketServer server;
private int op;
private ListadoOperarios ops;
private ListadoPedidos peds;
private ListadoSockets socks;
public HiloPedidos(SocketServer ss, int opRecibido, ListadoOperarios operarios, ListadoPedidos pedidos, ListadoSockets sockets) {
server=ss;
opRecibido=op;
ops=operarios;
peds=pedidos;
socks=sockets;
}
#Override
public void run() {
int cambio=0, nuevo;
Pedido pedRecibido;
try {
while(true) {
// ENVÍA PEDIDOS
nuevo=peds.Contar(op);
if(nuevo==cambio) {
cambio=peds.Contar(op);
pedRecibido=peds.TraerNuevo(op, cambio);
server.enviarObjeto(pedRecibido);
}
}}
catch (NumberFormatException e) {
new ErrorDatos();
} catch (ConnectException e) {
new ErrorConexion();
} catch (SocketException e) {
try {
socks.getSockets().remove(socks.getSockets().indexOf(server.getSocket()));
server.getSocket().close();
} catch (IOException e1) {
new ErrorFlujo();
}
new WarnSocket();
} catch (IOException e) {
try {
socks.getSockets().remove(socks.getSockets().indexOf(server.getSocket()));
server.getSocket().close();
} catch (IOException e1) {
new ErrorFlujo();
}
new WarnFlujo();
}
}
}
Problem is that the last thread can't really notice a change in the list, as i debugged it and never reached the breakpoint inside the condition of sending the assignment. The class ListadoPedidos goes like this:
public class ListadoPedidos {
private static volatile ArrayList<Pedido> pedidos=new ArrayList<>();
public ListadoPedidos() {
}
public ArrayList<Pedido> getPedidos() {
return pedidos;
}
public synchronized void addPedidos(Pedido pedido) {
pedidos.add(pedido);
}
public int Contar(int o) {
int n=0;
for (Pedido p: pedidos) {
if (p.getNro_operario()==o) {
n++;
}
}
return n;
}
public Pedido TraerNuevo(int o, int c) {
int n=0;
Pedido nuevo = new Pedido();
for (Pedido p: pedidos) {
if (p.getNro_operario()==o) {
n++;
}
if (n==c) {
nuevo=p;break;
}
}
return nuevo;
}
}
Contar is the one that counts for an assignment with the value nrooperario same as the value it brings from the thread, and TraerNuevo brings the assignment to be sended (never reached this method).
I tried declaring the ArrayList as volatile and all but nothing works. Mind that even if i use socket connections, the problem has more to do with shared varaible not being able to update between threads. Any help will be appreciated.
Try this, basically, synchronize access.
public class ListadoPedidos {
private static volatile ArrayList<Pedido> pedidos=new ArrayList<>();
public ListadoPedidos() {
}
/**
* Here DO NOT return the arrayList. The underlying implementation is not threadsafe
*/
// public ArrayList<Pedido> getPedidos() {
// return pedidos;
// }
public synchronized void addPedidos(Pedido pedido) {
pedidos.add(pedido);
}
public synchronized int Contar(int o) {
int n=0;
for (Pedido p: pedidos) {
if (p.getNro_operario()==o) {
n++;
}
}
return n;
}
public synchronized Pedido TraerNuevo(int o, int c) {
int n=0;
Pedido nuevo = new Pedido();
for (Pedido p: pedidos) {
if (p.getNro_operario()==o) {
n++;
}
if (n==c) {
nuevo=p;break;
}
}
return nuevo;
}
}
The amount of code you have given us makes it difficult to answer your question. To be honest, the Spanish does not help either. But I can give you some general advice.
Let us start with the question. What exactly is the question? From what I can understand, it comes down to: "how can two threads read the same variable?"
Even if that is not the question, try to make the question as clear as possible for yourself.
Then start with a new test-project separate from the project you are working on. Write the minimal amount of code that you think should work. If it does not work, write even less code that does work (e.g. use static variables to make things even more simple). Go back and forth until you have code that can answer your question. If you cannot get it to work, step back and think about assumptions that you made that might not be true.
If you still cannot figure it out, come back here with the minimal amount of code that you think should work and a clear question.
This method of "trying it with minimal code in a test-project" is something I still use after years of programming to solve problems. When I solve a problem this way, I usually learn something new and often I discover that I made an assumption that did not hold true.

Two server threads in a while loop blocking each other

everybody.
Hope you can help me with this one:
I have two threads, which are tasked with handling connections from a client.
This is my code
ServerSocket loginSocket = new ServerSocket(8000);
ServerSocket fileSocket = new ServerSocket(7000);
while (running) {
new LoginThread(loginSocket.accept(),loginInormation).start();
new LoaderThread(fileSocket.accept()).start();
}
When I try to connect to the loginSocket two times, the server will block and stop working, blocking the client, but this doesn't happen if I delete this:
new LoginThread(loginSocket.accept(),loginInormation).start();
I'm not getting any error messages, so why is this happening and how can I fix this?
The accept() method is a blocking method, which means that your program won't continue until a connection is made with loginSocket().
When you're creating your LoginThread, the program waits a connection to set the first parameter of your object, and it will not continue the execution until a connection is made.
The line new LoginThread(loginSocket.accept(),loginInormation).start(); contains the method call loginSocket.accept(), which will be called before this thread is created. This method call will block until a client logs in. (In addition, the second thread will be blocked by fileSocket.accept()).
As for a solution, I would move the accept() calls to inside each of the Threads. You will need to pass the sockets to the threads for them to do this.
Start fileSocket and login socket in different threads
package com.ca.training.task.app;
import java.io.IOException;
import java.net.ServerSocket;
public class App {
public void execute() {
LoginRunnable loginRunnable = new LoginRunnable();
loginRunnable.setLoginInformation(new Object());//Login information
FileRunnable fileRunnable = new FileRunnable();//Data for loaded runnable.
fileRunnable.setParams(new Object());
startLoginThread(loginRunnable);
startFileThread(fileRunnable);
}
private static void startLoginThread(LoginRunnable loginRunnable) {
Thread loginThread = new Thread(loginRunnable);
loginThread.start();
}
private static void startFileThread(FileRunnable fileRunnable) {
Thread loadedThread = new Thread(fileRunnable);
loadedThread.start();
}
class LoginRunnable implements Runnable {
private Object loginInformation;
#Override
public void run() {
try {
ServerSocket loginSocket = new ServerSocket(8000);
loginSocket.accept();
} catch (IOException e) {
e.printStackTrace();
}
}
public Object getLoginInformation() {
return loginInformation;
}
public void setLoginInformation(Object loginInformation) {
this.loginInformation = loginInformation;
}
}
class FileRunnable implements Runnable {
private Object params;
#Override
public void run() {
try {
ServerSocket fileSocket = new ServerSocket(7000);
} catch (IOException e) {
e.printStackTrace();
}
}
public Object getParams() {
return params;
}
public void setParams(Object params) {
this.params = params;
}
}
}

Why is my boolean not being changed?

So I'm trying to create a client/server program. I want to know when my client disconnects of his own accord, so I've setup a heartbeat system. Every 6 seconds my client sends a ping to my server, if the client doesn't send a ping for a total of 30 seconds the client is considered disconnected and removed from the current connections list (for which I plan to implement a GUI). Or at least, that's the plan.
ConnectionManager.java
public class ConnectionManager implements Runnable{
static Socket connection;
private ArrayList<Thread> allConnections;
private ArrayList<Connection> allConnectionList;
private ServerSocket server;
private int id = 0;
public ConnectionManager() {
allConnections = new ArrayList<Thread>();
allConnectionList = new ArrayList<Connection>();
}
#Override
public void run() {
try {
server = new ServerSocket(5555);
System.out.println("Server is running!");
while(true) {
connection = server.accept();
Connection a = new Connection(connection, id);
Runnable runnable = a;
allConnectionList.add(a);
allConnections.add(new Thread(runnable));
allConnections.get(allConnections.size() - 1).start();
id++;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void removeConnection(int id) {
allConnections.remove(id);
allConnectionList.remove(id);
}
Connection.java
public class Connection implements Runnable {
private Socket a;
public boolean amIActive;
private int id;
public Connection(Socket a, int id) {
amIActive = true;
this.a = a;
this.id = id;
}
public void onConnect() {
try {
String TimeStamp = new java.util.Date().toString();
String formattedAddress = a.getInetAddress().toString().replace("/", "");
System.out.println("Received connection from: " + formattedAddress + " at " + TimeStamp);
Runnable runnable = new ConnectionListener(this);
Thread connectionThread = new Thread(runnable);
connectionThread.start();
String returnCode = "Server repsonded to " + a.getInetAddress().toString().replace("/", "") + " at "+ TimeStamp + (char) 13;
BufferedOutputStream os = new BufferedOutputStream(a.getOutputStream());
OutputStreamWriter osw = new OutputStreamWriter(os, "US-ASCII");
osw.write(returnCode);
osw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void run() {
onConnect();
System.out.println("We got this far!");
while(amIActive) {
whileTrue();
}
System.out.println("This code never gets run because we get stuck in the while loop above");
Main.b.removeConnection(id);
System.out.println("Connection was closed from " + a.getInetAddress());
}
public void setOffline(boolean state) {
this.amIActive = state;
}
public void whileTrue() {
}
public Socket getSocket() {
return a;
}
ConnectionListener.java
public class ConnectionListener implements Runnable{
public Connection myConnection;
public boolean receivedHeartbeat;
public int missedHeartbeats = 0;
public ConnectionListener(Connection a) {
this.myConnection = a;
}
#Override
public void run() {
Runnable runnable = new Heartbeat(this);
Thread thread = new Thread(runnable);
thread.start();
while(myConnection.amIActive) {
try {
BufferedInputStream is;
is = new BufferedInputStream(myConnection.getSocket().getInputStream());
InputStreamReader isr = new InputStreamReader(is);
StringBuffer process = new StringBuffer();
int character;
while((character = isr.read()) != 13) { //GETTING STUCK HERE BECAUSE STUPID.
if(character == -1) {
myConnection.setOffline(true);
} else {
process.append((char)character);
}
}
handleInput(process);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void handleInput(StringBuffer process) {
String messageSent = process.toString();
if(messageSent.equals("Ping!")) {
receivedHeartbeat = true;
}
}
Heartbeat.java
public class Heartbeat implements Runnable{
private ConnectionListener b;
public Heartbeat(ConnectionListener a) {
b = a;
}
#Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
if(b.missedHeartbeats > 5) {
b.myConnection.amIActive = false;
System.out.println("Setting amIActiveToFalse!");
}
if(b.receivedHeartbeat) {
b.receivedHeartbeat = false;
} else {
b.missedHeartbeats++;
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
My console is spammed with System.out.println("Setting amIActiveToFalse!"); from Heartbeat.java. But the while loop in Connection.java keeps running. I believe this might be something to do with my threading, but I can't figure it out.
When you have a non-volatile variable, there is no guarentee of visability of a change in one thread to another. In particular, if the JVM detects that a thread doesn't alter a boolean it can inline it, meaning you will never see the value change.
The simple solution is to make the boolean volatile and it will not be inlined and one thread will see when another changes it.
For more details http://vanillajava.blogspot.com/2012/01/demonstrating-when-volatile-is-required.html
The trivial answer to this is: make the variable volatile.
Without this, it is allowed for the thread changing the value to basically keep its updates in cache, committing them to main memory some time later.
This allows threaded code to run much faster, since it can keep its variables in cache rather than having to fetch from main memory. However, the consequence of this is that other threads don't see the update.
Making the variable volatile prevents this from happening: a thread always reads the value from main memory, and writes are immediately committed.
I say that this is the trivial answer because it doesn't necessarily fix all of your problems. There may also be an atomicity issue: in between one thread reading the variable and writing it again, another thread might sneak in and change its value, which may or may not put the first thread into an undefined state from the perspective of its invariants.
Specifically:
if(b.receivedHeartbeat) { b.receivedHeartbeat = false;
It is possible that some other thread can change b.receivedHeartbeat to false after this thread evaluates it to true, so this iteration is erroneously counted as a "non-missed" heartbeat.
This can be fixed by making the variable a (non-volatile) AtomicBoolean, on which there is an atomic compare-and-set method, which avoids such race conditions.
Java Concurrency In Practice is a great reference on these issues, I wholeheartedly recommend it. Look for the topics "visibility" and "atomicity".
Also read the advanced chapter on the Java Memory Model. That made me doubt myself at first, but made me a much stronger programmer after I digested it.
There are a couple issues I saw while debugging the code you posted, but I was able to successfully get the heartbeat functionality working.
In the Connection Listener class I don't think the if statement with .equals("Ping!") will match, because of the newline character at the end of each line.
In the Connection Listener class I would probably put the socket's Input Stream at the top of the loop not inside the loop. (I don't think this will break it but it's probably nicer this way)
ConnectionListener Updates:
public void run() {
Runnable runnable = new Heartbeat(this);
Thread thread = new Thread(runnable);
thread.start();
BufferedReader br = null;
try {
//is = new BufferedInputStream(myConnection.getSocket().getInputStream());
br = new BufferedReader(new InputStreamReader(myConnection.getSocket().getInputStream()));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
while(myConnection.amIActive) {
try {
String processLine = br.readLine();
System.out.println("handleInput:" + processLine);
handleInput(processLine);
} catch (Exception e) {
System.out.println("Exception!");
e.printStackTrace();
}
}
}
public void handleInput(String messageSent) {
if(messageSent.startsWith("Ping!")) { //Need to use startsWith, or add newline character
receivedHeartbeat = true;
System.out.println("receivedHeartbeat!");
}
}
Also, in your Heartbeat class make sure you reset the missedHeartbeats counter to 0 on true:
if(b.receivedHeartbeat) {
b.receivedHeartbeat = false;
b.missedHeartbeats = 0;
} else {
b.missedHeartbeats++;
}

How can I make the thread sleep for a while and then process all the messages?

I'm writing an Android app that uses two threads. One is UI thread and the other handles server communication. Is it possible for the other thread to wait for a specified amount of time and then process all the messages that have arrived and then wait again?
I need this so that I can collect different data and send it to server in one session.
I've build my thread with HandlerThread but now I'm stuck. Can anyone point me to the right direction?
This is the code I'm using inside the second thread:
public synchronized void waitUntilReady() {
serverHandler = new Handler(getLooper()){
public void handleMessage(Message msg) { // msg queue
switch(msg.what) {
case TEST_MESSAGE:
testMessage(msg);
break;
case UI_MESSAGE:
break;
case SERVER_MESSAGE:
break;
default:
System.out.println(msg.obj != null ? msg.obj.getClass().getName() : "is null");
break;
}
}
};
}
EDIT:
I resolved my issue by going with Thread instead of HandlerThread and using queue.
I'm new to programming so I apologize for any horrenous errors but here's the code I ended up using.
public class ServiceThread extends Thread {
// TODO maybe set the thread priority to background?
static ServiceThread sThread = new ServiceThread(); // service thread instance
private volatile Handler mainHandler;
//
public Thread mainThread;
private boolean OK = true;
public Queue<MessageService> msgQueue;
private ThreadPoolExecutor exec;
private ServiceThread() { }
#Override
public void run() {
synchronized (this){
msgQueue = new ConcurrentLinkedQueue<MessageService>();
notifyAll();
}
mainHandler = new Handler(Looper.getMainLooper());
ThreadPoolExecutor exPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
exec = exPool;
// MAIN LOOP
try {
while(OK) {
getMessagesFromQueue();
Thread.sleep(3000);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//end of loop
}
public void ProcessMessage(MessageService message) {
System.err.println("ProcessMessage with command: " + message.command);
}
/** Called from the Main thread. Waits until msgQueue is instantiated and then passes the reference
* #return Message Queue
*/
public Queue<MessageService> sendQueue() {
synchronized (this){
while(msgQueue == null) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block -- move the try block!
e.printStackTrace();
}
}
}
return msgQueue;
}
public void setOkFalse () {
if (OK == true)
OK = false;
}
// Message handling methods
/** Priority message from UI thread, processed in another thread ASAP.
* Should be used on commands like getBigPicture or getPics when cached pics are running out
* or upload picture etc.
* #param message - Message should always be MessageService class
* TODO check that it really is.
*/
public void prioTask (MessageService message) {
final MessageService taskMsg = message;
Runnable task = new Runnable() {
#Override
public void run(){
ProcessMessage(taskMsg);
}
};
exec.execute(task);
}
/**
* Gets messages from queue, puts them in the list, saves the number of messages retrieved
* and sends them to MessageService.handler(int commands, list messageList)
* (method parameters may change and probably will =) )
*/
public void getMessagesFromQueue() {
int commands = 0;
ArrayList <MessageService> msgList = new ArrayList <MessageService>();
while(!msgQueue.isEmpty()) {
if(msgQueue.peek() instanceof MessageService) {
//put into list?
msgList.add(msgQueue.remove());
commands++;
} else {
//Wrong type of message
msgQueue.remove();
System.err.println("getMessagesFromQueue: Message not" +
" instanceof MessageService, this shouldn't happen!");
}
}
if (commands > 0) {
HTTPConnection conn;
try {
conn = new HTTPConnection();
MessageService.handleSend(commands, msgList, conn);
conn.disconnect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
P.S. This is also my first post here. Should I mark it solved or something? How?

Java concurrent networking issues

I am a Java newbie trying to learn network programming and concurrency, and I thought I'd try out writing a simple chat server where input from a client is echoed to all the clients. That's not happening. I added a couple print statements so that the program will announce that it is waiting for connections and each time it receives a connection. I am using Telnet locally to connect to the port on my machine.
The program announces success for the first and second concurrent connections but then does not announce success for subsequent connections until I close all connections. So, for example, I'll connect from five separate terminals, and the program will announce "Connection 1" and "Connection 2" but will not announce "Connection 3", 4, and 5 until I close all the terminals.
I'm looking for help figuring out where my errors lie as well as general advice for how to approach debugging a situation like this.
In a nutshell, my program has
A Main class, which starts the other three threads
A ClientListener class, which uses a SocketReader to listen for connections and stores the Sockets inputstreams and outputstreams in two Sets.
A MessageReader, which iterates over the inputstreams. If it finds a message, it puts it in a SynchronousQueue and waits for the
MessageWriter to remove it. The MessageWriter sends the message to all the outputstreams.
The code is below. Thanks for any help!
public class Main {
public static void main(String[] args) {
ClientListener clientListener = new ClientListener();
Thread clientListenerThread = new Thread(clientListener);
clientListenerThread.setPriority(Thread.MAX_PRIORITY);
clientListenerThread.start();
MessageReader messageReader = new MessageReader(clientListener);
Thread messageReaderThread = new Thread(messageReader);
messageReaderThread.setPriority(Thread.MIN_PRIORITY);
messageReaderThread.start();
MessageWriter messageWriter = new MessageWriter(messageReader, clientListener);
Thread messageWriterThread = new Thread(messageWriter);
messageWriterThread.setPriority(Thread.NORM_PRIORITY);
messageWriterThread.start();
}
}
public class ClientListener implements Runnable {
private static final int DEFAULT_PORT = 5000;
private Set<Scanner> clientIn = Collections.synchronizedSet(
new LinkedHashSet<Scanner>());
private Set<PrintWriter> clientOut = Collections.synchronizedSet(
new LinkedHashSet<PrintWriter>());
public Set<Scanner> getClientIn() {
return clientIn;
}
public Set<PrintWriter> getClientOut() {
return clientOut;
}
#Override
public void run() {
try {
ServerSocket server = new ServerSocket(DEFAULT_PORT);
System.out.println("Listening for connections...");
int connectionNum = 0;
while(true) {
Socket socket = server.accept();
connectionNum++;
System.out.format("Connection %s%n", connectionNum);
Scanner in = new Scanner(socket.getInputStream());
PrintWriter out = new PrintWriter(socket.getOutputStream());
clientIn.add(in);
clientOut.add(out);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class MessageReader implements Runnable {
private ClientListener clientListener;
private BlockingQueue<String> messages = new SynchronousQueue<String>();
public MessageReader(ClientListener clientListener) {
this.clientListener = clientListener;
}
#Override
public void run() {
while(true) {
Set<Scanner> clients = clientListener.getClientIn();
synchronized (clients) {
for(Scanner client: clients) {
if(client.hasNext()) {
try {
messages.put(client.next());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public String getMessage() throws InterruptedException {
return messages.take();
}
}
public class MessageWriter implements Runnable {
private ClientListener clientListener;
private MessageReader messageReader;
public MessageWriter(
MessageReader messageReader,
ClientListener clientListener) {
this.messageReader = messageReader;
this.clientListener = clientListener;
}
#Override
public void run() {
try {
while(true) {
String message = messageReader.getMessage();
Set<PrintWriter> clients = clientListener.getClientOut();
synchronized (clients) {
for(PrintWriter client: clients) {
client.println(message);
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
I'm not a threading expert, but in class MessageReader there is this line
if(client.hasNext())
Javadoc for Scanner.hasNext() say's "This method may block while waiting for input to scan. The scanner does not advance past any input."
If the scanner is still in wait the synchronized method never proceeds and block all other inputs. And as said in my earlier comment the line which says clientIn.add(in); in class ClientListener probably gets blocked given that its a synchronized Set, but since the print statment is written before it, it might give the impression that Connection 2 was succesfully established.

Categories