How do I test a multi-threading server - java

I have a main thread server which basically listen to whoever want to connect to the port
/**
* The main server thread receives request from and sends
* response to clients.
*/
public class Server {
/*
Port number for the client connection
*/
private static final int PORT = 3000;
/*
The number of client can be connected
*/
private static final int SIZE = 10;
/*
The executor
*/
private static ExecutorService executorService = Executors.newFixedThreadPool(SIZE);
/**
* Starts the main server.
*/
public static void main(String[] args) {
/*
All the information are stored into the queue
*/
BlockingQueue<Message> messageQueue = new LinkedBlockingQueue<>();
/*
All the clients are stored into the map
*/
ConcurrentMap<byte[], Boolean> clientManagement = new ConcurrentHashMap<>();
runMainServer(messageQueue, clientManagement);
}
private static void runMainServer(BlockingQueue<Message> messageQueue, ConcurrentMap<byte[], Boolean> clientManagement) {
try (
ServerSocket serverSocket = new ServerSocket(PORT);
) {
System.out.println("Starting server");
while (true) {
System.out.println("Waiting for request");
Socket socket = serverSocket.accept();
System.out.println("Processing request");
ClientThread newClient = new ClientThread(socket, messageQueue, clientManagement);
executorService.submit(newClient);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
And I have many multi-threading sub-server to handle each identical client. The client is first going to be accepted by the server, and checked if first message that the server received is a connect_message. If it is, then they are officially connected. There are many more message other than connect_message. But I am just not going to be too specific on them.
/**
* The client thread.
*/
public class ClientThread implements Runnable {
private Socket socket;
private BlockingQueue<Message> messageQueue;
private ConcurrentMap<byte[], Boolean> clientManagement;
private byte[] username;
/**
*
*
* #param socket
* #param messageQueue
* #param clientManagement
*/
public ClientThread(Socket socket, BlockingQueue<Message> messageQueue, ConcurrentMap<byte[], Boolean> clientManagement) {
this.socket = socket;
this.messageQueue = messageQueue;
this.clientManagement = clientManagement;
this.username = new byte[1];
}
/**
*
*/
#Override
public void run() {
try (
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
) {
Message m = (Message) in.readObject();
if (m.getIdentifier() == MessageIdentifier.CONNECT_MESSAGE) {
ConnectMessage cm = (ConnectMessage) m;
this.username = cm.getUsername();
clientManagement.put(cm.getUsername(), true);
byte[] cntMsg = "Successfully Connected!".getBytes();
ConnectResponse cr = new ConnectResponse(true, cntMsg.length, cntMsg);
out.writeObject(cr);
} else {
// Handle failing request
handleFailedMsg(out, "Client should connect first");
socket.close();
throw new IllegalArgumentException("Connect unsuccessfully");
}
handleClient(in, out);
socket.close();
} catch (IOException | ClassNotFoundException | InterruptedException e) {
e.printStackTrace();
}
}
/**
*
* #param in
* #param out
* #throws InterruptedException
* #throws IOException
* #throws ClassNotFoundException
*/
private void handleClient(ObjectInputStream in, ObjectOutputStream out)
throws InterruptedException, IOException, ClassNotFoundException {
while (true) {
// Handle message taken from the queue
Message msgFromQueue = messageQueue.take();
handleQueueRequest(msgFromQueue, out);
// Handle request obtained by user
Message request = (Message) in.readObject();
// Handle disconnect
if (request.getIdentifier() == MessageIdentifier.DISCONNECT_MESSAGE) {
DisconnectMessage dm = (DisconnectMessage) request;
// If the message is not for this thread, then put it back and ignore it.
if (!Arrays.equals(username, dm.getUsername())) {
messageQueue.add(request);
continue;
}
// Check if the username is inside the client map
if (!clientManagement.containsKey(dm.getUsername())) {
handleFailedMsg(out, "The client doesn't exist");
}
// Disconnect
clientManagement.remove(dm.getUsername());
// Create disconnect response
byte[] message = "See you again".getBytes();
DisconnectResponse dr = new DisconnectResponse(true, message.length, message);
// Write to the client
out.writeObject(dr);
break;
}
// Handle other
if (!handleRequest(request, out)) {
handleFailedMsg(out, "The request failed due to incorrect username.");
}
}
}
/**
*
* #param request
* #param out
* #return
* #throws IOException
*/
private boolean handleRequest(Message request, ObjectOutputStream out) throws IOException {
switch (request.getIdentifier()) {
// If broadcast, then every one should know
case BROADCAST_MESSAGE:
BroadcastMessage bm = (BroadcastMessage) request;
if (!Arrays.equals(username, bm.getUsername())) {
return false;
}
messageQueue.add(request);
break;
// If user want the list of connected users
case QUERY_CONNECTED_USERS:
QueryUsersMessage qu = (QueryUsersMessage) request;
if (!Arrays.equals(username, qu.getUsername())) {
return false;
}
List<Pair<Integer, byte[]>> userList = new ArrayList<>();
for (byte[] username : clientManagement.keySet()) {
Pair<Integer, byte[]> user = new Pair<>(username.length, username);
userList.add(user);
}
// Create a new query response containing all the users
QueryResponse qr = new QueryResponse(clientManagement.keySet().size(), userList);
out.writeObject(qr);
break;
// If user wants to send a direct message to the other user
case DIRECT_MESSAGE:
DirectMessage dm = (DirectMessage) request;
if (!Arrays.equals(username, dm.getUsername())) {
return false;
}
messageQueue.add(request);
break;
// If user wants to send an insult to the other user and broadcast to the chat room
case SEND_INSULT:
SendInsultMessage si = (SendInsultMessage) request;
if (!Arrays.equals(username, si.getUsername())) {
return false;
}
messageQueue.add(request);
break;
}
return true;
}
/**
*
* #param out
* #param description
* #throws IOException
*/
public void handleFailedMsg(ObjectOutputStream out, String description) throws IOException {
byte[] failedMsg = description.getBytes();
FailedMessage fm = new FailedMessage(failedMsg.length, failedMsg);
out.writeObject(fm);
}
/**
*
* #param request
* #param out
* #throws IOException
*/
public void handleQueueRequest(Message request, ObjectOutputStream out) throws IOException {
switch (request.getIdentifier()) {
case SEND_INSULT:
// Gets the message from queue
SendInsultMessage si = (SendInsultMessage) request;
// Check if the user already gotten the message
if (!si.getOtherUsers().contains(username)) {
out.writeObject(si);
si.addUsers(username);
}
// Check if all the users already gotten the message
if (si.getOtherUsers().size() < clientManagement.keySet().size()) {
messageQueue.add(si);
}
break;
case DIRECT_MESSAGE:
DirectMessage dm = (DirectMessage) request;
// Check if the message is for this user
if (Arrays.equals(username, dm.getRecipientUsername())) {
out.writeObject(dm);
} else { // If not for this user then put it back
messageQueue.add(dm);
}
break;
case BROADCAST_MESSAGE:
// Gets the message from queue
BroadcastMessage bm = (BroadcastMessage) request;
// Check if the user already gotten the message
if (!bm.getOtherUsers().contains(username)) {
out.writeObject(bm);
bm.addUsers(username);
}
// Check if all the users already gotten the message
if (bm.getOtherUsers().size() < clientManagement.keySet().size()) {
messageQueue.add(bm);
}
break;
}
}
I want to do JUnit test for my server. What is the best way to test a multi-threading server like this?
Here are the JUnit test code that I am trying. I first start a thread which is accepted by the server. Then I am going to start a client and pretend that the client is sending something to the server. I first want to try a connect_message to see how connection work. But so far, the test doesn't seem to responding on JUnit test. It just keeps running, nothing happen
public class ClientThreadTest {
private Thread foo;
private List<ClientThread> clientList;
private BlockingQueue<Message> messageQueue;
private ConcurrentMap<byte[], Boolean> clientManagement;
private static final int PORT = 3000;
#Before
public void setUp() throws Exception {
messageQueue = new LinkedBlockingQueue<>();
clientManagement = new ConcurrentHashMap<>();
}
#Test
public void run() throws IOException, ClassNotFoundException {
ServerSocket socket = new ServerSocket(PORT);
foo = new Thread(new ClientThread(socket.accept(), messageQueue, clientManagement));
foo.start();
Socket fooClient = new Socket("localhost", PORT);
ObjectOutputStream out = new ObjectOutputStream(fooClient.getOutputStream());
ObjectInputStream in = new ObjectInputStream(fooClient.getInputStream());
// First test Connection message
byte[] username = "foo".getBytes();
ConnectMessage cm = new ConnectMessage(username.length, username);
// The message need to get
byte[] cntMsg = "Successfully Connected!".getBytes();
ConnectResponse cr = new ConnectResponse(true, cntMsg.length, cntMsg);
out.writeObject(cm);
ConnectResponse m = (ConnectResponse) in.readObject();
System.out.println(Arrays.toString(m.getMessage()));
}

I have solved my own problem!
For anyone who is doing JUnit testing on a multi-threading server. Here is my suggestion:
You have to start you main server at the beginning, before anything else. Keep you main server listening to some port that you give it.
Then you have to start your client and you have to give it the same port number which you gave to the main server
Last but not least, you can start your thread to deal with a specific client. Somehow if I instantiated my thread as ClientThread foo, and I called foo.run(), it won't work. I have to instantiate Thread foo, and make my ClientThread() as an input to Thread(), and call foo.start() instead!
Now it is working!

Related

Java TcpConnection

Hello I have a code written in Java and I need to create an TCP Connection with GPS device in android studio, where you can type in IP/PORT addresses, if someone can help me thanks in advance.
public class TCPConnection implements Runnable {
/**
* <h1>TCP Connection construct</h1>
* <p>The tcp connection requires two parameters socket and view model. </p>
* #param socket to establish connection.
* */
TCPConnection(Socket socket) {
super();
this.socket = socket;
converter = new Converter();
crc16 = new Crc16();
}
/**
* <h1>Run function to start listener</h1>
* <p>Simply runs the runnable thread to listen everything from client</p>
* */
public void run() {
try {
inputStream = new DataInputStream(socket.getInputStream());
outputStream = new DataOutputStream(socket.getOutputStream());
Listen();
} catch (IOException e) {
e.printStackTrace();
}
}
Probably I need to create an button to start Listen incoming connections, also use Log class .....
/**
* <h1>Listen</h1>
* <p>Function for listening connected client</p>
* #throws IOException throws exception if input stream is interrupted
* */
private void Listen() throws IOException {
while (flag) {
System.out.println("listening...");
while (!socket.isClosed() && inputStream.available() == 0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
Communicate();
}
inputStream.close();
outputStream.close();
socket.close();
}
/**
* <h1>Get Number Of Records</h1>
* <p>Reads the number of records to send back to the sender</p>
* #param data the parameter is a received hex data
* #return String format number of records
* */
private String GetNumberOfRecords(String data) {
return data.substring(18, 20);
}
Everything is written in a comments line, why stackoverflow says add more details :D...
/**
* <h1>Communicate</h1>
* <p>A reader and sender with client, first it reads imei, then sends back 01.
* It receives data, as soon it receives it sends back number of records.
* The while loop initializes and runs until it get interrupted or client disconnects.</p>
* */
private void Communicate() {
imei = Objects.requireNonNull(ReadInput()).substring(4);
imei = converter.ReadImei(imei);
String path = System.getProperty("user.home") + "/Desktop";
logger = new Logger(path+"/Logs/TCPLogs/"+imei);
logger.PrintToLOG(GetTime()+" IMEI: " +imei);
if(imei.length() < 15){
SendOutput("00");
}
else{
SendOutput("01");
logger.PrintToLOG("\tResponse: [0" + 1 + "]");
String input = ReadInput();
Log(Objects.requireNonNull(input));
while(flag){
String recordsCount = GetNumberOfRecords(input);
SendOutput("000000" + recordsCount);
logger.PrintToLOG("\tCrc: " + Integer.toHexString(CRC(input)));
logger.PrintToLOG("\tResponse: [000000" + recordsCount + "]\n");
input = ReadInput();
Log(Objects.requireNonNull(input));
}
}
/**
* <h1>Send Output</h1>
* <p>Sends output to the client</p>
* #param message the parameter is a received hex data
* */
private void SendOutput(String message) {
try {
outputStream.write(converter.StringToByteArray(message));
outputStream.flush();
} catch (IOException e) {
System.out.println("Output stream was interrupted");
}
}
/**
* <h1>CRC</h1>
* <p>Calculates CRC of received data</p>
* #param str the parameter is a received hex data
* #return int of crc16
* */
private int CRC(String str) {
str = str.substring(16, str.length() - 8);
byte[] bytes = converter.StringToByteArray(str);
return crc16.getCRC(bytes);
}
/**
* <h1>Read Input</h1>
* <p>Reads the input from client. Currently maximum message byte is set up to 8192,
* if message is bigger then message will not be properly readable and displayed.</p>
* #return String of received data
* */
private String ReadInput() {
byte[] messageByte = new byte[8192];
int dataSize;
try {
dataSize = inputStream.read(messageByte);
String finalInput = converter.BytesArrayToHex(messageByte, dataSize);
SendToConsole(finalInput);
return finalInput;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* <h1>Send To Console</h1>
* <p>Simply prints out the results to the text area for user</p>
* #param input the parameter is String format to print in text area
* */
private void SendToConsole(String input) {
if(imei!=null)
{
String message = viewModel.getClientMessage() + "\r\nFrom imei - "+imei+" : " + input + "\n" + repeatChar();
Platform.runLater(() -> viewModel.setClientMessage(message));
}
else {
String message = viewModel.getClientMessage() + "\r\nReceived imei - : " + input + "\n" + repeatChar();
Platform.runLater(() -> viewModel.setClientMessage(message));
}
}
/**
* <h1>Log</h1>
* <p>Given String is being written to log file.</p>
* #param data the parameter is a received data
* */
private void Log(String data) {
logger.PrintToLOG("\tcodec : " + data.substring(16, 18));
logger.PrintToLOG("\tNumber of Records : " + GetNumberOfRecords(data));
logger.PrintToLOG("\tAVL data : " + data + "\n");
}
/**
* <h1>Set Running</h1>
* <p>Sets flag to run or stop while loop in order to interrupt the thread.</p>
* */
void setRunning() {
this.flag = false;
}
/**
* <h1>Repeat Char</h1>
* <p>Repeats the '=' character multiple times.</p>
* #return String is being returned.
* */
private String repeatChar() {
char[] data = new char[50];
Arrays.fill(data, '=');
return new String(data);
}
/**
* <h1>Get Time</h1>
* <p>Gets time when method is being called</p>
* #return Time in String format
* */
private String GetTime()
{
LocalDateTime localDateTime = LocalDateTime.now();
LocalTime localTime = localDateTime.toLocalTime();
return localTime.toString();
}
}
public class TCPServer implements Runnable {
private int port;
private Socket socket;
private ServerSocket ss;
private boolean running = true;
private ArrayList<TCPConnection> tcpConnections;
/**
* <h1>TCP server construct</h1>
* <p>The tcp server takes port parameter </p>
* #param port is required for server to listen all incoming connections
* */
public TCPServer(int port) {
this.port = port;
}
/**
* <h1>Run</h1>
* <p>Runs the runnable thread to listen connections, it accepts a connection, if accept was successful,
* the connection is added to tcpConnections list and runs the TCPConnection for further listening.
* The server is running in while loop and stops when Running is set to false,
* then break is called and shutdowns every connected client.</p>
* */
public void run() {
tcpConnections = new ArrayList<>();
try {
ss = new ServerSocket(port);
System.out.println("Listening on port : " + ss.getLocalPort());
ExecutorService executorService;
while (true) {
executorService = Executors.newSingleThreadExecutor();
socket = ss.accept();
TCPConnection connection = new TCPConnection(socket);
executorService.submit(connection);
tcpConnections.add(connection);
if (!running) {
StopConnections();
break;
}
}
executorService.shutdownNow();
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (IOException e) {
System.out.println("socket is closed");
}
}
/**
* <h1>Set Flag</h1>
* <p>Function is being called when we want to interrupt server thread and stop it.</p>
* #param flag the parameter sets whenever to true(run server) or false(stop server)
* */
public void setFlag(boolean flag) {
running = flag;
if (!running) {
try {
ss.close();
if (socket != null)
socket.close();
} catch (IOException e) {
System.out.println("Socket is " + socket.isClosed());
}
}
}
/**
* <h1>Stop Connections</h1>
* <p>Function is being called when we are stopping server,
* this function iterates through every connection and stops it.</p>
* */
private void StopConnections() {
if (!tcpConnections.isEmpty()) {
for (TCPConnection connections : tcpConnections) {
connections.setRunning();
}
tcpConnections.clear();
}
}
}
Android supports Java code as long as your android API level supports the Java version you are using. There shouldn't be any reason why you cannot use this in Android.
Just note that Android will throw an exception if you run any network tasks on the UI thread. E.g. Creating a socket should be run as an IntentService or AsyncTask, there are other options as well.

JAVA NIO Server - NOT receiving client blank message / partial message

I built a server and client using NIO package with server socket channel and socket channel. The basic setup works. but when I try to move my code handling each connection in a separate thread using executor service I start getting weird errors where I am getting partial message or sometimes blank messages.
This is my code where am I passing my selection key to a thread with executor service
private void readAndRespond(SelectionKey selectionKey) {
this.executorService.submit(new Worker(selectionKey));
}
private class Worker implements Runnable {
private SelectionKey selectionKey;
private Worker(SelectionKey selectionKey) {
this.selectionKey = selectionKey;
}
#Override
public void run() {
SocketChannel socketChannel;
ArrayList<ByteBuffer> buffers;
String data, reply;
ByteBuffer responseBuffer;
socketChannel = (SocketChannel) selectionKey.channel();
try {
buffers = this.readRequest(socketChannel);
data = this.extractData(buffers);
if (!data.isEmpty()) {
reply = responseManager.reply(data);
responseBuffer = ByteBuffer.wrap(reply.getBytes());
socketChannel.write(responseBuffer);
}
}
catch (IOException e) {
System.out.println("Unable to process response " + e.getMessage());
}
}
private ArrayList<ByteBuffer> readRequest(SocketChannel socketChannel) throws IOException {
int counter;
ByteBuffer current;
ArrayList<ByteBuffer> buffers;
counter = 2;
buffers = new ArrayList<>();
current = ByteBuffer.allocate(minBuffer);
buffers.add(current);
while (socketChannel.read(current) > 0) {
if (!current.hasRemaining()) {
current = ByteBuffer.allocate(minBuffer * 2 * counter);
buffers.add(current);
counter++;
}
}
return buffers;
}
private String extractData(ArrayList<ByteBuffer> buffers) throws UnsupportedEncodingException {
StringBuilder stringBuilder;
stringBuilder = new StringBuilder();
for(ByteBuffer buffer : buffers) {
stringBuilder.append(new String(buffer.array(), "UTF-8"));
}
return stringBuilder.toString().trim();
}
}
Needed to add this before handing off to thread for reading
selectionKey.interestOps( selectionKey.interestOps() & ~SelectionKey.OP_READ );

Inter Process Communication to share updates to data in Java

I'm creating a token ring with sensors. I have client processes defined in SensorClient class. These client objects receive information about the list of other clients from a server process, the createSensor class.
The problem is that I would like clients to update the information they have when it changes on the server.
The server class:
public class createSensor {
private static createSensor instance = null;
private ArrayList<Sensor> sensor = new ArrayList<>();
public int position, prevPosition, nextPosition, prevPort, nextPort;
private createSensor() {
}
public synchronized ArrayList insertSensor(String type, String identificator, int port, String id, String gatwayAddr, long timestamp) throws IOException {
sensor.add(new Sensor(type, identificator, port, id, gatwayAddr, timestamp));
return new ArrayList<>(sensor); //
}
}
public synchronized boolean hasMeasurements() {
while (InnerBuffer.getInstance().emptyInnerBuffer())
return false;
return true;
}
public synchronized void setPrevNextWhenDelete(int position,ArrayList<Sensor> sensorList) throws IOException {
//code
}
public synchronized ArrayList<Sensor> getSensorList() {
return new ArrayList<>(sensor);
}
public synchronized int size() {
return sensor.size();
}
public synchronized String returnRecentMeasurement (String id){
String recentMeasurement=null;
for (Sensor sensori : sensor) {
if (sensori.getIdentificator().equalsIgnoreCase(id))
recentMeasurement= InnerBuffer.getInstance().returnRecentMeasurements(id);
else
recentMeasurement = null;}
return recentMeasurement;
}
public synchronized void setPrevNextWhenAdd() throws IOException { //some other code where int position, prevPosition, nextPosition, prevPort, nextPort get their values.
}}
The client class:
public class SensorClient {
public static void main(String[] args) throws Exception {
//Starting a new sensor
Sensor sensor = new Sensor(type,identificator,portnumber,ipnumber,gatewayAddr,timestamp);
Gson gson = new Gson();
String message = gson.toJson(sensor);
Client c = Client.create();
WebResource r = c.resource("http://localhost:9999/gateway/");
ClientResponse response = r.path("sensors/add").type(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).post(ClientResponse.class, message);
if (response.getStatus() == 200) {
repeat = false;
Type collectionType = new TypeToken<ArrayList<Sensor>>(){}.getType();
ArrayList<Sensor> sensorList = gson.fromJson(response.getEntity(String.class), collectionType);
System.out.println("Starting the sensor ...");
System.out.println("Push exit when you want to delete the sensor!");
int position = 0;
for(int i = 0; i< sensorList.size();i++){
if(sensorList.get(i).getIdentificator().equalsIgnoreCase(sensor.getIdentificator()) )
position = i;
}
sensors.Sensor.simulation(type, identificator);// special thread for sensors simulations
createSensor.getInstance().setPrevNextWhenAdd(position,sensorList);
serverSocket serverSocket = new serverSocket(portnumber,sensorList,position,sensorList.get(position).getNext());
serverSocket.start();
StopSensor stopSensor = new StopSensor(identificator,portnumber,position,sensorList);
stopSensor.start();
oneSensor s = new oneSensor(portnumber,sensorList);
s.start();
} else {
repeat = true;
count +=1;
System.out.println("Error. Wrong data! ");
}
}
while (repeat );
}
}
}
The serverSocket thread
public class serverSocket extends Thread {
public int port,nextPort;
ArrayList<gateway.Sensor> sensorList;
public static int position;
public serverSocket(int port, ArrayList<gateway.Sensor> sensorList,int position,int nextPort) {
this.port = port;
this.nextPort=nextPort;
this.sensorList= sensorList;
this.position=position;}
public void run() {
ServerSocket welcomeSocket;
Socket connectionSocket;
try {
welcomeSocket = new ServerSocket(port);
while (true) {
connectionSocket = welcomeSocket.accept();
receivedMessages thread = new receivedMessages(connectionSocket,sensorList,position,nextPort);
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
System.err.println("Error!!!!!!!!!");
}
}
}
The receivedMessages thread
public class receivedMessages extends Thread {
private BufferedReader inFromClient;
private Socket connectionSocket;
ArrayList<gateway.Sensor> sensorList;
int position,nextPort;
public receivedMessages(Socket socket, ArrayList<gateway.Sensor> sensorList,int position,int nextPort){
connectionSocket = socket;
this.sensorList=sensorList;
this.position=position;
this.nextPort=nextPort;
try {
inFromClient = new BufferedReader( new InputStreamReader(connectionSocket.getInputStream()));
} catch (IOException e) { e.printStackTrace(); }
}
#Override
public void run() {
// while(true) {
try {
String message = (inFromClient.readLine().toString());
System.out.println(message);
if (message.startsWith("Next") || message.startsWith("Previous")) {
System.out.println(message);
} else if (message.startsWith("The")) {
System.out.println(message); createSensor.getInstance().setPrevNextWhenDelete(position, sensorList);
} else {// i receive the message that the list has changed
System.out.println(message);
sensorList = createSensor.getInstance().getSensorList();
System.out.println("Updated " + sensorList);}
Listening for changes
There are 2 possibilities for your client processes to be informed when data is modified:
your client objects could regularly check whether list has been modified:
by asking for the current version of the list
or by asking for a timestamp of the list's modification (your server class, createSensor, would then have to keep a record of that)
your client objects could open a port to listen to notifications, and your server class could notify them of a change, pushing the new list to them when the list is modified.
Sharing updates between the listener thread and the worker thread
In both cases, on your clients side, you will have one thread doing the work, and another thread listening for changes. That second thread will reconstruct a new list each time. In order to share it with the worker thread, here is what you can do :
your listener thread will keep a reference to the sensorList that it was previously passed by the worker thread when created
when reconstructing a new list, your listener thread will store that new list in another reference, newList
your listener list can then modify the "old" list so that it matches the new list, by using oldList.clear() then oldList.addAll(newList).
This would work, since even though the worker thread might have its own reference to the list, both references point to the same object, so it would see the changes.
Code for starting listener thread
For example, if your worker thread is the main thread, and it is creating the listener thread, your code could be like this, inside the worker thread code :
class Listener implements Runnable {
List<Sensor> sensorList;
Listener(List<Sensor> list ){
sensorList = list;
}
public void run(){
// code to listen to changes on the server
// When server sends new information, thread can update the list directly:
// sensorList.clear();
// sensorList.addAll(newList);
}
}
Listener l = new Listener(sensorList);
Thread listenerThread = new Thread(l);
listenerThread.start();
You can read about threads and how they share memory in the Java Tutorials on Concurrency.
Be careful with synchronization though : when you update the old list, you should make sure other thread is not iterating over it. You should probably use Collections.synchronizedList(sensorList) to make sure there are no thread interferences.

Check if data is available on ObjectInputStream [duplicate]

This question already has answers here:
Continuously read objects from an ObjectInputStream in Java
(4 answers)
Closed 7 years ago.
A couple of weeks ago, I posted the following question because I had problems with reading objects from an ObjectInputStream using readObject:
Continuously read objects from an ObjectInputStream in Java
With the responds I got, I think I was able to understand what is going wrong -> I am calling readObject in a loop, even if no data has been send en therefore I receive an EOFException.
However, because I really want a mechanism where I am continuesly reading from the input stream I am looking for a solution for this problem.
I tried to use the following to create a mechanism where I only call readObject when there is data available:
if(mObjectIn.available() > 0)
mObjectIn.readObject()
But unfornately, mObjectIn.available() always returns 0.
Can anyone get me in the good direction. Is it possible at all to implement what I want??
You can send an int through the ObjectOutputStream to let the other side know when you will stop sending objects.
For example:
public static void main(String[] args) {
//SERVER
new Thread(new Runnable() {
#Override
public void run() {
try (ServerSocket ss = new ServerSocket(1234)) {
try (Socket s = ss.accept()) {
try (ObjectInputStream ois = new ObjectInputStream(
s.getInputStream())) {
while (ois.readInt() != -1) {//Read objects until the other side sends -1.
System.out.println(ois.readObject());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
//CLIENT
try (Socket s = new Socket(InetAddress.getByName("localhost"), 1234)) {
try (ObjectOutputStream oos = new ObjectOutputStream(
s.getOutputStream())) {
for (int i = 0; i < 10; i++) {
oos.writeInt(1);//Specify that you are still sending objects.
oos.writeObject("Object" + i);
oos.flush();
}
oos.writeInt(-1);//Let the other side know that you've stopped sending object.
}
} catch (Exception e) {
e.printStackTrace();
}
}
Or you can write a null object at the end to let the other side know you won't be sending any more objects. This will work only if you are sure that none of the objects you need to send are null.
new Thread(new Runnable() {
#Override
public void run() {
try (ServerSocket ss = new ServerSocket(1234)) {
try (Socket s = ss.accept()) {
try (ObjectInputStream ois = new ObjectInputStream(
s.getInputStream())) {
String obj;
while ((obj = (String) ois.readObject()) != null) {
System.out.println(obj);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
try (Socket s = new Socket(InetAddress.getByName("localhost"), 1234)) {
try (ObjectOutputStream oos = new ObjectOutputStream(
s.getOutputStream())) {
for (int i = 0; i < 10; i++) {
oos.writeObject("Object" + i);
oos.flush();
}
oos.writeObject(null);
}
} catch (Exception e) {
e.printStackTrace();
}
I have written client/server tcp code in Java before. Streams can be tricky. When you work with streams you cannot assume all the data is there when you do a read. Instead you have to pull bytes out of the stream and if some msg delimiter is reached, then you take all those acquired bytes and process them. In your case you will turn them into an object.
Below are three classes I use to extract msg's from a socket.
First the MsgExtractor that get as input bytes that would essentially come from a socket. It checks to see if a msg has arrived (via the delimiter).
import org.apache.log4j.Logger;
//~--- JDK imports ------------------------------------------------------------
import java.text.ParseException;
import java.util.Arrays;
import java.util.MissingResourceException;
import java.util.concurrent.BlockingQueue;
/**
* This class parses the data retrieved from the TCP socket streams for new messages. All new messages found are inserted into the shared message queue.
*
* #author jmartinez
*/
public class MsgExtractor {
//////////////////////////////////////////////////////////////////////////
// STATIC VARIBLES
private static final Logger logger = Logger.getLogger(MsgExtractor.class);
///////////////////////////////////////////////////////////////////////
// CONSTANTS
private final int INIT_BUFFER_SIZE = 10000;
// <buffer variables>
private byte[] bufferedMsg = new byte[INIT_BUFFER_SIZE];
private int bufferSize = INIT_BUFFER_SIZE;
private int curBufferPos = 0; // ...current position on the buffered message
private int curMsgSize = 0; // ...current amount of buffered chars
///////////////////////////////////////////////////////////////////////
// VARIABLES
private final byte[] delimiter;
private final int delimiterSize;
private final BlockingQueue msgQueue;
private final int maxMsgSize;
// </>
////////////////////////////////////////////////////////////////////////
// FUNCTIONS
/**
* Creates a new MsgExtractor.
*
* #param msgService
*/
public MsgExtractor(MessageService msgService) {
ServerProperties properties = ServerProperties.getInstance();
if (properties == null) {
throw new MissingResourceException("unable to obtain properties", MsgExtractor.class.getName(),
"ServerProperties");
}
this.maxMsgSize = Integer.parseInt(properties.getProperty(ServerProperties.MAX_MESSAGE_SIZE));
this.delimiter = Arrays.copyOf(msgService.getMsgHandler().getMessageDelmiter(),
msgService.getMsgHandler().getMessageDelmiter().length);
this.delimiterSize = delimiter.length;
this.msgQueue = msgService.getSharedMsgQueue();
}
/**
* Inserts new chars into the message buffer. It then extracts any messages found in the buffer by checking for any occurrences of the message delimiter. Extracted messages are removed from the buffer, converted to String, and inserted into the ManagedQueue.
*
* #param cbuf - An array containing the new chars that need to be added to the message buffer.
* #param offset - Array offset from where on the array to start collecting the new chars.
* #param length - The number of chars that need to be collected.
* #throws java.lang.InterruptedException
* #throws java.text.ParseException
*/
public void insertNewChars(byte[] cbuf, int offset, int length) throws InterruptedException, ParseException {
// ...check if the message buffer has enough room to add the new chars... if not, increase the buffer size.
if (bufferSize < curMsgSize + length) {
increaseBufferSize();
}
// ...add the new chars to the buffer one at a time
for (int i = 0; i < length; i++) {
bufferedMsg[curMsgSize++] = cbuf[i + offset];
}
// ...keep checking for new messages as long as they are being found
boolean rv;
do {
rv = checkForNewMsg();
} while (rv == true);
if (curMsgSize > maxMsgSize) {
throw new ParseException("max message size reached and still not found delimiter", curMsgSize);
}
}
/**
* Doubles the message buffer size.
*/
private void increaseBufferSize() {
bufferSize *= 2;
byte[] temp = new byte[bufferSize];
System.arraycopy(bufferedMsg, 0, temp, 0, curMsgSize);
bufferedMsg = temp;
}
/**
* Checks if the delimiter is found in the currently buffered message.
* checkForNewMsg starts its search where it last left off at.
*
* Performance can be improved if this method checks for all occurrences of the message delimiter, instead of one.
*
* #return true if delimiter was found in buffer, else false
*/
private boolean checkForNewMsg() throws InterruptedException {
while (curBufferPos <= curMsgSize - delimiterSize) {
boolean delimitterFound = true;
for (int i = 0; i < delimiterSize; i++) {
if (delimiter[i] != bufferedMsg[i + curBufferPos]) {
delimitterFound = false;
break;
}
}
if (delimitterFound) {
extractNewMsg(curBufferPos);
return true;
} else {
curBufferPos++;
}
}
return false;
}
/**
* A new message is located at index = 0 through delimiterPos - 1. the method extracts that message and inserts it into a local String array.
*
* Performance can be improved if this method extracted a messages for all occurrences of the message delimiter, instead of one.
*
* #param delimiterPos - The position where the delimiter was found.
*/
private void extractNewMsg(int delimiterPos) throws InterruptedException {
try {
msgQueue.put(new String(bufferedMsg, 0, delimiterPos - 1));
} catch (InterruptedException ie) {
logger.error("Interrupted while putting message to ManagedQueue", ie);
throw ie;
} catch (Exception e) {
logger.error("Unable to put message to ManagedQueue", e);
}
// <reset the message buffer and corresponding variables>
byte[] tmpBuffer = new byte[this.bufferSize];
int tmpMsgSize = 0;
for (int i = delimiterPos + this.delimiterSize; i < curMsgSize; i++) {
tmpBuffer[tmpMsgSize++] = bufferedMsg[i];
}
curBufferPos = 0;
bufferedMsg = tmpBuffer;
curMsgSize = tmpMsgSize;
// </>
}
}
Here is the ConnectionHandler that manages the connection and feeds the MsgExtractor with bytes:
import org.apache.log4j.Logger;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.text.ParseException;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* This class handles all new connections. It reads data from the socket stream
* and sends it to a MsgExtractor for further processing.
*
* This class in Runnable. use the run method to start it up and interrupt it to
* shut it down.
*
* #author Jose
*/
public class ConnectionHandler implements Runnable {
//////////////////////////////////////////////////////////////////////////
// STATIC VARIBLES
// ...log4j's Logger is thread safe
private static final Logger logger = Logger.getLogger(ConnectionHandler.class);
private final static AtomicBoolean isRunning = new AtomicBoolean(false);
/////////////////////////////////////////////////////////////////////////
// Constants
private final int BUFFER_SIZE = 8000;
private final byte[] rcvdChars = new byte[BUFFER_SIZE];
////////////////////////////////////////////////////////////////////////
// INSTANCE VARIABLES
private final Socket socket;
private final MsgExtractor msgExtractor;
/////////////////////////////////////////////////////////////////////////
// FUNCTIONS
/**
* Creates a new ConnectionHandler.
*
* #param socket - The socket that this object is to read from.
* #param msgService - The MessageService that is used to create the
* MsgExtractor object that this object uses.
*/
public ConnectionHandler(Socket socket, MessageService msgService) {
this.socket = socket;
logger.info("ConnectionHandler thread ID:" + Thread.currentThread().getId() + " instanctiated for listen port "
+ socket.getLocalPort());
msgExtractor = new MsgExtractor(msgService);
}
/**
* Starts the ConnectionHandler. Creates an input stream from this objects
* socket to read data from. all read data is sent to a MsgExtractor. The
* MSgExtractor will extract messages from the read data and will add any
* messages to this objects ManagedQueue. This method continues operating
* till the thread is interrupted or the socket is no longer available for
* providing input. Returns right away if validation of this object fails.
*/
public void run() {
// ...if validation fails, return
if (isValid() == false) {
return;
}
// ...if already running, return
if (!isRunning.compareAndSet(false, true)) {
logger.warn("ConnectionHandler thead ID:" + Thread.currentThread().getId()
+ " is already running, not going to run again.");
return;
}
logger.info("ConnectionHandler thead ID:" + Thread.currentThread().getId() + " is starting up.");
// <get input reader from socket>
InputStream inputReader;
try {
inputReader = socket.getInputStream();
} catch (IOException ex) {
logger.error("ConnectionHandler thread ID:" + Thread.currentThread().getId()
+ ", failed to get socket input stream in ParserThread.run", ex);
return;
}
// </>
// ...bytes read from the socket
int bytesRead;
try {
// ...stops when the thread is interrupted or the socket no longer provides input
while ((socket.isInputShutdown() == false) || (Thread.interrupted() == false)) {
try {
// ...get data from socket stream
bytesRead = inputReader.read(rcvdChars, 0, BUFFER_SIZE);
} catch (IOException e) { // ... catch any exception and call it a day for this thread
logger.error("ConnectionHandler thread ID:" + Thread.currentThread().getId()
+ ", encountered error reading from socket, could be a closed connection.", e);
break;
}
try {
msgExtractor.insertNewChars(rcvdChars, 0, bytesRead);
} catch (ParseException pe) {
logger.error("ConnectionHandler thread ID:" + Thread.currentThread().getId()
+ ", encountered parsing error, closing connection.", pe);
break;
} catch (InterruptedException ex) {
break;
}
}
} finally {
// ...close the socket if it is still open
if (socket.isClosed() == false) {
try {
socket.close();
} catch (IOException ex) {
logger.error("ConnectionHandler thread ID:" + Thread.currentThread().getId()
+ ", failed to close socket.", ex);
}
}
isRunning.set(false);
} // end of: finally
logger.info("ConnectionHandler thead ID:" + Thread.currentThread().getId() + " is shutting down.");
}
/**
* Used by the run() method to validate this object. If validation fails,
* the run() method returns right away.
*
* #return - Returns true is this object is valid, else false.
*/
private boolean isValid() {
if (socket == null) {
logger.error("ConnectionHandler thread ID:" + Thread.currentThread().getId()
+ ", validation failed, Socket is null");
return false;
}
return true;
}
}
And for completion here is the MsgService that pops up in the above code:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* The Model Object, or DTO, of the CMS server. It contains information that is needed by just about every component of the CMS server.
*
* There is a separate instance of this class for each messaging service that the CMS server is configured for.
*
* #author Jose
*/
public class MessageService {
/**
* the shared message queue where new messages are inserted into by the MsgExtractor, and taken by the MessageQueueWorker.
*/
private final BlockingQueue<byte[]> sharedMsgQueue = new LinkedBlockingQueue<byte[]>();
/**
* the TCP listen port
*/
private final int port;
/**
* the MessageHandler that will process new messages
*/
private final MessageHandler msgHandler;
/**
* optional. max number of TCP connections
*/
private final int maxConnections;
/**
* optional. max number of worker threads that will be used to process new messages
*/
private final int maxWorkerThreads;
/**
* Creates new message service object. Sets max connections and max worker threads to 0.
*
* #param port - TCP port this message service will listen on.
* #param msgHandler - the MessageHandler that will be called to process messages received for this message service.
*/
public MessageService(int port, MessageHandler msgHandler) {
this.port = port;
this.msgHandler = msgHandler.getNewInstance();
this.maxConnections = 0;
this.maxWorkerThreads = 0;
}
/**
* Creates new message service object. Sets max worker threads to 0.
*
* #param port - TCP port this message service will listen on.
* #param msgHandler - the MessageHandler that will be called to process messages received for this message service.
* #param connections - max concurrent connections available for this service.
*/
public MessageService(int port, MessageHandler msgHandler, int connections) {
this.port = port;
this.msgHandler = msgHandler.getNewInstance();
this.maxConnections = connections;
this.maxWorkerThreads = 0;
}
/**
* Creates new message service object.
*
* #param port - TCP port this message service will listen on.
* #param msgHandler - the MessageHandler that will be called to process messages received for this message service.
* #param connections - max concurrent connections available for this service.
* #param workerThreads - max worker threads that will process messages for this message service.
*/
public MessageService(int port, MessageHandler msgHandler, int connections, int workerThreads) {
this.port = port;
this.msgHandler = msgHandler.getNewInstance();
this.maxConnections = connections;
this.maxWorkerThreads = workerThreads;
}
/**
*
* #return this object's MessageHandler
*/
public MessageHandler getMsgHandler() {
return msgHandler.getNewInstance();
}
/**
*
* #return the TCP port this MessageService will listen on
*/
public int getPort() {
return port;
}
/**
*
* #return the BlockingQueue used to store new messages.
*/
public BlockingQueue<byte[]> getSharedMsgQueue() {
return sharedMsgQueue;
}
/**
*
* #return max concurrent connections available for this service
*/
public int getMaxConnections() {
return maxConnections;
}
/**
*
* #return max worker threads that will process messages for this message service
*/
public int getMaxWorkerThreads() {
return this.maxWorkerThreads;
}
}

Using Threads to Handle Sockets

I am working on a java program that is essentially a chat room. This is an assignment for class so no code please, I am just having some issues determining the most feasible way to handle what I need to do. I have a server program already setup for a single client using threads to get the data input stream and a thread to handle sending on the data output stream. What I need to do now is create a new thread for each incoming request.
My thought is to create a linked list to contain either the client sockets, or possibly the thread. Where I am stumbling is figuring out how to handle sending the messages out to all the clients. If I have a thread for each incoming message how can I then turn around and send that out to each client socket.
I'm thinking that if I had a linkedlist of the clientsockets I could then traverse the list and send it out to each one, but then I would have to create a dataoutputstream each time. Could I create a linkedlist of dataoutputstreams? Sorry if it sounds like I'm rambling but I don't want to just start coding this, it could get messy without a good plan. Thanks!
EDIT
I decided to post the code I have so far. I haven't had a chance to test it yet so any comments would be great. Thanks!
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.ServerSocket;
import java.util.LinkedList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class prog4_server {
// A Queue of Strings used to hold out bound Messages
// It blocks till on is available
static BlockingQueue<String> outboundMessages = new LinkedBlockingQueue<String>();
// A linked list of data output streams
// to all the clients
static LinkedList<DataOutputStream> outputstreams;
// public variables to track the number of clients
// and the state of the server
static Boolean serverstate = true;
static int clients = 0;
public static void main(String[] args) throws IOException{
//create a server socket and a clientSocket
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(6789);
} catch (IOException e) {
System.out.println("Could not listen on port: 6789");
System.exit(-1);
}// try{...}catch(IOException e){...}
Socket clientSocket;
// start the output thread which waits for elements
// in the message queue
OutputThread out = new OutputThread();
out.start();
while(serverstate){
try {
// wait and accept a new client
// pass the socket to a new Input Thread
clientSocket = serverSocket.accept();
DataOutputStream ServerOut = new DataOutputStream(clientSocket.getOutputStream());
InputThread in = new InputThread(clientSocket, clients);
in.start();
outputstreams.add(ServerOut);
} catch (IOException e) {
System.out.println("Accept failed: 6789");
System.exit(-1);
}// try{...}catch{..}
// increment the number of clients and report
clients = clients++;
System.out.println("Client #" + clients + "Accepted");
}//while(serverstate){...
}//public static void main
public static class OutputThread extends Thread {
//OutputThread Class Constructor
OutputThread() {
}//OutputThread(...){...
public void run() {
//string variable to contain the message
String msg = null;
while(!this.interrupted()) {
try {
msg = outboundMessages.take();
for(int i=0;i<outputstreams.size();i++){
outputstreams.get(i).writeBytes(msg + '\n');
}// for(...){...
} catch (IOException e) {
System.out.println(e);
} catch (InterruptedException e){
System.out.println(e);
}//try{...}catch{...}
}//while(...){
}//public void run(){...
}// public OutputThread(){...
public static class InputThread extends Thread {
Boolean threadstate = true;
BufferedReader ServerIn;
String user;
int threadID;
//SocketThread Class Constructor
InputThread(Socket clientSocket, int ID) {
threadID = ID;
try{
ServerIn = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
user = ServerIn.readLine();
}
catch(IOException e){
System.out.println(e);
}
}// InputThread(...){...
public void run() {
String msg = null;
while (threadstate) {
try {
msg = ServerIn.readLine();
if(msg.equals("EXITEXIT")){
// if the client is exiting close the thread
// close the output stream with the same ID
// and decrement the number of clients
threadstate = false;
outputstreams.get(threadID).close();
outputstreams.remove(threadID);
clients = clients--;
if(clients == 0){
// if the number of clients has dropped to zero
// close the server
serverstate = false;
ServerIn.close();
}// if(clients == 0){...
}else{
// add a message to the message queue
outboundMessages.add(user + ": " + msg);
}//if..else...
} catch (IOException e) {
System.out.println(e);
}// try { ... } catch { ...}
}// while
}// public void run() { ...
}
public static class ServerThread extends Thread {
//public variable declaration
BufferedReader UserIn =
new BufferedReader(new InputStreamReader(System.in));
//OutputThread Class Constructor
ServerThread() {
}//OutputThread(...){...
public void run() {
//string variable to contain the message
String msg = null;
try {
//while loop will continue until
//exit command is received
//then send the exit command to all clients
msg = UserIn.readLine();
while (!msg.equals("EXITEXIT")) {
System.out.println("Enter Message: ");
msg = UserIn.readLine();
}//while(...){
outboundMessages.add(msg);
serverstate = false;
UserIn.close();
} catch (IOException e) {
System.out.println(e);
}//try{...}catch{...}
}//public void run(){...
}// public serverThread(){...
}// public class prog4_server
I have solved this problem in the past by defining a "MessageHandler" class per client connection, responsible for inbound / outbound message traffic. Internally the handler uses a BlockingQueue implementation onto which outbound messages are placed (by internal worker threads). The I/O sender thread continually attempts to read from the queue (blocking if required) and sends each message retrieved to the client.
Here's some skeleton example code (untested):
/**
* Our Message definition. A message is capable of writing itself to
* a DataOutputStream.
*/
public interface Message {
void writeTo(DataOutputStream daos) throws IOException;
}
/**
* Handler definition. The handler contains two threads: One for sending
* and one for receiving messages. It is initialised with an open socket.
*/
public class MessageHandler {
private final DataOutputStream daos;
private final DataInputStream dais;
private final Thread sender;
private final Thread receiver;
private final BlockingQueue<Message> outboundMessages = new LinkedBlockingQueue<Message>();
public MessageHandler(Socket skt) throws IOException {
this.daos = new DataOutputStream(skt.getOutputStream());
this.dais = new DataInputStream(skt.getInputStream());
// Create sender and receiver threads responsible for performing the I/O.
this.sender = new Thread(new Runnable() {
public void run() {
while (!Thread.interrupted()) {
Message msg = outboundMessages.take(); // Will block until a message is available.
try {
msg.writeTo(daos);
} catch(IOException ex) {
// TODO: Handle exception
}
}
}
}, String.format("SenderThread-%s", skt.getRemoteSocketAddress()));
this.receiver = new Thread(new Runnable() {
public void run() {
// TODO: Read from DataInputStream and create inbound message.
}
}, String.format("ReceiverThread-%s", skt.getRemoteSocketAddress()));
sender.start();
receiver.start();
}
/**
* Submits a message to the outbound queue, ready for sending.
*/
public void sendOutboundMessage(Message msg) {
outboundMessages.add(msg);
}
public void destroy() {
// TODO: Interrupt and join with threads. Close streams and socket.
}
}
Note that Nikolai is correct in that blocking I/O using 1 (or 2) threads per connection is not a scalable solution and typically applications might be written using Java NIO to get round this. However, in reality unless you're writing an enterprise server which thousands of clients connect to simultaneously then this isn't really an issue. Writing bug-free scalable applications using Java NIO is difficult and certainly not something I'd recommend.

Categories

Resources