I'm writing a application that has a connection to a measuring device.
This device can be connected by serial and networkconnection.
The serial side I'm done with and catches the data that is send by using a SerialPortEvent.
Now I'm trying to accomplish the same thing with a socket. I have the connection working, and I can send/and receive data from the device. Problem with this is that I force a Thread.sleep to wait for all data to be ready. But now I want to automatically catch the receiving data like it did with the SerialPortEvent.
This is a 2 part question:
Is there a similar Event for a socket? Or is a custom solution preferable in this situation? If so, please add explanation.
How to accomplish DATA_AVAILABLE for a socket
Below here is a snippet of the code (only the neccesary catching part) for the SerialEventPort, as a reference to what I also want to accomplish with a socket:
#Override
public void serialEvent(SerialPortEvent event)
{
if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE)
{
try
{
int available = inputStream.available();
byte[] readBuffer = new byte[available];
if (available > 0)
{
inputStream.read(readBuffer);
}
}
}
In this SO answer Sorrow states the following:
I recommend using java.nio.channels.SocketChannel connected with Selector and SelectionKey. This solution is somewhat event-based, but is more complicated than just plain sockets.
If you decide for that solution you will find the code examples in the linked answer.
But, if you are talking about java.net.Socket then, no, there are no events. I like JTeagle's answer on a similar question:
This is often done by spawning a separate thread for the client that continuously makes blocking calls to read() from the stream - that way, as soon as data becomes available the read() call unblocks and can act on what it received ('the event fires'), then it goes back to blocking waiting for the next event.
And in my experience also, that's mostly how sockets are handled in Java. I wrote an implementation of event based socket. Since reading is blockable, a thread is most probably needed not to block your main program:
public class ObservableSocket extends Thread {
private final Socket socket;
private final ArrayList<ObservableSocketListener> listeners;
private volatile boolean isReading;
private int BUFFER_SIZE = 1024;
public ObservableSocket(String host, int port) throws UnknownHostException, IOException {
this.socket = new Socket(host, port);
this.listeners = new ArrayList<ObservableSocketListener>(1);
isReading = true;
this.start();
}
public void addListener(ObservableSocketListener l) {
if (!listeners.contains(l)) {
listeners.add(l);
}
}
public void removeListener(ObservableSocketListener l) {
if (!listeners.contains(l)) {
listeners.remove(l);
}
}
public void die() {
isReading = false;
try {
this.join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
public void write(byte[] data) throws IOException {
socket.getOutputStream().write(data);
socket.getOutputStream().flush();
}
private byte[] getData(byte[] buffer, int red) {
byte[] redData = new byte[red];
System.arraycopy(buffer, 0, redData, 0, red);
return redData;
}
#Override
public void run() {
byte[] buffer = new byte[BUFFER_SIZE];
int red;
ObservableSocketEvent event;
try {
while (isReading && (red = socket.getInputStream().read(buffer)) > -1) {
event = new ObservableSocketEvent(this, getData(buffer, red));
for (ObservableSocketListener l : listeners) {
l.dataAvailable(event);
}
}
}
catch (Exception exception) {
event = new ObservableSocketEvent(this, exception);
for (ObservableSocketListener l : listeners) {
l.errorOccured(event);
}
}
finally {
if (socket != null) {
try {
socket.close();
for (ObservableSocketListener l : listeners) {
l.closed(new ObservableSocketEvent(this));
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
}
This is the listener class you'll need to implement:
public interface ObservableSocketListener extends EventListener {
public void dataAvailable(ObservableSocketEvent event);
public void errorOccured(ObservableSocketEvent event);
public void closed(ObservableSocketEvent event);
}
And the event class:
public class ObservableSocketEvent extends EventObject {
private final byte[] data;
private final Exception exception;
public ObservableSocketEvent(Object source) {
super(source);
this.data = null;
this.exception = null;
}
public ObservableSocketEvent(Object source, byte[] data) {
super(source);
this.data = data;
this.exception = null;
}
public ObservableSocketEvent(Object source, Exception exception) {
super(source);
this.data = null;
this.exception = exception;
}
public byte[] getData() {
return data;
}
public Exception getException() {
return exception;
}
}
I made a server generating some random data for testing this code, this is how I used it form my client's class main method:
ObservableSocket observableSocket = new ObservableSocket("localhost", 3339);
observableSocket.addListener(new ObservableSocketListener() {
#Override
public void dataAvailable(ObservableSocketEvent event) {
System.out.println("data received: "+new String(event.getData()));
}
#Override
public void closed(ObservableSocketEvent event) {
System.out.println("closing socket");
}
#Override
public void errorOccured(ObservableSocketEvent event) {
System.out.println("error occured");
event.getException().printStackTrace();
}
});
Thread.currentThread().sleep(10000);
observableSocket.die();
And it outputs:
data received: data 0
data received: data 1
data received: data 2
data received: data 3
data received: data 4
closing socket // thread is alive here
BUILD SUCCESSFUL (total time: 10 seconds) // thread dies here
In terms of my test, the sleep in client is needed because the die method:
exits the reading loop (via a flag set to false)
and waits for the thread to die (Thread.join)
Without the sleep, the test client finishes immediatley (the die method works). Without the die method, the ObservableSocket thread lives after the test is over.
When using this code you should be aware of two things:
upon instantiating the ObservableSocket the Socket is immiediatly connected and a Thread is started.
you must call the die method from a thread which is not the ObservableSocket thread (e.g. don't call that method from within that class)
Related
Edited my question for clarification and code:
My goal is to pass my String data from my background thread, to my main application thread. Any help is appreciated.
Here is the code that creates the main background thread. This is located in my Server.java class
public class Server {
boolean isConnected = false;
Controller controller = new Controller();
public void startHost() {
Thread host = new Thread(() -> {
Controller controller = new Controller();
ServerSocket server = null;
try {
server = new ServerSocket(GeneralConstants.applicationPort);
} catch (BindException e2) {
System.out.println("Port Already in Use!");
} catch (IOException e) {
//do nothing
}
while (true) {
if (server == null) { break; }
try {
Socket client = server.accept();
System.out.println("Client Connected: " + isConnected);
if (!isConnected) {
controller.createClientHandler(client);
isConnected = true;
System.out.println("Client Connected: " + isConnected);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
host.setDaemon(true);
host.start();
}
Here is the code that is then called when a client is connected, located in my Controller.java class.
public synchronized void createClientHandler(Socket client) {
boolean alreadyConnected = false;
if (alreadyConnected) {
//do NOT assign multiple threads for each client
} else {
ClientHandler handleClients = new ClientHandler("client", client);
}
}
The program then creates two background threads for my client, one to manage receiving messages, and sending messages.
public ClientHandler(String name, Socket s) {
clientSocket = s;
clientName = name;
receiveThread = new Thread(this::receive);
sendThread = new Thread(this::send);
connected = clientSocket.isConnected();
receiveThread.start();
sendThread.start();
}
The thread then successfully creates the inputstream and passes the object to my controller. Which then process and grabs a string assigning it to a variable
public synchronized void handleReceivedPacket(String name, BufferedReader in) {
try {
data = in.readLine();
System.out.println("Successfully assigned data to: " + data);
} catch (IOException e) {
System.out.println("Unable to read result data");
}
}
How do I access my String data from the main thread without getting null?
Aka I can call (or something similar)
controller.returnData();
from my main application. From which it'll either return null (no data yet), or actually return my data. Right now, it's always null.
Edit, this is what's actually calling controller.returnData() {
I don't want to paste a massive amount of code for fear of reaching StackOverflow's code limit, so here's my application structure.
My JavaFX creates the scene, and creates a root gridpane, it then calls a method that creates sub gridpanes based the specified input. Aka, a user can press "Main Menu" that calls my method setScene() which removes the current "sub-root" gridpane and creates a "new" scene. Right now, I have a GameBoard.java class which on button press, calls controller.returnData()
PassOption.setOnAction(event -> {
System.out.println(controller.returnData());
});
There is no functional purpose for this besides testing. If I can receive the data, then I can expand on this using the data.
Start thinking about design. In network applications you typically have to manage the following responsibilites:
Connected clients and their state (connection state, heartbeats, ...)
Received messages from the clients
Messages to transmit to the clients
It makes sense to separate those responsibilities in order to keep the code clean, readable and maintainable.
Separation can mean both, thread-wise and class-wise.
For example, you could implement it as follows:
The class ClientAcceptor is responsible for opening the socket and accepting clients. As soon as a client has connected, it delegates the further work to a controller and then waits for other clients:
public class ClientAcceptor implements Runnable {
#Override
public void run() {
while (true) {
ServerSocket server;
try {
server = new ServerSocket(1992);
Socket client = server.accept();
if (client.isConnected()) {
controller.createClientHandler(client);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
The controller could then create a handler (if the controller decides to do so, e.g. it could also decline the client). The ClientHandler class could look as follows:
public class ClientHandler {
private Thread receiveThread;
private Thread sendThread;
private boolean connected;
private Socket clientSocket;
private String clientName;
private LinkedBlockingDeque<byte[]> sendQueue;
public ClientHandler(String name, Socket s) {
clientSocket = s;
clientName = name;
receiveThread = new Thread(() -> receive());
sendThread = new Thread(() -> send());
connected = clientSocket.isConnected();
receiveThread.start();
sendThread.start();
}
private void receive() {
BufferedInputStream in = null;
try {
in = new BufferedInputStream(clientSocket.getInputStream());
} catch (IOException e) {
connected = false;
}
while (connected) {
try {
byte[] bytes = in.readAllBytes();
if (bytes != null && bytes.length > 0) {
controller.handleReceivedPacket(clientName, bytes);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void send() {
BufferedOutputStream out = null;
try {
out = new BufferedOutputStream(clientSocket.getOutputStream());
} catch (IOException e) {
connected = false;
}
while (connected) {
byte[] toSend = sendQueue.getFirst();
if (toSend != null && toSend.length > 0) {
try {
out.write(toSend);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void send(byte[] packet) {
sendQueue.add(packet);
}
public void close() {
connected = false;
}
}
The ClientHandler is responsible for receiving and transmitting data. If a packet arrives it informes the controller, which parses the packet. The ClientHandler also provides a public API to send data (which is stored in a queue and handled by a thread) and close the connection.
The above code examples are neither tested, nor complete. Take it as a starting point.
I have 7 threads running in an ExecutorPool that process data and occasionally need data from a listener instance running on another thread. The listener sends a request over a socket to a server and a while later, when the result is returned, the listener will return the data to the worker thread that called it. I want to block the worker thread until the requested data is returned, but I don't want to block the listener from making other requests from the other worker threads. How do I do that?
If one thread hands off work to another thread, and then subsequently simply waits for the result, you don't need another thread to do the work. You may need a class that does the work, but which is called on the same thread. And if the same instance is used by multiple threads some synchronization may be needed. But the bottom line is this :
You don't need the listener thread. Replace it with a component that handles a request, and call it synchronously.
Edit
Given your own answer, your problem is a bit clearer. As #JimN suggests you probably want to hand out a Future to the worker thread, and make it a CompletableFuture the Listener keeps in a Map keyed by request id until the response returns.
Sample code :
public class WorkUnitProcessor implements Runnable {
// ...
#Override
public void run() {
while(true) {
WorkUnit work = master.getNextWorkUnit();
if(work == null) return;
doWork(work);
}
}
public void doWork(WorkUnit work) {
//Do some work...
try {
DataRequest dataRequest = createRequest(work);
Future<Response> future = server.getData(dataRequest);
Response response = future.get(); // this call blocks until the Response is available.
//finish doing work
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
// handle e.getCause()
}
}
// ...
}
public class Server implements DataSourceDrivenCallback {
private final DataSource dataSource;
private Map<Integer, CompletableFuture<Response>> openRequests = new ConcurrentHashMap<>();
public Server(DataSource dataSource) {
this.dataSource = dataSource;
}
#Override
public void incomingDataCallback(int requestId, ChunkOfData requestedData) {
CompletableFuture<Response> responseHolder = openRequests.remove(requestId); // get the responseHolder
if (responseHolder != null) {
responseHolder.complete(toResponse(requestedData)); // make the response available.
}
}
public Future<Response> getData(DataRequest datarequest) {
int requestId = dataSource.submitRequest(serializeAndTranslateRequest(datarequest));
CompletableFuture<Response> future = new CompletableFuture<>();
openRequests.put(requestId, future);
return future;
}
// ...
}
I think this might work. What I was looking for is described here:
https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
It's the ability to make a thread sleep until it is notified by the thread that it is waiting on. Seems easy to use.
public class DataProcessor {
private List<WorkUnit> work;
private Server server;
public DataProcessor(List<WorkUnit> work, int numprocessors) {
this.work = work;
setupProcessors(numprocessors);
Server server = new Server();
}
private void setupProcessors(int numprocessors) {
for(int i = 0; i < numprocessors; i++) {
WorkUnitProcessor worker = new WorkUnitProcessor(this, server);
worker.start();
}
}
public synchronized WorkUnit getNextWorkUnit() {
if(work.isEmpty()) return null;
return work.remove(0);
}
}
public class WorkUnitProcessor(Server server) {
private DataProcessor master;
private Server server;
public WorkUnitProcessor(DataProcessor master) {
this.master = master;
}
#Override
public void run() {
while(true) {
WorkUnit work = master.getNextWorkUnit();
if(work == null) return;
doWork(work);
}
}
public void doWork(WorkUnit work) {
//Do some work...
server.getData(datarequest, this);
while(!datarequest.filled) {
try {
wait();
} catch (InterruptedException e) {}
}
//finish doing work
}
}
public class Server implements DataSourceDrivenCallback {
private DataSource ds;
private Map<Integer, OpenRequest> openrequests;
public Server() {
//setup socket and establish communication with server through DataSource object
DataSource ds = new DataSource(<ID>, <Socket>);
}
public synchronized void getData(DataRequest datarequest, WorkUnitProcessor workerthread) {
int requestid = ds.submitRequest(serializeAndTranslateRequest(datarequest));
openrequests.add(new OpenRequest(workerthread, datarequest));
}
#Override
public void incomingDataCallback(int requestid, ChunkOfData requesteddata) {
OpenRequest request = openrequests.get(requestid);
request.datarequest.storeData(requesteddata);
request.workerthread.notify();
}
}
public class OpenRequest {
private WorkUnitProcessor workerthread;
private DataRequest datarequest;
//other details about request
}
My project used JSSC library for linking PC and microcontroller.
Write method:
public void write(byte[] buffer) throws SerialPortException {
if (serialPort.isOpened())
serialPort.writeBytes(buffer);
}
Read method:
public byte[] read() throws SerialPortException {
byte[] result = null;
FutureTask<byte[]> task = new FutureTask<>(new PortReader());
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
result = (byte[]) executor.submit(task).get(1000, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
System.err.println(getClass().getSimpleName() + " READ: Timeout exception!");
}
return result;
}
private class PortReader implements Callable<byte[]>, SerialPortEventListener {
private byte[] data = null;
#Override
public void serialEvent(SerialPortEvent event) {
if (event.isRXCHAR() && event.getEventValue() > 0) {
try {
data = serialPort.readBytes(event.getEventValue());
} catch (SerialPortException e) {
e.printStackTrace();
}
}
}
#Override
public byte[] call() throws Exception {
if (data == null)
Thread.sleep(200);
return data;
}
}
I tried to implement a synchronous write (immediately send data) to the port and asynchronous read (waiting for the input data at least 1000 ms) from port.
Is it correct decision? Maybe there are other ways of asynchronous data reading?
Thank you!
Better to use the wait event as used inside serialport class. This way makes the port to wait until a command is equecuted.
serialPort.writeBytes(buffer);//Write data to port
serialPort.addEventListener(new PortReader(serialPort), SerialPort.MASK_RXCHAR);
int[][] eventArray=serialPort.waitEvents()
for (int i = 0; i < eventArray.length; i++) {
if ((eventArray[i][0] > 0) ) {
serialPort.eventListener.serialEvent(new SerialPortEvent("COM1", eventArray[i][0], eventArray[i][1])); //give your port name and the events got.
}
}
So I have a multithreaded server, and data sends back and forth correctly, but my write operations stalls on the slower connections. I've noticed that it goes by connection time. The first client to connect always receives data first from the server. The next one has to wait until the first one is done receiving and so on so forth. What I'm looking for is a server that sends data to many clients without waiting for a client to finish receiving. I've read up about NIO (non-blocking), but I'd really prefer keeping my current method, which is to use a separate thread for each client.
Here's the code.
Server:
public class Server implements Runnable {
private Thread thread;
private ServerSocket serverSocket;
private ArrayList<ClientThread> clients;
public Server(int port) throws IOException {
thread = new Thread(this);
clients = new ArrayList<ClientThread>();
serverSocket = new ServerSocket(port);
thread.start();
}
#Override
public void run() {
while (true) {
try {
//Listens to clients connecting.
ClientThread client = new ClientThread(serverSocket.accept());
clients.add(client);
ServerWindow.addText("-- Someone connected!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void broadcast(String data) {
broadcast(data, null);
}
public void broadcast(String data, ClientThread exclude) {
int amount = clients.size();
for (int i = 0; i < amount; i++) {
if (!clients.get(i).equals(exclude)) { //Don't send it to that client.
try {
clients.get(i).broadcast(data);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
Client thread object:
public class ClientThread implements Runnable {
private Thread thread;
private Socket socket;
private Scanner input;
private PrintWriter output;
public ClientThread(Socket s) throws IOException {
thread = new Thread(this);
socket = s;
socket.setTcpNoDelay(true);
//socket.setSoTimeout(10); //Send little chunk for 10 milliseconds.
input = new Scanner(socket.getInputStream());
output = new PrintWriter(socket.getOutputStream());
thread.start();
}
public void run() {
while (true) {
if (input.hasNext()) {
reciever(input.nextLine());
}
}
}
private void reciever(String data) {
ServerWindow.addText(data);
ServerWindow.server.broadcast(data, this);
}
public void broadcast(String data) throws IOException {
output.println(data);
output.flush();
}
}
It seems you are calling the broadcast method from the same thread.
This is a common pitfall for users new to multithreading in Java.
The fact that the broadcast method is in a subclass of Thread does not mean it will be executed on that Thread
In fact it will be executed on the thread that called it. The only method that will be executed on your created ClientThread is run() and anything that run() calls while it is executing. If you want said thread to not only read data from your connection but also write to it, you have to modify the run method to listen to external commands to start writing.
This question already has answers here:
Do Java sockets support full duplex?
(2 answers)
Closed 5 months ago.
I have a Java application which is Voip. I am using the one socket to send and receive information at the same time via threads. Code is shown below ..
Socket clientSocket = sockList.accept();
OutputStream outSock = clientSocket.getOutputStream();
InputStream inSock = clientSocket.getInputStream();
new Thread( new Capture(outSock)).start();
new Thread( new PlayAudio(inSock)).start();
outSock.close();
clientSocket.close();
The problem that I'm finding is that when I write to the outputstream, it blocks on the first write. I'm sending not many bytes. Bellow is my write code.
private class Capture implements Runnable{
private OutputStream out;
public Capture(OutputStream out){
this.out = out;
}
#Override
public void run() {
try{
int numBytesRead;
TargetDataLine outLine = getMic();
outLine.open();
outLine.start();
byte[] data = new byte[outLine.getBufferSize() / 5];
byte[] test = {0x1,0x1,0x1};
while(true) {
//numBytesRead = outLine.read(data, 0, data.length);
//System.out.println(numBytesRead);
out.write(test, 0, test.length);
out.flush();
/*if(numBytesRead > 0){
out.write(data, 0, data.length);
System.out.println("C");
}*/
}
}catch(Exception ex){}
}
}
The other thread that reads the sound code is ...
private class PlayAudio implements Runnable{
private InputStream in;
public PlayAudio(InputStream in){
this.in = in;
}
#Override
public void run() {
int write;
try{
SourceDataLine inLine = getSpeaker();
inLine.open();
inLine.start();
byte[] data = new byte[inLine.getBufferSize()];
byte[] test = new byte[3];
while(true){
System.out.println(1);
//write = in.read(data, 0, data.length);
in.read(test, 0 , test.length);
System.out.println(2);
/*if(write > 0){
inLine.write(data, 0, write);
System.out.println(3);
System.out.println(write);
}*/
}
} catch(Exception ex){}
}
}
I've commented a good portion of the actual code since I'm just trying to get it to work. My write function blocks indefinitely on the first write. Is it possible this could be a problem with my threads? My only thought is that the output and input streams are sharing my socket object which may cause a deadlock or something. Please let me know whats up.
Yes you can write to a sockets input and output stream at the same time.
from do-java-sockets-support-full-duplex
Since the input stream and the output stream are separate objects within the Socket, the only thing you might concern yourself with is, what happens if you had 2 threads trying to read or write (two threads, same input/output stream) at the same time? The read/write methods of the InputStream/OutputStream classes are not synchronized. It is possible, however, that if you're using a sub-class of InputStream/OutputStream, that the reading/writing methods you're calling are synchronized. You can check the javadoc for whatever class/methods you're calling, and find that out pretty quick.
Yes you can write on socket while reading , but you have to read socket in an independent thread. I am using this concept. Here the example is (read carefully it supports mutiple client as well ) :
public class TeacherServerSocket {
private Logger logger = Logger.getLogger(TeacherServerSocket.class);
public static Map<String, TeacherServerThread> connectedTeacher = new HashMap<String, TeacherServerThread>();
ServerSocket serverSocket;;
#Override
public void run() {
// starting teacher server socket
this.serverSocket = startServer();
// if unable to to start then serverSocket would have null value
if (null != this.serverSocket) {
while (true) {
//listening to client for infinite time
Socket socket = listenToClient();
if (null != socket) {
TeacherServerThread teacherServerThread = new TeacherServerThread(socket);
Thread thread = new Thread(teacherServerThread);
thread.start();
//putting teacher ip address and teacher object into map
connectedTeacher.put(teacherServerThread.getTeacherIp(),teacherServerThread);
System.out.println("INFO: Teacher is connected with address "+ teacherServerThread.getTeacherIp());
}
}
}
}
#Override
public ServerSocket startServer() {
//port number on which teacher server will be run.
int port=12345;
try {
// throw an exception if unable to bind at given port
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("Teacher server socket started on port no :"+port);
return serverSocket;
} catch (IOException e) {
logger.error("Unable to start Teacher Server socket");
e.printStackTrace();
}
return null;
}
#Override
public Socket listenToClient() {
if (this.serverSocket != null) {
try {
// throw an exception is unable to open socket
Socket socket = this.serverSocket.accept();
return socket;
} catch (IOException e) {
logger.error("Unable to open socket for teacher");
e.printStackTrace();
}
}
else {
logger.error("TeacherServerSocket has got null value please restart the server");
}
return null;
}
#Override
public Map getConnectedDevicesMap() {
return TeacherServerSocket.connectedTeacher;
}
/**
* This method will send message to connected teacher which comes form student
* #author rajeev
* #param message, which comes form student
* #return void
* * */
#Override
public void publishMessageToClient(String message) {
if(TeacherServerSocket.connectedTeacher.size()>0){
System.out.println("Total Connected Teacher: "+TeacherServerSocket.connectedTeacher.size());
for (String teacherIp : TeacherServerSocket.connectedTeacher.keySet()) {
TeacherServerThread teacherServerThread=TeacherServerSocket.connectedTeacher.get(teacherIp);
teacherServerThread.publishMessageToTeacher(message);
}
}
}
#Override
public void stopServer() {
if (this.serverSocket != null) {
try {
serverSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
To read in an in independent thread for multiple client :
public class TeacherServerThread implements Runnable {
Logger logger=Logger.getLogger(TeacherServerThread.class);
Socket socket;
String teacherIp;
public TeacherServerThread(Socket socket) {
this.socket=socket;
this.teacherIp=socket.getInetAddress().toString();
}
#Override
public void run() {
//starting reading
ReadFromTeacherAndPublishToStudent messageReader=new ReadFromTeacherAndPublishToStudent();
Thread thread=new Thread(messageReader);
thread.start();
}
private class ReadFromTeacherAndPublishToStudent implements Runnable {
#Override
public void run() {
String message=null;
try {
BufferedReader readTeacherData=new BufferedReader(new InputStreamReader(socket.getInputStream()));
StudentServerSocket studentServerSocket=new StudentServerSocket();
//sending message to student which is read by teacher
while((message=readTeacherData.readLine())!=null){
//System.out.println("Message found : "+message);
// studentServerSocket.publishMessageToClient(message); // do more stuff here
}
// if message has null value then it mean socket is disconnected.
System.out.println("INFO: Teacher with IP address : "+teacherIp+" is disconnected");
TeacherServerScoket.connectedTeacher.remove(getTeacherIp());
if(null!=socket){
socket.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} //class
public void publishMessageToTeacher(String message){
if(this.socket!=null){
try {
PrintWriter writeMessageToTeacher=new PrintWriter(this.socket.getOutputStream());
writeMessageToTeacher.println(message);
writeMessageToTeacher.flush();
System.out.println(" Message published to teacher"+message);
}catch(Exception e){
logger.error(e.toString());
logger.error("Exception In writing data to teacher");
}
}else {
logger.error("Unable to publish message to teacher .Socket has Null value in publishMessageToTeacher");
System.out.println("ERROR: socket has null value can not publish to teacher");
}
}
public String getTeacherIp()
{
return teacherIp;
}
}
change code according to you requirement......
The reason it seems my write() is blocking is because I stupidly closed the Socket() and my input streams didn't realize it. Hence, no data is ever sent out. Silly error on my behalf.