I'm making a M-JPEG Server in Android, and I have successfully implemented it, but I face a problem:
The first time I connect to it (Using a browser to connect), the stream is good and I get a real-time preview, but opening a new client (like, reloading the page or opening in a new tab), the stream gets slower and slower. Even if I close the previous client, it doesn't improve performance.
I have tried other M-JPEG Android Streamers (like myMobKit), it doesn't slow down.
Here is my Server code -
public class StreamServer implements Runnable {
public static Stack<byte[]> bufferStack;
...
public StreamServer(int port) {
...
bufferStack = new Stack<>();
bufferStack.setSize(100);
}
public void start() {
new Thread(this).start();
}
...
#Override
public void run() {
try {
serverSocket = new ServerSocket(port);
while (isRunning) {
Socket socket = serverSocket.accept();
new Thread(new StreamSocket(socket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void addBufferToStack(byte[] buffer) {
bufferStack.push(buffer);
}
}
...And the Socket implementation -
public class StreamSocket implements Runnable {
private Stack<byte[]> bufferStack = StreamServer.bufferStack;
...
StreamSocket(Socket socket) throws SocketException {
this.socket = socket;
this.socket.setTcpNoDelay(true);
this.socket.setKeepAlive(false);
...
}
#Override
public void run() {
if (!isStreaming) return;
PrintStream output = null;
try {
output = new PrintStream(socket.getOutputStream());
// Sent the initial header for M-JPEG.
...
// Start the loop for sending M-JPEGs
while (isStreaming && !socket.isClosed() && socket.isConnected()) {
try {
if (bufferStack.empty()) continue;
byte[] buffer = bufferStack.pop();
if (buffer == null) continue;
... (Some headers needed for M-JPEG streaming. Read it on Wikipedia)
output.write(buffer);
} catch (Exception e) {
e.printStackTrace();
}
}
output.flush();
output.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (output != null) output.close();
if (!socket.isClosed() || socket.isConnected()) socket.close();
isStreaming = false;
socket = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
The addBufferToStack(...) is called when a new JPEG is made (The camera's preview frame is taken, and processed into a JPG via native code).
I think the Socket Thread is not closed even after client is disconnected, although I am not sure. Please help in this, Thanks!
If any other information/code is needed, please tell, I will edit the question and add it.
EDIT -
The bufferStack is not empty when the server has started. The server is started after the camera + a few more things start.
Your loop will keep on looping even if the other side ends the connection:
(note my comments)
while (isStreaming && !socket.isClosed() && socket.isConnected()) {
try {
if (bufferStack.empty()) continue;
byte[] buffer = bufferStack.pop();
if (buffer == null) continue;
... (Some headers needed for M-JPEG streaming. Read it on Wikipedia)
output.write(buffer); // <-- will throw Exception if connection is broken
} catch (Exception e) {
e.printStackTrace(); // here you should also reset isStreaming
}
}
By catching the Exception (and not handling it actually), you dismiss the information, that the connection is gone.
Checking isConnected as loop condition is superfluent - it will always be true if the socket once has been connected. See https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html#isConnected-- :
Note: Closing a socket doesn't clear its connection state, which means this method will return true for a closed socket (see isClosed()) if it was successfuly connected prior to being closed.
I am currently not 100% sure about the behavior of isClosed but I strongly believe it will only turn true when you call close, not if an underlying stream throws an IOException.
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 a Socket that sends a list of Objects every few seconds to a client through ObjectOutputStream. On the server side, after every writeObject(myList) i execute flush then reset. Using VisualVM to check for memory usage, on the server there's no memory leaks, but on the client it seems that the previously read Lists are kept in memory. I tried to execute reset on the ObjectInputStream on the client side but looks like ObjectInputStream does not support this method (it throws a java.io.IOException: mark/reset not supported).
This is my server socket:
public class ConsultaBombas {
public static void inicializarServidorSocket() {
try {
ServerSocket serverSocket = new ServerSocket(5963);
Thread thread = new Thread(() -> {
while (!serverSocket.isClosed()) {
try {
final Socket socket = serverSocket.accept();
new ThreadComunicacao(socket).start();
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.setName("Consulta bombas (Inicializador)");
thread.start();
} catch (Exception e) {
e.printStackTrace();
}
}
static class ThreadComunicacao extends Thread {
private Socket socket;
public ThreadComunicacao(Socket socket) {
this.socket = socket;
setName("Consulta bombas (Comunicação) com início: " + new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(new Date()));
}
#Override
public void run() {
try {
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
while (!socket.isClosed()) {
List<Bomba> bombas = new DaoBomba().findAll();
out.writeObject(bombas);
out.flush();
out.reset();
Thread.sleep(1000);
}
} catch (SocketException e) {
if (e.getLocalizedMessage() != null && e.getLocalizedMessage().equalsIgnoreCase("Connection reset by peer: socket write error")) {
System.out.println("Cliente desconectou...");
} else {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
An this is the client (started with start() method):
public class ConsultaBombasClient {
private Socket socket;
private Thread threadConsulta;
public ConsultaBombasClient(BombasListener bombasListener, String maquinaDestino) {
threadConsulta = new Thread(() -> {
try {
Thread.currentThread().setName("Consulta Bombas");
System.out.println("Endereço bagual: "+maquinaDestino);
socket = new Socket(maquinaDestino, 5963);
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Object leitura;
while ((leitura = in.readObject()) != null) {
List<Bomba> bombas = (List<Bomba>) leitura;
bombasListener.run(bombas);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
});
threadConsulta.setDaemon(true);
}
public void start() {
threadConsulta.start();
}
public interface BombasListener {
void run(List<Bomba> bombas);
}
}
What am i doing wrong?
garbage collection is not immediate, do you have any real memory troubles? Have you tried running the client with low -Xmx value, did you receive the OutOfMemoryError?
– user3707125
You're right, after some time when the memory gets close to the maximum heap size, it clears the objects from memory. I wasn't seeing this because i have a lot of RAM in my pc but with Xmx50m i could see this working as you said. – Mateus Viccari
Clearly bombasListener.run(), whatever it may be, is not releasing the supplied list.
NB ObjectInputStream.readObject() does not return null at end of stream. It is therefore incorrect to use this test as a termination condition for a read loop.
I'm trying to do something potentially stupid, but I reckon it's a good idea, so bear with me. I tried to implement it, but I hit an awkward issue with sockets closing between threads - so I want some fresh eyes on the case.
Scenario
I want to write an object from a Client to a Server via sockets. There may be more than one Client communicating with the Server concurrently.
The object, a Message, is handled by the Server through its handling mechanisms. It is proposed that instead of the Server's main thread looking out for new incoming connections, a Listener thread is set up. Once it spots an incoming connection, it alerts the Server, storing the socket in a queue without receiving the data, so it can go back to listening quickly.
In its own time, the Server picks up the waiting socket, spawns a new thread, reads the Message, and closes the socket.
The code
Here's my first thoughts on how this should be implemented. There is a fundamental flaw in it which I will explain below.
Ignore the use of public fields - I'm just trying to make the code short for you guys
public class Server {
public boolean messageWaiting = false;
public static void main(String[] args) {
new Server().run();
}
public void run() {
Listener l = new Listener();
l.listen(this);
try {
while (true) {
System.out.println("I'm happily doing my business!");
Thread.sleep(1000);
if (messageWaiting) {
acceptMessages(l);
}
}
} catch (InterruptedException die) {}
}
private void acceptMessages(Listener l) {
while (!l.waiting.isEmpty()) {
try (
Socket client = l.waiting.poll();
ObjectInputStream ois = new ObjectInputStream(client.getInputStream())
) {
// Handle messages in new threads! (or a thread pool)
new Thread() {
public void run() {
try {
System.out.println(ois.readObject());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}.start();
} catch (Exception ex) {
// Oh no! The socket has already been closed!
ex.printStackTrace();
}
}
}
}
public class Listener {
public ConcurrentLinkedQueue<Socket> waiting = new ConcurrentLinkedQueue<>();
public void listen(final Server callback) {
new Thread() {
public void run() {
try (ServerSocket rxSock = new ServerSocket(7500)) {
while (!isInterrupted()) {
try (Socket client = rxSock.accept()) {
// Once a new socket arrives, add it to the waiting queue
waiting.add(client);
// Alert the server
callback.messageWaiting = true;
} catch (IOException ex) {
ex.printStackTrace();
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}.start();
}
}
public class Client {
public static void main(String[] args) {
try (
Socket txSock = new Socket(InetAddress.getLoopbackAddress(), 7500);
ObjectOutputStream oos = new ObjectOutputStream(txSock.getOutputStream())
) {
oos.writeObject("This is a Message, trust me.");
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
What's wrong with this?
This:
I'm happily doing my business!
I'm happily doing my business!
java.net.SocketException: Socket is closed
at java.net.Socket.getInputStream(Unknown Source)
at Server.acceptMessages(Server.java:30)
at Server.run(Server.java:20)
at Server.main(Server.java:9)
This is because the Java 7 try blocks I'm using close the sockets once they're finished. So why don't I do this manually? Try yourself - you end up with a warning saying you're only ever going to call close() on a null object!
So, how do I avoid the whole issue of my incoming socket being closed before the Server thread picks up on it? Or is this a bad idea anyway and I should do something else?
Your statement in Listener
try (Socket client = rxSock.accept()) { ...
Is a try-with-resources for the client socket. As soon as you add it to the queue and exit the try block, the socket gets auto-closed.
I was unable to find something similar to my application so I thought I would ask a new question. I am new to developing android (and java in general) but have some prior programming experience in C and Visual Basic. I am using a JPEG TTL Camera (Link Sprite LS Y201) and taking a picture to send from a TCP server to an android client device. On the client side, I am using an Async task to continuously get the data from the socket. So far, I have been able to get some bytes and store them in an array. Here are my questions:
1. The amount of data coming in from the socket is unknown. How to account for that?
2. How to check if all the data was read? The JPEG image data starts at hex value 0xFFD8 and ending value is 0xFFD9.
3. How to update this data to an imageview?
Thank you as well for taking the time to look this over. I really appreciate any help I can get!
The code I currently have is below:
// ----------------------- THE NETWORK TASK - begin ----------------------------
public class NetworkTask extends AsyncTask<Void, byte[], Boolean> {
Socket nsocket; //Network Socket
InputStream nis; //Network Input Stream
OutputStream nos; //Network Output Stream
BufferedReader inFromServer;//Buffered reader to store the incoming bytes
#Override
protected void onPreExecute() {
//change the connection status to "connected" when the task is started
changeConnectionStatus(true);
}
#Override
protected Boolean doInBackground(Void... params) { //This runs on a different thread
boolean result = false;
try {
//create a new socket instance
SocketAddress sockaddr = new InetSocketAddress("192.168.1.115",5050);
nsocket = new Socket();
nsocket.connect(sockaddr, 5000);//connect and set a 10 second connection timeout
if (nsocket.isConnected()) {//when connected
nis = nsocket.getInputStream();//get input
nos = nsocket.getOutputStream();//and output stream from the socket
BufferedInputStream inFromServer = new BufferedInputStream(nis);//"attach the inputstreamreader"
while(true){//while connected
ByteArrayBuffer baf = new ByteArrayBuffer(256);
int msgFromServer = 0;
while((msgFromServer = inFromServer.read()) != -1){;//read the lines coming from the socket
baf.append((byte) msgFromServer);
byte[] ImageArray = baf.toByteArray();
publishProgress(ImageArray);//update the publishProgress
}
}
}
//catch exceptions
} catch (IOException e) {
e.printStackTrace();
result = true;
} catch (Exception e) {
e.printStackTrace();
result = true;
} finally {
closeSocket();
}
return result;
}
//Method closes the socket
public void closeSocket(){
try {
nis.close();
nos.close();
nsocket.close();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//Method tries to send Strings over the socket connection
public void SendDataToNetwork(String cmd) { //You run this from the main thread.
try {
if (nsocket.isConnected()) {
nos.write(cmd.getBytes());
nos.flush();
} else {
outputText("SendDataToNetwork: Cannot send message. Socket is closed");
}
} catch (Exception e) {
outputText("SendDataToNetwork: Message send failed. Caught an exception");
}
}
//Methods is called every time a new byte is received from the socket connection
#Override
protected void onProgressUpdate(byte[]... values) {
if (values.length > 0) {//if the received data is at least one byte
if(values[85]== 255 ){//Start of image is at the 85th byte
///This is where I get lost. How to start updating imageview with JPEG bytes?
}
}
}
//Method is called when task is cancelled
#Override
protected void onCancelled() {
changeConnectionStatus(false);//change the connection to "disconnected"
}
//Method is called after taskexecution
#Override
protected void onPostExecute(Boolean result) {
if (result) {
outputText("onPostExecute: Completed with an Error.");
} else {
outputText("onPostExecute: Completed.");
}
changeConnectionStatus(false);//change connectionstaus to disconnected
}
}
// ----------------------- THE NETWORK TASK - end ----------------------------
On the server side, before sending each image, you could send an integer with the size of the image you're abut to transfer.
The available() function can be used for that. inFromServer.available() == 0 will tell you whether all data was read.
The following code will do it:
byte[] ImageArray = baf.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(ImageArray, 0, ImageArray.length);
imageView.setImageBitmap(bitmap);
Hope you find this useful! Good luck!
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.