I am writing simple Java Server which connecting only 5 users and run simply game.
My problem is communicate with clients, because the Game object is in Main Thread and every single subthread get information about specific player move (1-5 id). I don't know how to send this information to Main Thread and update game status.
Is my code correct, there aren't exists any big mistakes (this is my first project with multitasking), and what i supposed to do to communicate with Main Thread
Player.java
package Model;
import java.io.*;
import java.net.Socket;
public class Player extends Thread{
private long id;
private Socket clientSocket;
private InputStream clientInput;
private BufferedReader clientIn;
private DataOutputStream clientOut;
private String nickname;
private boolean isReady;
public Player(long id, Socket clientSocket) throws IOException {
this.id = id;
this.clientSocket = clientSocket;
this.clientInput = this.clientSocket.getInputStream();
this.clientIn = new BufferedReader(new InputStreamReader(this.clientInput));
this.clientOut = new DataOutputStream(this.clientSocket.getOutputStream());
this.isReady = false;
clientOut.writeBytes("POLACZONO\n");
clientOut.flush();
}
public void run() {
boolean isCorrect = false;
try {
while(!isCorrect) {
String login = this.clientIn.readLine();
if (!login.equals("") && login.startsWith("LOGIN") && login.length() > 6) {
this.clientOut.writeBytes("OK\n");
this.clientOut.flush();
setNickname(login.substring(login.indexOf(" ") + 1));
isCorrect = true;
this.isReady = true;
} else if (!login.equals("") && (!login.startsWith("LOGIN") || login.length() <= 6)) {
this.clientOut.writeBytes("ERROR\n");
this.clientOut.flush();
}
}
while (true) {
//DATA FROM CLIENT
}
//this.clientOut.writeBytes("START " + this.id + " " + startPlayer + "\n");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
this.clientSocket.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Main Thread
public void startServer(ServerSocket serverSocket) throws IOException {
playerList = Collections.synchronizedList(new ArrayList<Player>());
int remaining = 1;
while (true) {
if(playerList.size() < 5) {
while (playerList.size() < 5) {
Socket connectionSocket = serverSocket.accept();
playerList.add(new Player(remaining, connectionSocket));
playerList.get(playerList.size() - 1).start();
remaining++;
}
}
final int startPlayer;
if(!playerList.stream().noneMatch(x -> x.isReady())) {
startPlayer = new Random().nextInt((5 - 1) + 1) + 1;
for (Player player : playerList) {
player.getClientOut().writeBytes("START " + player.getId() + " " + startPlayer + "\n");
}
//GAME START
}
}
}
Ok it means you used one thread per user for read data and just one thread to send data to users !
In main thread you can use a LinkedBlockingQueue and pass this queue to reader threads ! Each time a packet received you can put it to the queue and take it in main thread !
Soyou must write this in your reader thread :
queue.put(data);
And this in main thread :
data = queue.take();
This queue is thread safe , so it means multi thread can put and take data !
The take method will block thread until a data put to the queue.
Related
I'm trying to intercept packets and be able to block them from incoming/outgoing, for a specific domain
In order to do that i made my (java) program adds the domain to the hosts file with a redirection to my own public ipv4 adress (this doesnt matter it just can't be the real IP and i must be able to intercept it, redirecting to my own IP makes sure nobody else in the world receives it). Secondly, i make the program listen to that signal and resend it on a different source port to the real server. (Checksum changes have been taken care of) Now the plan is to receive the response and do the exact same thing, but now by editting the source ip (my own public IP in this case) and the destination port
This should create a program where i'm a kind of middle men between a connection
But it doesnt work as expected, the moment im getting a response of the server (flags SYN/ACK), it's automatically sending them back a RST flag (IPv4/TCP) from the random chosen port by me which isnt the same as the port of the real client
I don't know if there are better ways to do this (there probably are) and how to prevent the problem I'm having, I couldn't really find similiar things to this on the internet. Any kind of help/hints would be appreciated
Keep in mind that I'm using jnetpscape at this moment and it would be nice to continue at what i'm doing right now
EDIT (code):
this is the "HConnection" class (not fully showed but all essential things):
public class HConnection {
private volatile int state = -1; // current state of the program
private volatile boolean HostFileEdited = false;
private volatile String domain = null;
private volatile boolean waitingConnection = false;
private volatile String ipOfDomain = null; // string of the server adress
private volatile byte[] ipofdomb; //4 bytes of the server adress
private volatile String myIpAdr = null; //my IP adress
private volatile byte[] myIpb; //my public IP in 4 bytes
private volatile byte[] port = null; //port of proxy
private volatile byte[] falseport = null; //port of client
private volatile ServerSocket server;
public HConnection() {
try {
server = new ServerSocket(0);
byte[] tempPortb = ByteBuffer.allocate(4).putInt(server.getLocalPort()).array();
System.out.println(server.getLocalPort());
port = new byte[]{tempPortb[2], tempPortb[3]};
(new Thread() {
public void run() {
try {
server.accept();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}).start();
state = 0;
} catch (UnknownHostException e) {System.out.println("fail");} catch (IOException e) {System.out.println("fail");}
}
public String getPublicIP () {
try{
myIpAdr = new BufferedReader(new InputStreamReader(new URL("http://checkip.amazonaws.com/").openStream())).readLine();
System.out.println(myIpAdr);
InetAddress ip = InetAddress.getByName(myIpAdr);
myIpb = ip.getAddress();
return myIpAdr;
}
catch (Exception e){}
return null;
}
public void setUrl(String domain) {
this.domain = domain;
}
public int getState() {
return state;
}
public void prepare() {
try{
URL urlofsite = new URL("https://"+domain);
InetAddress address = InetAddress.getByName(urlofsite.getHost());
ipOfDomain = address.getHostAddress();
System.out.println(ipOfDomain);
ipofdomb = address.getAddress();
addToHostsFile(getPublicIP() + "\t" + domain);
state = 1;
}
catch(Exception e){}
}
public void abort() {
removeFromHostsFile(domain);
HostFileEdited = false;
state = -1;
try {
server.close();
} catch (IOException e) { }
waitingConnection = false;
}
public void awaitConnection() {
if (state == 1) {
waitingConnection = true;
System.out.println("stap1");
StringBuilder errbuf = new StringBuilder(); // For any error msgs
int snaplen = 64 * 1024; // Capture all packets, no truncation
int flags = Pcap.MODE_PROMISCUOUS; // capture all packets
int timeout = 0; // 10 seconds in millis
Pcap pcap = Pcap.openLive("wlp4s0", snaplen, flags, timeout, errbuf);
if (pcap == null) {
System.err.printf("Error while opening device for capture: "
+ errbuf.toString());
return;
}
PcapHeader hdr = new PcapHeader(JMemory.POINTER);
JBuffer buf = new JBuffer(JMemory.POINTER);
int id = JRegistry.mapDLTToId(pcap.datalink());
while (HostFileEdited && waitingConnection && state == 1 && pcap.nextEx(hdr, buf) == Pcap.NEXT_EX_OK) {
PcapPacket packet = new PcapPacket(hdr, buf);
try {
packet.scan(id);
TcpPacket pkt = new TcpPacket(packet);
if (pkt.isTcp()) {
if (pkt.destinationIPequals(myIpAdr) && pkt.getDestinationPort() == 443 && (falseport == null || Arrays.equals(pkt.getSourcePortb(), falseport))) {
if (falseport == null) {
falseport = pkt.getSourcePortb();
}
pkt.changeDestinationIP(ipofdomb);
pkt.changeSourcePort(port);
pkt.iPchecksumFix();
pkt.tcPchecksumFix();
ByteBuffer b = ByteBuffer.wrap(pkt.getPacketInBytes());
System.out.println("10");
System.out.println("OUT"+ (pcap.sendPacket(b)));
}
else if (pkt.sourceIPequals(ipOfDomain) && pkt.getSourcePort() == 443 && falseport != null && Arrays.equals(pkt.getDestinationPortb(),port) ) {
pkt.changeSourceIP(myIpb);
pkt.changeDestinationPort(falseport);
pkt.iPchecksumFix();
pkt.tcPchecksumFix();
ByteBuffer b = ByteBuffer.wrap(pkt.getPacketInBytes());
System.out.println("IN"+ pcap.sendPacket(b));
}
}
}
catch (Exception e) {}
}
System.out.println("stap2");
if (state == 1 && waitingConnection == true) state = 2;
waitingConnection = false;
}
}
}
The "awaitConnection()" method is were currently most things are happening. But this will only be the beginning of my program
HConnection is called from the main class (SWT Designer):
private Button btnNewButton_1;
private HConnection connectie;
private void btnConnect_clicked(SelectionEvent e) throws InterruptedException {
if (btnNewButton_1.getText().equals("Connect")) {
String Url = combo.getText();
connectie = new HConnection();
connectie.setUrl(Url);
connectie.prepare();
lblNewLabel_2.setText("Waiting -> client");
new Thread(new Runnable() {
public void run() {
connectie.awaitConnection();
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if (connectie.getState() == 2) {
lblNewLabel_2.setText("Replacing URL");
}
else {
lblNewLabel_2.setText("Failed");
connectie.abort();
btnNewButton_1.setText("Connect");
}
}
});
if (connectie.getState() == 2) {
// go on with the rest of the program
}
}
}).start();
btnNewButton_1.setText("Abort");
}
else if(btnNewButton_1.getText().equals("Abort")) {
connectie.abort();
lblNewLabel_2.setText("Aborted");
btnNewButton_1.setText("Connect");
}
}
The following code accepts a connection, but doesn't maintain a reference to the resulting Socket instance. This Socket is eligible for garbage collection, and when that happens, it is automatically closed. A client sending data to that socket will then receive an RST.
public void run() {
try {
server.accept();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
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++;
}
I am working on a Java process that contains 2 threads: one for reading a file's contents and adding them in one shared blocking queue; and one for retrieving the lines from the blocking queue and sending them through the network (under a specified send rate). The two classes I have are the following:
Updated Code below
Producer Thread:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
public class SourceFileProducer implements Runnable {
private File file;
private BufferedReader reader;
private ArrayBlockingQueue<String> buffer;
private String fileName;
private String endMarker;
public SourceFileProducer(ArrayBlockingQueue<String> buffer,
String endMarker, String fileName) {
this.buffer = buffer;
this.endMarker = endMarker;
file = new File(fileName);
if(file.exists()) {
try {
reader = new BufferedReader(new FileReader(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
this.fileName = fileName;
}
#Override
public void run() {
System.out.println("SourceFileProducer thread-" + Thread.currentThread().getId() + " initiating with source file: " + fileName);
String line = "";
try {
while((line = reader.readLine()) != null) {
try {
buffer.put(line);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
buffer.put(endMarker);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("SourceFileProducer thread-" + Thread.currentThread().getId() + " scanned and buffered the whole file.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
and the Consumer thread:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
public class SourceFileConsumer implements Runnable {
private ArrayBlockingQueue<String> buffer;
private BufferedReader socketInput;
private PrintWriter socketOutput;
private Socket client;
private ServerSocket serverSocket;
private long checkpoint[] = null;
private int rate[] = null;
private String endMarker;
public SourceFileConsumer(ArrayBlockingQueue<String> buffer, String endMarker,
ServerSocket serverSocket, Socket client, long checkpoint[], int rate[]) {
this.buffer = buffer;
this.endMarker = endMarker;
this.client = client;
try {
socketOutput = new PrintWriter(client.getOutputStream(), true);
socketInput = new BufferedReader(new InputStreamReader(client.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
this.checkpoint = new long[checkpoint.length];
this.rate = new int[rate.length];
for(int i = 0; i < checkpoint.length; i++) {
this.checkpoint[i] = checkpoint[i];
this.rate[i] = rate[i];
}
this.serverSocket = serverSocket;
}
#Override
public void run() {
String line = null;
long start = System.currentTimeMillis();
int index = 0;
boolean fileScanFlag = true;
while(fileScanFlag) {
long startTimestamp = System.currentTimeMillis();
long interval = (startTimestamp - start) / 1000L;
if(interval >= checkpoint[index]) {
if(index < checkpoint.length - 1) {
if(interval >= checkpoint[index + 1]) {
index += 1;
System.out.println("SourceFileConsumer thread-" + Thread.currentThread().getId() +
" progressed to checkpoint " + index + " with rate: " + rate[index]);
}
}
}
int counter = 0;
while(counter < rate[index]) {
try {
line = buffer.take();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
if(line == endMarker) {
fileScanFlag = false;
break;
}
if(socketOutput != null && socketOutput.checkError()) {
System.out.println("SourceFileConsumer Thread-" + Thread.currentThread().getId() + " detected broken link...");
try {
client = serverSocket.accept();
socketOutput = new PrintWriter(client.getOutputStream(), true);
socketInput = new BufferedReader(new InputStreamReader(client.getInputStream()));
} catch(IOException e) {
e.printStackTrace();
}
System.out.println("SourceFileConsumer Thread-" + Thread.currentThread().getId() + " re-established connection...");
}
if(socketOutput != null)
socketOutput.println(line);
counter += 1;
}
long endTimestamp = System.currentTimeMillis();
if(endTimestamp - startTimestamp <= 1000) {
System.out.println("thread-" + Thread.currentThread().getId() + " input rate: " + counter +
", wait time: " + (1000 - (endTimestamp - startTimestamp)));
try {
Thread.sleep((1000 - (endTimestamp - startTimestamp)));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
if(socketInput != null && socketOutput != null && client != null) {
try {
socketInput.close();
socketOutput.close();
client.close();
} catch(IOException e) {
e.printStackTrace();
}
}
System.out.println("SourceFileConsumer Thread-" + Thread.currentThread().getId() + " transfer complete.");
}
}
The problem is that, after a while, both threads hang and no tuples are sent. When I run a top command in my Linux machine, I see that the Java process, in which the two threads are running in, uses a really small amount of CPU time. Why is this happening? Is this a problem with starvation? I think that starvation can be avoided by using the LinkedBlockingQueue.
Any hints?
Thanks,
Nick
That’s quite a lot of code, especially within your consumer. So it’s not possible to preclude that there are multiple errors. I recommend to simplify your code to narrow the problem, e.g. test your producer-consumer hand-off and the network operations independently.
One obvious problem is that you are trying to signal the end of a file via an AtomicBoolean but your consumer isn’t actually testing it before taking items. If you look at the place where it takes items, there is an inner loop:
while(counter < rate[index]) {
try {
line = buffer.take();
…
Since the producer has no influence on the counter < rate[index] condition, it has no control over how many lines the consumer will attempt to take before checking the state of the fileScanFlag.
But even if you try to fix this by checking the boolean flag right before take, the result is broken due to possible race conditions. The atomic boolean and the blocking queue are both thread-safe on their own but your combination of the two is not.
Putting the last item on the queue and setting the flag are two distinct operations. Right in-between these two actions, the consumer can take the last item, recheck the flag and find it being false and go to the next attempt to take while the producer is about to set it to true.
One solution is to change the order of the operations on the consumer side, which requires resorting to polling:
polling: for(;;) {
line = buffer.poll(timeout, timeOutUnit); // control the cpu consumption via timeout
if(line!=null) break polling;
if(fileScanFlag.get()) break outerLoop;
}
An alternative is not to use two different communication constructs. Instead of maintaining a boolean flag, place an end marker object to the queue once the file reached an end. This is one of the rare cases, where using the identity of a String rather than equals is appropriate:
public class SourceFileProducer implements Runnable {
private String endMarker;
…
public SourceFileProducer(LinkedBlockingQueue<String> buffer,
String endMarker, String fileName) {
this.buffer = buffer;
this.endMarker = endMarker;
…
#Override
public void run() {
System.out.println("SourceFileProducer thread-" + Thread.currentThread().getId()
+ " initiating with source file: " + fileName);
String line;
try {
while((line = reader.readLine()) != null) buffer.put(line);
} catch (IOException|InterruptedException e) {
e.printStackTrace();
}
buffer.put(endMarker);
}
public class SourceFileConsumer implements Runnable {
private String endMarker;
…
public SourceFileConsumer(LinkedBlockingQueue<String> buffer, String endMarker,
ServerSocket serverSocket, Socket client, long checkpoint[], int rate[]) {
this.buffer = buffer;
this.endMarker = endMarker;
…
line = buffer.take();
if(line==endMarker) break;
The value of the end marker doesn’t matter but it’s object identity. Hence, the code which creates the two threads must contain something like:
// using new to ensure unique identity
private static final String EOF = new String("end of file");
…
new SourceFileProducer(queue, EOF, …)
new SourceFileConsumer(queue, EOF, …)
The new operator guarantees to produce an object with a unique identity, therefore, comparing that marker object with any other String, i.e. the lines returned by BufferedReader, via == will always evaluate to false. Care must be taken not to let the marker object escape to code not knowing about its special role.
Good afternoon everyone,
I am working on a school project that requires me to use semaphores to control access to resources. From what I have developed so far, they are:
Semaphore 1) Waiting Area - This permits only 15 customers (Threads) to enter the waiting area, else they are rejected from the store (using TryAcquire).
Semaphore 2) ServerQueue - This permits customers (Threads) to use the only 3 servers in the restaurant once in the waiting area.
My Problem: Our professor requires the serverQueue to take the shortest order (IE, the thread with the least amount of burritosOrdered) when in the waitingArea.
Full flow of application:
Main method instantiates a serverQueue (3 servers) and a waitingArea (15 customers)
Main method instantiates and starts 20 customer threads
Each customer (Thread) run function has been overridden to attempt to get in the waiting area
Each customer in the waitingArea tries to access a server in the serverQueue
How can I tell the serverQueue to get the shortest order? Because the threads override the run, I don't have direct access to an array of all the threads to compare their values.
Thank you for taking a look!
Main
public class Main {
private static final int numCustomers = 5;
public static void main(String[] args)
{
ServerQueue serverQueue = new ServerQueue();
WaitingArea waitingArea = new WaitingArea(3, serverQueue);
Thread customers[] = new Thread[numCustomers];
for (int i = 0; i < numCustomers; i++)
{
customers[i] = new Thread(new Customer(waitingArea), "Customer " + i);
}
for (int i = 0; i < numCustomers; i++)
{
customers[i].start();
}
}
}
Customer
import java.util.Date;
import java.util.Random;
// Runnable is an interface that facilitates threads
public class Customer implements Runnable {
// The semaphore
// private ServerQueue serverQueue;
private WaitingArea waitingArea;
public int burritosOrdered;
public int burritosMade = 0;
// Constructor, allow semaphore to be passed/assigned
public Customer(WaitingArea waitingArea) {
this.waitingArea = waitingArea;
Random r = new Random();
this.burritosOrdered = r.nextInt(21);
}
public void setBurritosMade(int newBurritos) {
this.burritosMade += newBurritos;
}
// We must override the run function within Runnable
// The run function is called by threadObject.start();
#Override
public void run() {
waitingArea.seatCustomer(burritosOrdered);
}
}
waitingArea
import java.util.Date;
import java.util.concurrent.Semaphore;
public class WaitingArea {
private Semaphore semaphore;
private ServerQueue serverQueue;
private int maxCustomers;
public WaitingArea(int maxCustomers, ServerQueue serverQueue) {
semaphore = new Semaphore(maxCustomers, true);
this.serverQueue = serverQueue;
this.maxCustomers = maxCustomers;
}
public void seatCustomer(int burritosOrdered)
{
boolean hasPermit = false;
try
{
hasPermit = semaphore.tryAcquire();
if(hasPermit) {
System.out.println(new Date() + " - "
+ Thread.currentThread().getName()
+ " entered store ordering "
+ burritosOrdered + " burritos");
serverQueue.finishOrder();
} else {
System.out.println(new Date() + " - " + Thread.currentThread().getName() + " left due to full shop");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(hasPermit) {
semaphore.release();
System.out.println(new Date() + " - "
+ Thread.currentThread().getName()
+ " left with " + burritosOrdered + " burritos made");
}
}
}
}
serverQueue
import java.util.Date;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ServerQueue {
// This Semaphore will keep track of no. of servers used at any point.
private final Semaphore semaphore;
// While checking/acquiring a free server out of three available servers, we will use this lock.
private final Lock serverLock;
// This array represents the pool of free server.
private boolean freeServers[];
public ServerQueue() {
semaphore = new Semaphore(1, true);
freeServers = new boolean[1];
serverLock = new ReentrantLock();
// Set all servers to available
for(int i=0;i<freeServers.length;i++) {
freeServers[i] = true;
}
}
public void finishOrder() throws InterruptedException {
try {
System.out.println(semaphore.getClass());
// Decrease the semaphore counter to mark a printer busy
semaphore.acquire();
// Get the server printer
int assignedServer = getServer();
Thread.sleep(3000);
// Print the job
System.out.println(new Date() + " - " + Thread.currentThread().getName()
+ " is getting service from server " + assignedServer);
//Server is done; Free the server to be used by other threads.
releaseServer(assignedServer);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.print(new Date() + " - " + Thread.currentThread().getName() + " has been served\n");
//Increase the semaphore counter back
semaphore.release();
}
}
//Acquire a free server to finish a job
private int getServer() {
int foundServer = -1;
try {
//Get a lock here so that only one thread can go beyond this at a time
serverLock.lock();
//Check which server is free
for (int i=0; i<freeServers.length; i++)
{
//If free server found then mark it busy
if (freeServers[i])
{
foundServer = i;
freeServers[i] = false;
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//Allow other threads to check for free servers
serverLock.unlock();
}
return foundServer;
}
//Release the server
private void releaseServer(int i) {
serverLock.lock();
//Mark the server as free
freeServers[i] = true;
serverLock.unlock();
}
}
I am working on a project trying to make several people be able to control a robot arm. For this they have to connect to a Java server that then sends the commands to a robot screen for video conferencing.
I am trying to have a thread for each client and then I want to be able to switch between the different clients based on sound, because I want the speaker to be able to control the robot.
The clients all provide positional data and the level of sound taken by the kinect, and sent to the server in the form of a string.
I am having problems with performing the switch. Currently they seem to be switching back and forth and it makes the robot go haywire.
Is there a good way of comparing the threads to each other, find the appropriate one, switch to that, all the while checking the other threads to see if or when they become the most appropriate one? While also checking in case other clients try to connect to the server?
Thank you for your help.
I also include my code in case you want to look through it and get a better idea.
This is the server class:
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Hashtable;
public class MultiThreadedServer implements Runnable {
protected int serverPort = 8888;
protected ServerSocket serverSocket = null;
protected boolean isStopped = false;
protected Thread runningThread = null;
protected Thread clientThread = null;
protected Thread threadThread = null;
private Hashtable<Long, WorkerRunnable> Users = new Hashtable<Long, WorkerRunnable>();
private ArrayList<Thread> ClientThreads = new ArrayList<Thread>();
private WorkerRunnable client = null;
private ThreadHandler threadHandler = null;
private int sound_max = 0;
private boolean once = true;
public MultiThreadedServer (int port) {
this.serverPort = port;
}
public void run() {
synchronized(this) {
this.runningThread = Thread.currentThread();
}
openServerSocket();
threadHandler = new ThreadHandler();
while( !isStopped() ) {
Socket clientSocket = null;
try {
System.out.println(InetAddress.getLocalHost());
clientSocket = this.serverSocket.accept(); // Connect to clients
} catch (SocketTimeoutException e) {
} catch (IOException e) {
if( isStopped() ) {
System.out.println("Server Stopped");
return;
}
throw new RuntimeException("Error accepting client connection", e);
}
client = new WorkerRunnable(clientSocket, "Multithreaded Server");//Class does client work
clientThread = new Thread(client); // Make a thread for each client
clientThread.start(); // start thread
threadHandler.setUp(client, clientThread); // Set up the thread handler
if ( once == true) { // make sure the threadHandler thread is only created once
threadThread = new Thread(threadHandler);
threadThread.start();
once = false;
}
}
System.out.println("Server Stopped");
}
/**
* Check if the socket is stopped
* #return true if the socket is stopped
*/
private synchronized boolean isStopped() {
return this.isStopped;
}
/**
* Stop and close the socket
*/
public synchronized void stop() {
this.isStopped = true;
try {
this.serverSocket.close();
} catch (IOException e) {
throw new RuntimeException("Error closing server", e);
}
}
/**
* Open server socket
*/
private void openServerSocket() {
try {
this.serverSocket = new ServerSocket(this.serverPort);
} catch (IOException e) {
throw new RuntimeException("Cannot open port 8888", e);
}
}
}
This is the Worker class, that handles the data from the clients:
import gnu.io.NoSuchPortException;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class WorkerRunnable implements Runnable {
protected Socket clientSocket = null;
protected String serverText = null;
private BufferedReader inFromClient;
private DataOutputStream outToClient;
private int[] currentPos = new int[6];
private boolean connected = false;
static TwoWaySerialComm serialCom = null;
static MultiServoState mState;
static int sound_average;
int[] degrees = new int[7];
int count = 0;
public WorkerRunnable(Socket clientSocket, String serverText) {
this.clientSocket = clientSocket;
this.serverText = serverText;
initCurrentPos();
if (serialCom == null) {
serialCom = new TwoWaySerialComm();
}
try {
if (!serialCom.isConnected("COM5")) {
try {
serialCom.connect("COM5");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mState = new MultiServoState(serialCom);
}
} catch (NoSuchPortException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void run() {
try {
work();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
}
}
public void work() throws InterruptedException {
try {
InputStream input = clientSocket.getInputStream();
OutputStream output = clientSocket.getOutputStream();
inFromClient = new BufferedReader(new InputStreamReader(input));
outToClient = new DataOutputStream(output);
long time = System.currentTimeMillis();
updateData();
String message = null;
long endTime = System.currentTimeMillis() + 2000;
while ((message = (String) inFromClient.readLine()) != null) {
System.out.println("Message Received: " + message);
parse(message);
sound_average = degrees[6];
//
// Send the positional data to the robot
//
mState.runServo(degrees[0], degrees[1], degrees[2],
degrees[3], degrees[4], degrees[5]);
//
// Send a response information to the client application
//
currentPos[0] = mState.getCurrentPos(0);
currentPos[1] = mState.getCurrentPos(1);
currentPos[2] = mState.getCurrentPos(2);
currentPos[3] = mState.getCurrentPos(3);
currentPos[4] = mState.getCurrentPos(4);
try {
updateData();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("Request processed: " + time);
} catch (IOException e) {
// report exception somewhere
e.printStackTrace();
}
}
/**
* Initiate the robot's starting position.
*/
public void initCurrentPos()
{
currentPos[0] = 100;
currentPos[1] = 100;
currentPos[2] = 100;
currentPos[3] = 100;
currentPos[4] = 100;
currentPos[5] = 0;
}
/**
* Send the data to the client
*
* #throws IOException
*/
public void updateData() throws IOException {
String sentence = Integer.toString(currentPos[0]) + ", " +
Integer.toString(currentPos[1]) + ", " +
Integer.toString(currentPos[2]) + ", " +
Integer.toString(currentPos[3]) + ", " +
Integer.toString(currentPos[4]) + "." + "\n";
outToClient.flush();
outToClient.writeBytes(sentence);
}
/**
* Get the clients sound average
* #param message
*/
public int getSoundAverage() {
return sound_average;
}
public void parse(String message) {
if (message != null) {
char c;
StringBuilder sb = new StringBuilder(4);
int j = 0;
boolean help = false;
for (int i = 0; i < message.length(); i++) {
c = message.charAt(i);
if (Character.isDigit(c)) {
sb.append(c);
help = true;
}
if (!Character.isDigit(c) && help == true) {
degrees[j] = Integer.parseInt(sb.toString());
j++;
help = false;
sb.delete(0, sb.length());
}
}
}
System.out.println("Waiting for client message...");
}
/**
* Close all connections
*/
public void close() {
if (connected) {
synchronized (this) {
connected = false;
}
if (outToClient != null) {
try {
outToClient.close();
synchronized (this) {
outToClient = null;
}
} catch (IOException e) {
// there is nothing we can do: ignore it
}
}
if (inFromClient != null) {
try {
inFromClient.close();
synchronized (this) {
inFromClient = null;
}
} catch (IOException e) {
// there is nothing we can do: ignore it
}
}
if (clientSocket != null) {
try {
clientSocket.close();
synchronized (this) {
clientSocket = null;
}
} catch (IOException e) {
// there is nothing we can do: ignore it
}
}
}
}
public void returnThread() {
return;
}
}
The final class is the thread handler where I try to compare sound levels and yield all threads except the loudest one:
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.Hashtable;
import com.research.aserver.WorkerRunnable;
public class ThreadHandler implements Runnable {
protected boolean isStopped = false;
protected Thread runningThread = null;
protected Thread clientThread = null;
private Hashtable<Long, WorkerRunnable> Users = new Hashtable<Long, WorkerRunnable>();
private ArrayList<Thread> ClientThreads = new ArrayList<Thread>();
private WorkerRunnable client = null;
private int sound_max = 0;
private int index = 0;
public ThreadHandler() {
}
public void setUp(WorkerRunnable client, Thread clientThread) {
this.client = client;
this.clientThread = clientThread;
Users.put(clientThread.getId(), this.client); // Place clients in a list with its thread ID as key
ClientThreads.add(this.clientThread); // List of client threads
}
#Override
public void run() {
long endTime = System.currentTimeMillis() + 2000; // Help variable to check every 2 sec
while (!Users.isEmpty() && !ClientThreads.isEmpty()) {
for (int i = 0; i < ClientThreads.size(); i++) { // Remove clients and threads if no longer active
if (!ClientThreads.get(i).isAlive()) {
Users.remove(ClientThreads.get(i).getId());
ClientThreads.get(i).interrupt();
ClientThreads.remove(i);
}
}
if(System.currentTimeMillis() >= endTime) { // Do work every 2 sec
for (int i = 0; i < ClientThreads.size(); i++) { // Get the client with the loudest sound
if (sound_max < Users.get(ClientThreads.get(i).getId()).getSoundAverage()) {
sound_max = Users.get(ClientThreads.get(i).getId()).getSoundAverage();
index = i;
}
}
for (int i = 0; i < ClientThreads.size(); i++) { // yield all threads that are not the loudest
if (Users.get(ClientThreads.get(index).getId()) != Users.get(ClientThreads.get(i).getId())){
ClientThreads.get(i).yield();
index = 0;
}
}
endTime = System.currentTimeMillis() + 2000; // update time
}
sound_max = 0;
}
}
}
One idea might be to use a PriorityBlockingQueue and define a quality value for each input, which is then sort by quality automatically inside the list.
Using this your consumer thread can simply fetch the first one in line and process it, knowing that it is the most appropriate one, while the generator threads can simply throw all input in the Queue.