Consider this (simplified) piece of code:
public class Test {
// assigned elsewhere
InetSocketAddress socketAddress;
String socketHost;
int socketPort;
Socket socket;
int COMMAND = 10;
int CONNECTION_TIMEOUT = 10 * 1000;
int SOCKET_TIMEOUT = 30 * 1000;
DataOutputStream dos;
DataInputStream dis;
protected void connect() throws IOException, InterruptedException {
socket.connect(socketAddress != null ? socketAddress : new InetSocketAddress(socketHost, socketPort), CONNECTION_TIMEOUT);
socket.setSoTimeout(SOCKET_TIMEOUT);
socket.setTcpNoDelay(true);
}
void initializeDataStreams() throws IOException {
dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream(), socket.getSendBufferSize()));
dis = new DataInputStream( new BufferedInputStream( socket.getInputStream(), socket.getReceiveBufferSize()));
}
void run() {
try {
connect();
initializeDataStreams();
sendCommand(COMMAND, true);
sendIdAndUsername(true);
sendSyncPreference(true);
sendBlockedIds(true);
sendHeaders();
// reading from 'dis' here
// ...
} catch (InterruptedException | IOException e){
/* ... */
}
}
void sendCommand(int command, boolean buffered) throws IOException {
dos.write(command);
if (!buffered) {
dos.flush();
}
}
void sendIdAndUsername(boolean buffered) throws IOException {
sendId(true); // always buffered
String username = "user name";
dos.writeBoolean(username != null);
if (username != null) {
dos.writeUTF(username);
}
if (!buffered) {
dos.flush();
}
}
void sendId(boolean buffered) throws IOException {
dos.writeUTF("user id");
if (!buffered) {
dos.flush();
}
}
void sendSyncPreference(boolean buffered) throws IOException {
boolean fullSync = true;
dos.writeBoolean(fullSync);
if (!buffered) {
dos.flush();
}
}
void sendBlockedIds(boolean buffered) throws IOException {
Set<String> blockedCrocoIds = new HashSet<>();
ObjectOutputStream oos = new ObjectOutputStream(dos);
oos.writeObject(blockedCrocoIds);
if (!buffered) {
oos.flush();
}
}
private void sendHeaders() throws IOException {
dos.writeUTF("some string");
dos.writeInt(123);
// some other writes...
// this should flush everything, right?
dos.flush();
}
}
I left it intentionally with all the methods, just in case I've made some terribly obvious mistake there. When I execute Test.run(), sometimes (really hard to predict when exactly) it seems like the flush() in sendHeaders() doesn't work at all.
Server side doesn't receive anything on its ServerSocket.accept() for next 22 seconds (don't ask me where this number comes from, part of the mystery).
The idea was that I wont call flush() on every transmission but call it only once, to save the bandwidth.
So what's wrong with this code? How to ensure writes to my stream are reliable / immediate so the server can read it ASAP?
I also accept answer "there's nothing wrong", in that case it must be something which is being done in parallel and affecting the network stack on Android.
EDIT: Server code is really nothing special:
ListeningThread listeningThread = new ListeningThread();
listeningThread.start();
listeningThread.join();
and then:
public class ListeningThread extends Thread {
private ServerSocket serverSocket;
public ListeningThread() {
try {
// unbound server socket
serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);
serverSocket.bind(new InetSocketAddress(NetworkUtil.APP_SERVER_PORT));
} catch (IOException e) {
log(e);
}
}
#Override
public void run() {
log("run");
while (serverSocket.isBound() && !isInterrupted()) {
try {
Socket socket = serverSocket.accept();
new CommandThread(socket).start();
} catch (IOException e) {
log(e);
}
}
try {
serverSocket.close();
} catch (IOException e) {
log(e);
}
}
}
and finally:
public class CommandThread extends Thread {
private final Socket socket;
public CommandThread(Socket socket) {
log("CommandThread");
this.socket = socket;
}
#Override
public void run() {
log("run");
try {
socket.setSoTimeout(NetworkUtil.SOCKET_TIMEOUT);
socket.setTcpNoDelay(true);
InputStream is = socket.getInputStream();
int cmd = is.read(); // <========= so actually this is failing
switch (cmd) {
// handling of the command
case COMMAND:
new DownloadMessagesThread(socket).start();
break;
}
} catch (IOException | SQLException e) {
log(e);
}
}
}
As mentioned in the comments, I'd open to agree on anything wrong with the object streams & co but the trouble is that I'm unable to reach (again, it's just sometimes, it's very random...) CommandThread's run(). So unless I'm missing something else, there's no way Object Streams could cause this kind of failure.
EDIT 2: Correction: it's not accept() I cannot reach, it's the first read operation:
03-07 11:22:42.965 00010 CommandThread: CommandThread
03-07 11:22:42.966 00108 CommandThread: run
[... nothing happening ...]
03-07 11:23:04.549 00111 DownloadMessagesThread: run
Could this be caused by mixing the object stream and data stream after all?
You should verify that the ObjectOutputStream creation in sendBlockedIds is not the culprit.
I've already had some protocol "deadlocks" while mixing DataStreams and ObjectStreams, since the creation of the Writer/Reader pair of ObjectStreams implies a kind of handshake that may fail while mixing those streams.
EDIT: While reading again your question, I realized that I had not answered it. So yes, it is reliable. And +1 for EJP answer.
To answer the question in your title, it is 100% reliable, as it doesn't do anything. Only the flush() methods of streams that are buffered actually do anything, and that only includes ObjectOutputStream and BufferedOutputStream, and PrintStream depending on how you construct it. Not DataOutputStream, and not the output stream of the socket itself.
So in this case the only flush method that does anything is the buffered output stream's, and you can certainly rely on that, as it is just code, and has been working for twenty years.
If this is affecting the speed of accept(), there must be something odd about your accept loop that you haven't shown us: typically, doing I/O in the accept loop instead of in the started thread.
And you should certainly not create an ObjectOutputStream in the middle of the connection. Create it at the start and use it for everything, and an ObjectInputStream at the other end.
NB setting the buffer sizes to the socket buffer sizes respectively is really fairly pointless. The defaults are adequate.
Related
I've a small android library which handles a serial port, it has basic functionality like open, read, write and close.
I have made an applications that uses this library to write on the serial port and read the responses, within this application there is a thread that periodically opens the serial port asks for the status get the response and close the serial port.
I want to protect the serial communication in a way that if the main thread opens the communication the secondary thread that only checks the status can not open it and wait for the main thread to finish.
class SerialChannel extends Channel
{
private SerialPortUtility serialPortUtility;
private static final String SERIAL_FILE = "/dev/ttyMT2";
private static final String CONTROL_FILE = "/sys/devices/platform/file";
private static final String UNKNOWN_COMMAND = "UNKNOWN COMMAND";
private FileOutputStream fileOutputStream;
private FileInputStream fileInputStream;
#Override
public void open() throws CommunicationException
{
try
{
if (isSerialOpened() != SerialStatus.Open)
{
toggleSerial(SerialStatus.Open.getStatus());
Thread.sleep(100);
}
serialPortUtility = getSerialPortUtility();
fileInputStream = (FileInputStream) serialPortUtility.getInputStream();
fileOutputStream = (FileOutputStream) serialPortUtility.getOutputStream();
currentProcess = Optional.of(Thread.currentThread().getId());
Thread.sleep(500);
}
catch (IOException | InterruptedException e)
{
throw new CommunicationException(e.getMessage());
}
}
#Override
public void close() throws CommunicationException
{
if (serialPortUtility == null)
{
throw new CommunicationException("SerialPort is null");
}
try
{
toggleSerial(SerialStatus.Close.getStatus());
fileOutputStream.close();
fileInputStream.close();
serialPortUtility.close();
fileInputStream = null;
fileOutputStream = null;
serialPortUtility = null;
}
catch (IOException e)
{
throw new CommunicationException(e.getMessage());
}
}
#Override
public void send(byte[] buffer, int timeout, int length) throws CommunicationException
{
if (fileOutputStream == null)
{
throw new CommunicationException("Problem while sending data!");
}
try
{
fileOutputStream.write(buffer);
fileOutputStream.flush();
}
catch (IOException e)
{
throw new CommunicationException(e.getMessage());
}
}
#Override
public byte[] receive(int length, int timeout) throws CommunicationException
{
StringBuilder stringBuilder = new StringBuilder();
byte[] buffer = new byte[length];
int ret;
int totalSize = 0;
if (fileInputStream == null)
{
throw new CommunicationException("FileInputStream is null!");
}
try
{
long millisStart = Calendar.getInstance().getTimeInMillis();
boolean timeoutReached;
while (true)
{
timeoutReached = (Calendar.getInstance().getTimeInMillis() - millisStart > timeout * 1000);
if (fileInputStream.available() <= 0 && timeoutReached)
{
expectingResult = false;
throw new CommunicationException("Error");
}
else if (fileInputStream.available() > 0)
{
break;
}
}
millisStart = Calendar.getInstance().getTimeInMillis();
while (totalSize != length && (ret = fileInputStream.read(buffer)) != -1)
{
String received = new String(buffer);
stringBuilder.append(received);
if(buffer.length == 15 && received.equals(UNKNOWN_COMMAND))
{
break;
}
totalSize += ret;
}
expectingResult = false;
}
catch (IOException e)
{
throw new CommunicationException(e.getMessage());
}
return stringBuilder.toString().getBytes();
}
private SerialPortUtility getSerialPortUtility() throws IOException
{
if (serialPortUtility == null)
{
File file = new File(SERIAL_FILE);
int baudRate = 115200;
return new SerialPortUtility(file, baudRate, 0);
}
return serialPortUtility;
}
private void toggleSerial(String data) throws IOException
{
FileOutputStream fos = new FileOutputStream(new File(CONTROL_FILE));
fos.write(data.getBytes());
fos.flush();
fos.close();
}
private SerialStatus isSerialOpened() throws IOException
{
byte[] buffer = new byte[1];
FileInputStream fis = new FileInputStream(new File(CONTROL_FILE));
int result = fis.read(buffer);
fis.close();
if (result > -1 && buffer[0] == 1)
{
return SerialStatus.Open;
}
return SerialStatus.Close;
}
}
This class extends custom class Channel that implements an interface with the methods open, close, read, send and implements also AutoCloseable.
Now if I make the open method synchronized any thread that enters here will lock, but will lock until it exits the open method, and when the thread moves to the another method let's say read and stay there until it gets a response, the checker thread will come and enters the open method. Using AutoCloseable the close method will execute and close the serial port communication. If I synchronize an object, there still is a window when the object is not synchronized.
How can I tell the checker thread that the communication is already opened and make him wait until the main thread finish.
Checker looks like this, it is within an timer:
try(Channel ch = CommunicationFactory.getInstance().selectChannel(CommunicationType.SERIAL))
{
ch.open();
//do stuff
}
catch (CommunicationException ex)
{
ex.printStackTrace();
}
The "main" thread looks the same only that it is in an AysncTask.
If additional informations are required please let me know!
Thank you in advance for your effort and time!
How can I tell the checker thread that the communication is already opened and make him wait until the main thread finish.
I don't fully understand your code but the critical thing with threads and locking is to make sure that all threads are calling code that is synchronized on the same object instance.
If I synchronize an object, there still is a window when the object is not synchronized.
Not if you use the same instance of the object. Making each of the public methods in SerialChannel synchronized will make sure that only 1 thread can be using the object at once.
I suspect that your real problem is not about protecting the SerialChannel object but more about race-conditions between the threads. They need to make multiple calls to the methods and they can block each other or interleave in an improper manner.
You can get around this with a couple of changes. You can make the send(...) and receive(...) methods auto-opening. Threads would just call send() or receive() which in turn would internally call open() if the fileInputStream or fileOutputStream was null. The thread would be inside of a synchronized so this would not be interrupted by another thread.
Another completely different model to consider would be to have one thread reading from the serial port and another writing to it that are dedicated to that task -- they would be built into the SerialChannel object. They would share data with the external threads using a read BlockingQueue and a write BlockingQueue. Then the serial port is opened early in your application which starts the IO threads and the external threads never worry about the IO. They just put() and take() from the queues. I typically do this (for example) when reading and writing to the console.
Hope something here helps.
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.
I have a small problem. I have trying to use a method in another class to send an object to the server I have. I am using Java with Sockets.
Method:
public void sendMessageToServer(String message) {
if (message != null) {
try {
serverComManager.outputStream.writeObject(message);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Class trying to use the method:
sendMessage.sendMessageToServer("Hello");
The error is thrown at the line:
serverComManager.outputStream.writeObject(message);
Thank you in advance!
~Rane
EDIT: As requested, I have added the 'serverComManager' declaration as well as the code for that class. I have also included the full error. I hope this helps you understand my problem.
Declaration:
ServerCommunicationManager serverComManager = new ServerCommunicationManager();
Code for ServerCommunicationManager:
boolean connected;
//Setup
Socket clientSocket;
ObjectOutputStream outputStream;
ObjectInputStream inputStream;
public boolean connectToHost() throws UnknownHostException, IOException{
clientSocket = new Socket("localhost", 2444);
setupStreams(clientSocket);
if(clientSocket.isConnected()){
connected = true;
}else{
connected = false;
}
return connected;
}
private void setupStreams(Socket s) throws IOException{
outputStream = new ObjectOutputStream(s.getOutputStream());
inputStream = new ObjectInputStream(s.getInputStream());
}
Error:
Exception java.lang.NullPointerException
at SendToServer.sendMessageToServer(SendToServer.java:16)
at DissconnectClient.dissconnectFromServer(DissconnectClient.java:15)
Error Lines:
DissconnectClient 15: sendMessage.sendMessageToServer(abortConnectionKeyword);
SendToServer 16: serverComManager.outputStream.writeObject(message);
NOTE: DisconnectClient is one of the classes I am writing with. Here is the class code:
public class DissconnectClient {
//Variables
private final String keyword = "DISSCONNECT";
//Setup
SendToServer sendMessage = new SendToServer();
public void dissconnectFromServer(){
sendMessage.sendMessageToServer(keyword);
}
}
I cannot see where do you assign a value of "serverComManager" or where do you create an isntance of this. Maybe in a constructor method ot the class which has the method "sendMessageToServer" you're doing something like this.serverComManager = (...). I'm not sure how you are handle the logic of "serverComManager" but so far, my approach to solve the issue would be the following (if I'm writing a client that sends a message to the server). And considering there's no code provided for your "serverConnManager", maybe you could identify something missing in your current implementation.
public void sendMessageToServer(String message) {
if (message != null) {
try {
//Assume I already have an instance of client Socket:
//Socket outgoingConn = new Socket(host, port)
//1. I get the OutputStream of my outgoing connection
OutputStream outStream = outgoingConn.getOutputStream();
//2. Create an object output stream
ObjectOutputStream objectWriter = new ObjectOutputStream(outStream);
//3. Write the object
objectWriter.writeObject(message);
//Close the io if required (would recommend try-with-resources if using jdk7)
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
If you are working just with String messages, BufferedWriter orBufferedReadr would be enough, if you try to handle complex objects that can be both "beans" or Strings, better if you create something like:
public class MessageBean implements Serializable {
private MessageType type;
private Object param;
//Getters/Setters
}
MessageType could be an enum specifying the type of objects supported, since param field is an object you can handle as an String or as a bean. Then work based on the MessageType or using the "instanceof". But Well, this is just a suggestion if you want to try something further.
Hope it helps. Happy coding!
Regards.
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.
I've written a simple application in Java where there are two nodes, each with a ServerSocket open to a port listening for incoming connections. The nodes run two threads each, sending 1000 messages to the other node through a persistent TCP socket created when sending the first message. However, the nodes do not receive all 1000 messages. One may receive 850 while the other only receives 650. This number tends to stay constant over multiple runs.
The sending code is as follows:
public void SendMsg(String dest, Message myMsg) {
Socket sendsock = null;
PrintWriter printwr = null;
try {
if(printwr == null) {
sendsock = new Socket(dest, Main.rcvport);
printwr = new PrintWriter(sendsock.getOutputStream(), true);
}
String msgtosend = myMsg.msgtype.toString() + "=" + Main.myaddy + "=" + myMsg.content + "\n";
printwr.print(msgtosend);
} catch (UnknownHostException ex) {
System.out.println(ex);
//DO: Terminate or restart
} catch (IOException ex) {
System.out.println(ex);
//DO: Terminate or restart
}
}
Performance seems to improve if I use
buffwr = new BufferedWriter(printwr)
as well and use buffwr.write(...) instead of printwr.print(...), though it doesn't seem to be a complete solution for the data loss. There are no exceptions to show that packets weren't delivered, so according to the sender, they were all sent successfully.
On the receiving end, the accepted connection is treated as follows:
BufferedReader inbuff = new BufferedReader(new InputStreamReader(incoming.getInputStream()));
while(running) {
String rcvedln = inbuff.readLine();
if(rcvedln != null) {
count++;
System.out.println(count);
}
}
Is there an problem with how the readers and writers have been used that could be causing the problem? Thanks.
SendMsg() is creating a new socket every call, so you aren't using a persistent TCP connection. The method isn't closing the socket, either, so you have a lot of open collections. You may be reaching a limit to the number of connections the process can make (the sockets may not be closed when the objects are garbage collected).
Finally, as kd304 pointed out, the Javadoc for PrintWriter states this about the autoFlush parameter of the PrintWriter constructor: "if true, the println, printf, or format methods will flush the output buffer". Your code wasn't calling a method that did a flush.
Try this:
public class MessageSender implements Closeable {
private final Socket socket;
private final PrintWriter writer;
public MessageSender(String dest, int port) {
socket = new Socket(dest, port);
writer = new PrintWriter(socket.getOutputStream(), true);
}
public void sendMessage(Message message) {
try {
writer.println(message.toString());
} catch (UnknownHostException ex) {
System.out.println(ex);
//DO: Terminate or restart
} catch (IOException ex) {
System.out.println(ex);
//DO: Terminate or restart
}
}
#Override
public void close() throws IOException {
writer.close();
socket.close();
}
Note I modified the code so that sendMessage() calls Message.toString() to get the formatted message. It doesn't seem right for sendMessage() to reference fields in Message in order to format the message. Instead of using toString() you could create a method in Message specifically for this purpose.
Here's the server side code:
public class Server implements Runnable {
private final ServerSocket serverSocket;
private final ExecutorService executor;
private volatile boolean running = true;
public Server(int port, ExecutorService executor) throws IOException {
serverSocket = new ServerSocket(port);
this.executor = executor;
}
#Override
public void run() throws IOExeption {
while (running) {
Socket socket = serverSocket.accept();
executor.execute(new ConnectionHandler(socket));
}
}
public boolean stop(long timeout, TimeUnit unit) {
running = false;
executor.shutdown();
return executor.awaitTermination(timeout, unit);
}
}
You can use Executors to create an ExecutorService to run the tasks. Note that ConnectionHandler needs to close the socket it is given.
Are you closing out the PrintWriter to flush the stream?
} finally {
printwr.close();
sendsock.close();
}
Ah, sorry. I accidentally removed the commenting from the code. It's actually like this:
public void SendMsg(String dest, Message myMsg) {
Socket sendsock = null;
try {
if(printwr == null) {
sendsock = new Socket(dest, Main.rcvport);
printwr = new PrintWriter(sendsock.getOutputStream(), true);
}
String msgtosend = myMsg.msgtype.toString() + "=" + Main.myaddy + "=" + myMsg.content + "\n";
printwr.print(msgtosend);
} catch (UnknownHostException ex) {
System.out.println(ex);
//DO: Terminate or restart
} catch (IOException ex) {
System.out.println(ex);
//DO: Terminate or restart
}
}
printrw is declared and stored outside the function, so once it's set up, there is no need for sendsock or for reinitializing printrw. In the actual application, I'm storing the PrintWriter for every connection in a HashMap and retrieving it at the start of the SendMsg(...) function.
Since the connections are persistent, every time one is accepted, a new thread is lunch that runs a while loop to check it continuously for data. These threads and connections are only closed once the application is terminated. In addition to my previous question, is there a more efficient way of doing this?
Earlier, I'd implemented this code without the "\n" and using println(...) instead and I still had the issue of some messages not being received, so I'm not sure what is causing the problem. The messages are sent like so:
public class SendPortal2 implements Runnable {
String dest = null;
SendPortal2 (String dest) {
this.dest = dest;
}
public void run() {
for(int i=1; i<1000; i+=2) {
Message myMsg = new Message("Message", Main.myaddy + " " + String.valueOf(i));
Main.myCommMgr.SendMsg(dest, myMsg);
}
}
}
There are two such threads running. When I ran the code again just now, one side got 999 packets whereas the other one only got 500, leading me to believe sometimes the data from an entire thread could be blocked out. Is that likely?
Thanks for the replies!
If I put a Thread.sleep(2) inside the for loop where the SendMsg function is called, more messages are received properly, but it's not always 1000. Could it be possible that the system's resources are being hogged by two threads running while loops continuously?