Can't read socket InputStream on Jelly Bean - java

I have a TCP socket connection which works well on Android 2.3 but now facing some problems on Android 4.1.
The problem is that InputStream.read() method always returns -1 (without blocking), like the connection is closed.
Creating socket:
SocketFactory socketFactory = SocketFactory.getDefault();
Socket socket = socketFactory.createSocket("c.whatsapp.net", 5222);
socket.setSoTimeout(3*60*1000);
socket.setTcpNoDelay(true);
Retrieving input and output streams and writing some initial data:
InputStream inputStream = new BufferedInputStream(socket.getInputStream());
OutputStream outputStream = new BufferedOutputStream(socket.getOutputStream());
outputStream.write(87);
outputStream.write(65);
outputStream.write(1);
outputStream.write(2);
outputStream.flush();
Then, this condition always passes without blocking:
int c = inputStream.read();
if (c < 0) {
Log.d(TAG, "End of stream");
}
This code is running in a background thread. And it was working on Gingerbread.
Tried to use InputStreamReader and OutputStreamWriter instead of direct streams - no effect.

I have seen that very same error before, although this answer might look offtopic give it a chance and let me know if it worked, for some reason sockets are having strange behavior on jellybean even when they were working completely fine in lower android versions, the way I fixed this issue was to move the targetSdkVersion to jelly bean as well as the Project Build Target under Android properties of the project, didn't modify one single line of code, just that, and for some reason it does the trick...
Hope this helps.
Regards!

I had some similar issue where the inputStream.read() returned -1 and I did not get any Exception. In fact the server was down and the connection broken. I didn't test it with different versions, only with 4.0.
Here's the Google Bug Report about this behavior.
Unfortunately status of the bug seems to be 'closed' as not reproduceable.
My work around was to interpret the -1 as a close of the socket and an unreachable server. When you try to reconnect, you get the right errors.

I have had a similar problem and fixed it with a workaround like this
private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
private static class WatchDog implements Runnable{
private Thread thread = Thread.currentThread();
public void run() {
Log.d(LOG_TAG, "Interrupting read due to timeout");
thread.interrupt();
}
}
private void read(InputStream in, ByteBuffer bb, long waitTime) throws IOException {
int startingPos = bb.position();
long timeout = System.currentTimeMillis() + RESPONSE_TIMEOUT;
ScheduledFuture<?> watchdogFuture = executor.schedule(new WatchDog(), RESPONSE_TIMEOUT, TimeUnit.MILLISECONDS);
try {
while(System.currentTimeMillis() < timeout && bb.hasRemaining()){ //workaround fixing timeout after 1ms
try{
int read = in.read(bb.array(), bb.position(), bb.remaining());
if(read > 0){
bb.position(bb.position()+read);
}
} catch(SocketTimeoutException e){}
if(bb.hasRemaining()){
Thread.sleep(5);
}
}
watchdogFuture.cancel(true);
} catch (InterruptedException e) {}
if(bb.hasRemaining()){
throw new SocketTimeoutException("Unable to read requested bytes: "
+ (bb.position()-startingPos) + "/" + (bb.limit()-startingPos)
+ " after " + (System.currentTimeMillis() - timeout + RESPONSE_TIMEOUT) + "ms");
}
}

Using BufferedReader and PrintWriter works on all versions for me and is extremely convenient for sending and receiving anything you want (even JSON strings) via any communication protocol. Try saving them as member variables when starting your background thread like this:
mInput = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
mOutput = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
For asynchronous communication your background thread might then look like this:
#Override
public final void run() {
while (!Thread.currentThread().isInterrupted()) {
if (mInput == null) {
break;
}
String message = null;
try {
message = mInput.readLine();
} catch (IOException e) {
// handle the exception as you like
break;
}
if (Thread.currentThread().isInterrupted()) {
// thread was interrupted while reading
break;
} else if (message != null) {
// handle the message as you like
}
}
}
Use another background thread to send messages:
#Override
public void run() {
if (mOutput != null) {
mOutput.println(<message to be );
if (mOutput == null) {
// the above thread was interrupted while writing
} else if (!mOutput.checkError()) {
// everything went fine
} else {
// handle the exception
}
}
}
Also, you will have to close the streams from outside to make sure readLine doesn't block forever:
try {
mOutput.close();
mInput.close();
mOutput = null;
mInput = null;
} catch (IOException e) {
// log the exception
}
Now, since you're using TCP sockets it may happen that the socket is actually dead and readLine is still blocking. You have to detect that and close the streams just like above. For that, you will have to add another thread (oh well) that periodically sends keep-alive-messages. If no message was received from the remote device for X seconds, it has to close the streams.
This whole approach makes sure the socket is closed and all threads finish at all circumstances. Of course you can make the communication synchronous, if that is what you need, by removing the sender-thread and including println() inside the reader-thread instead. I hope that helps you (even though the answer comes 8 months late).

Friend,
try inputStream.readLine(); (i.e) DataInputStream.readLine(); (Deprecated method)
this worked for me...

Try this code -
Runnable runnable = new Runnable() {
#Override
public void run() {
synchronized (this) {
Socket s = null;
String inMsg = null, msg2 = null;
try {
try {
s = new Socket(server, port);
} catch (Exception e) {
return;
}
BufferedReader in = new BufferedReader(
new InputStreamReader(s.getInputStream()));
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
try {
inMsg = in.readLine()
+ System.getProperty("line.separator");
} catch (Exception e) {
return;
}
out.write(message + "\n\r");
out.flush();
try {
msg2 = in.readLine();
if (msg2 == null) {
return;
}
} catch (Exception e) {
return;
}
out.close();
s.close();
} catch (Exception e) {
return;
}
}
}
};
It works for me.

You should use Apache Commons IO: http://commons.apache.org/proper/commons-io/
See IOUtils.copy() http://commons.apache.org/proper/commons-io/javadocs/api-release/index.html?org/apache/commons/io/package-summary.html

Related

ThreadPoolExecutor.shutdownNow() not throwing InterruptedException in Thread

I am implementing a Transfer Server program which takes messages from clients (via console input) and then forwards it to some sort of mailbox.
To allow concurrent reception of several messages by different clients, I first created a class that implements the Runnable interface. Each of this class instances will handle the communication with exactly one client:
public class ClientConnection implements Runnable {
//...
//...
#Override
public void run() {
try {
// prepare the input reader and output writer
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true);
Message message = new Message();
String request = "";
// read client requests
while ((request = reader.readLine()) != null) {
System.out.println("Client sent the following request: " + request);
String response;
if (request.trim().equals("quit")) {
writer.println("ok bye");
return;
}
response = message.parseRequest(request);
if (message.isCompleted()) {
messagesQueue.put(message);
message = new Message();
}
writer.println(response);
}
} catch (SocketException e) {
System.out.println("ClientConnection: SocketException while handling socket: " + e.getMessage());
} catch (IOException e) {
throw new UncheckedIOException(e);
} catch (InterruptedException e) {
System.out.println("Client Connection was interrupted!");
e.printStackTrace();
} finally {
if (clientSocket != null && !clientSocket.isClosed()) {
try {
clientSocket.close();
} catch (IOException ignored) {}
}
}
}
}
I do have a parent thread which is responsible for starting and managing all the ClientConnection runnables:
#Override
public void run() {
clientConnectionExecutor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
while (true) {
Socket clientSocket;
try {
// wait for a Client to connect
clientSocket = serverSocket.accept();
ClientConnection clientConnection = new ClientConnection(clientSocket, messagesQueue);
clientConnectionExecutor.execute(clientConnection);
} catch (IOException e) {
// when this exception occurs, it means that we want to shut down everything
clientConnectionExecutor.shutdownNow(); // force terminate all ClientConnections
return;
}
}
}
Now according to this Stackoverflow Question, I would have expected that as soon as shutdownNow(); is being called, an InterruptedException would be thrown within my ClientConnection.run() method, and there, it should print Client Connection was interrupted!. But this does not happen, so the catch clause seems never to be reached, the input reading loop just goes on.
I read in another Stackoverflow question that this might be related to some other codeline within the block seems to be consuming the InterruptedException, but there wasn't any particular information on what codeline could do that. So I am thankful for any hints.
Edit: It turns out that as soon as I manually exit the loop by typing "quit" on the client, the loop will quit and then, Client Connection was interrupted! will be printed. So somehow the exception seems to be ignored as long as the loop is running, and only handled afterwards.
From Oracle docs for shutdownNow:
There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. For example, typical implementations will cancel via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate.
If you take a look into ThreadPoolExecutor sources, you will find out that shutdownNow interrupts threads with this code:
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
Your ClientConnection doesn't check the flag Thread.interrupted. Due to information in the post, I can't figure out which method throws InterruptedException. Probably, some other method, for example, readLine of reader or writer, blocks the thread, because they use socket's InputStream and OutputStream and because it's obvious that socket's streams block the thread if data is not immediatly available.
For example, I wrote this code to test it:
class Example {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try(ServerSocket serverSocket = new ServerSocket()) {
serverSocket.bind(new InetSocketAddress(8080));
Socket socket = serverSocket.accept();
int dataByte = socket.getInputStream().read();
System.out.println(dataByte);
} catch (IOException e) {
e.printStackTrace();
}
});
thread.start();
thread.interrupt();
}
}
On OpenJdk-16.0.2 there is no actual interruption.
I see two possible solutions for your problem:
Check Thread.interrupted inside the while loop if you are sure that Socket doesn't block your thread.
If your are not sure, use SocketChannel in non-blocking mode instead of Socket for checking Thread.interrupted manually.
For the second way I tranformed my example into this:
class Example {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try(ServerSocketChannel serverSocket = ServerSocketChannel.open()) {
serverSocket.configureBlocking(false);
serverSocket.bind(new InetSocketAddress(8080));
SocketChannel socket = null;
while (socket == null) {
socket = serverSocket.accept();
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
socket.read(byteBuffer);
byte[] bytes = new byte[byteBuffer.limit()];
byteBuffer.flip();
byteBuffer.get(bytes);
System.out.println(new String(bytes, StandardCharsets.UTF_8));
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
System.out.println("Interrupted successfully");
}
});
thread.start();
thread.interrupt();
}
}
It works.
Good luck with Java :)
I would have expected that as soon as shutdownNow(); is being called, an InterruptedException would be thrown within my ClientConnection.run()
Your messagesQueue should be a BlockingQueue. So messagesQueue.put(message) will make you need to catch an Interrupted exception. So only when the thread is blocked in the put method(queue is full), you call threadpool#shutdownNow, then the thread will receive an Interrupted exception. In other cases, thread will not receive this Interrupted exception.
You can change while ((request = reader.readLine()) != null) to while ((request = reader.readLine()) != null && !Thread.interrupted()).
Another solution is to maintain all client sockets, and close all client sockets when you need to close them, this way, the client thread will directly receive an IOException:
List<Socket> clientSockets = new ArrayList<>();
while (true) {
try {
Socket accept = serverSocket.accept();
clientSockets.add(accept);
executorService.submit(new ClientConnection(accept));
}catch (Exception e) {
for (Socket socket : clientSockets) {
try {
socket.close();
} catch (Exception exception) {
//
}
}
//executorService.shutdownNow();
}
}

Android: BluetoothSocket receives its own output

I have an app that connects to a RaspberryPi via Bluetooth and loops the same data to it while it receives some data back.
I had some issues with the connection so this workaround is needed to connect my android phone to the RaspberryPi: IOException: read failed, socket might be closed - Bluetooth on Android 4.3
For some reason, the android phone is receiving its own output.
The String "Hello Raspberry. It's me, AndroidPhone" is sent to the output in a never-ending loop. The incoming data (from the RaspberryPi) is also read in a never-ending loop.
But somehow I don't only receive the data from the RaspberryPi but also the string sends via smartphone. This is my code:
public class MainActivity extends AppCompatActivity {
private BluetoothAdapter bluetoothAdapter;
UUID SERIAL_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
#Override
protected void onCreate(Bundle savedInstanceState) {
// (...)
// Only GUI-stuff until this point
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice raspberryPi = bluetoothAdapter.getRemoteDevice("B8:27:EB:56:DC:B2");
BluetoothSocket btSocket;
try {
btSocket = raspberryPi.createRfcommSocketToServiceRecord(SERIAL_UUID);
btSocket.connect();
} catch (IOException e) {
Log.e("BTError", e.getMessage());
// Workaround, found on: https://stackoverflow.com/questions/18657427/ioexception-read-failed-socket-might-closed-bluetooth-on-android-4-3
try {
Log.e("BTError", "Trying fallback...");
btSocket = (BluetoothSocket) raspberryPi.getClass().getMethod("createRfcommSocket", new Class[]{int.class}).invoke(raspberryPi, 1);
btSocket.connect();
(new Thread(new SendingThread(btSocket))).start();
(new Thread(new ReceivingThread(btSocket))).start();
} catch (Exception e2) {
Log.e("BTError", e2.getMessage());
Log.e("BTError", "Couldn't establish Bluetooth connection!");
}
}
}
private class SendingThread extends Thread {
private OutputStream out;
public SendingThread(BluetoothSocket btSocket) {
try {
out = btSocket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void run() {
try {
int delay = 100000000;
while (true) {
if (delay == 0) {
Log.i("WRT", "Written to RaspberryPi");
out.write("Hello Raspberry. It's me, AndroidPhone".getBytes());
delay = 100000000;
}
delay--;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private class ReceivingThread extends Thread {
private InputStream in;
public ReceivingThread(BluetoothSocket btSocket) {
try {
in = btSocket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void run() {
int data = 0;
while (true) {
try {
data = in.read();
} catch (IOException e) {
e.printStackTrace();
}
Log.i("RCV", String.valueOf((char) data));
}
}
}
On the RaspberryPi end, everything looks normal. A simple java program starts the Linux command rfcomm listen /dev/rfcomm0 and reads from/writes to the file /dev/rfcomm0 with FileReader and FileWriter. The only relevant lines on this end are:
run {
// Inside writer-thread
bluetoothWriter = new BufferedWriter(new FileWriter("/dev/rfcomm0"));
while(true) {
bluetoothWriter.write("This is RaspPi");
bluetoothWriter.flush();
}
}
and
run {
// Inside reader-thread
bluetoothReader = new BufferedReader(new FileReader("/dev/rfcomm0"));
while(true) {
int incData = bluetoothReader.read();
System.out.print((char) incData);
}
}
Thank you for your help!
edit: Still no solution to this problem. I suspected that the RaspberryPi is somehow sending back what it received. But when I disabled that it sends out anything, the smartphone still directly receives what it has sent out.
I scoured over the Bluetooth classes sources. The workaround seems legit from the first glances. Try this first:
if (delay == 0) {
Log.i("WRT", "Written to RaspberryPi");
out.write("Hello Raspberry. It's me, AndroidPhone".getBytes());
out.flush(); // <-- You are not flushing
delay = 100000000;
}
And the message sticks in you socket for you to read over and over again.
If that does not fix it the other option I can think of is that somehow the socket is initialized to be a socket to your Android device. The .createRfcommSocket() method seems to create a socket to your own device if the Bluetooth device is null when the socket was being created. I'm not sure how this would exactly happen, but if the Raspberry Pi's state is somehow mangled after exception I suppose it could be something to look into.
On the raspy side: If you are just starting both of those threads doesn't it mean that you are constantly sending messages to /dev/rfcomm0 and flushing. I recommend that you change it so that raspy reacts to a received message by sending back the wanted message instead of spamming all the time. I'm not sure if this is part of your problem but it would at least make debugging & development a bit easier.
I am not sure if this is the solution you need, because I don't know if you are using bluetooth classic or bluetooth 4.0>+, but I wrote a library for text based BLE and WiFi P2P 2-way communication for android (and I know the Raspberry Pi is capable of BLE communication), I don't create a socket connection for BLE communication though, but I do for WiFi P2P. Take a look, I hope it helps. It isn't published yet, so you would have to clone/fork the repo.
I think you have trouble writing
As far as I know, for buffer, should use \n and ...
bluetoothWriter.write("This is RaspPi\n");
But I prefer to use a combination of DataOutputStream and BufferedReader
For Read:
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
try {
String line = bufferedReader.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
for write:
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
String s = "Hi\n";
try {
dataOutputStream.write(s.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
It is better to correct the point made by a dear friend about flush() ...
I'm not sure, please test yourself ...

TCP socket not working on devices with Android 7+ [duplicate]

This question already has answers here:
How can I fix 'android.os.NetworkOnMainThreadException'?
(66 answers)
Closed 5 years ago.
In my application I am using TCP socket connection to connect android device to server. Socket is working normally on every device, except devices with Android 7+ where I get "Socket is closed" error.
Socket problems happen on Nexus 6P and Samsung s7!
I am using socket in Runnable in a Service, initializing socket with:
public class ClientThread implements Runnable {
public void run() {
while (true) {
try {
DataHandler.indicator.set(false);
mRun = true;
if (DataHandler.DOMAIN_IP.contains(":")) {
serverAddr = Inet4Address.getByName(DataHandler.DOMAIN_IP.split(":")[0]);
} else
serverAddr = Inet4Address.getByName(DataHandler.DOMAIN_IP.replace(":3030", ""));
Log.i("Socket", "Connecting");
socket = new Socket(serverAddr, SERVERPORT);
socket.setKeepAlive(true);
socket.setSoLinger(true, 1);
socket.setSoTimeout(30000);
DataHandler.IP_ADDRESS = socket.getLocalAddress().toString().split("/")[1];
DataInputStream dis = new DataInputStream(socket.getInputStream());
connectionDelay = MIN_WAIT;
Log.i("Socket", "Connected");
String serverMessage = "";
byte[] array = new byte[1024];
while (mRun) {
int firstbye = dis.read();
int arraylength = dis.available();
array = new byte[arraylength];
dis.read(array, 0, array.length);
String str = new String(new byte[]{(byte) firstbye});
serverMessage += str + EncodingUtils.getString(array, "UTF-8");
DataHandler.indicator.set(true);
if (firstbye != -1) {
String mstString = new String(serverMessage);
serverMessage = "";
onReceive(NotificationService.this, mstString);
} else { // if (serverMessage.equalsIgnoreCase("")) {
try {
socket.close();
Log.e("Socket", "Disconnect");
socket = null;
stopClient();
} catch (Exception e) {
e.getMessage();
}
serverMessage = null;
DataHandler.indicator.set(false);
continue;
}
}
} catch (UnknownHostException e) {
isNeedtoWrite = true;
} catch (IOException e1) {
isNeedtoWrite = true;
DataHandler.indicator.set(false);
} finally {
try {
socket.close();
DataHandler.indicator.set(false);
Log.e("Socket", "Disconnect");
socket = null;
stopClient();
} catch (Exception e) {
e.getMessage();
}
}
try {
connectionDelay = Math.min(MAX_WAIT, RandomUtils.nextInt(MIN_WAIT, (int) (connectionDelay * BACKOFF_RATE)));
Log.i("SocketSleep", String.valueOf(connectionDelay));
Thread.sleep(connectionDelay);
DataHandler.indicator.set(false);
} catch (InterruptedException e) {
e.getMessage();
}
}
}
}
Before code reaches setSoTimeout it gets to exception.
I have tried a different application which uses same Socket logic and on start it worked normally and then it just dropped unexpectedly and it doesn't work anymore. I checked on wireshark and this happens:
Does anyone have any clue what could the problem be?
Bux fix:
Due to a bug in previous versions of Android, the system did not flag writing to a TCP socket on the main thread as a strict-mode violation. Android 7.0 fixes this bug. Apps that exhibit this behavior now throw an android.os.NetworkOnMainThreadException. Generally, performing network operations on the main thread is a bad idea because these operations usually have a high latency that causes ANRs and jank.
And link to documentation.
I found out what the problem was... I actually HAD some code which called
socket.getOutputStream().write(GetSendString());
outside my thread, and this was causing socket to disconnect / crash because of NetworkOnMainthread error, but I didn't have Exception handled properly!
Maybe the problem is caused by reading the InputStream using .read command.
Try using BufferedReader, and similar for the OutputStream :
BufferedReader dis = new BufferedReader(
new InputStreamReader( socket.getInputStream() ));
PrintWriter os_buffer = new PrintWriter(
new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);

Serversocket accepts more connections as it should from the same client

I want to make a simple http response without much to it, but it somehow screwes up. It makes around 3 connections but sometimes sending the 2nd or 3rd response before the request when i checked it with Wireshark. I am confused big time!
Here is the code:
public static int letThrough() throws IOException
{
ServerSocket serverSocket = new ServerSocket(80);
Socket connectionSocket = null;
while(running)
{
connectionSocket = serverSocket.accept();
System.out.println("Client: " + connectionSocket.getPort());
makeSession(connectionSocket);
}
serverSocket.close();
return 1;
}
static public Runnable makeSession(Socket connectionSocket)
{
Runnable rSession = new Runnable(){
#Override
public void run()
{
try
{
String clientIp = connectionSocket.getInetAddress().getHostAddress();
System.out.println("Client verbunden... (" + clientIp + ")");
PrintWriter oPWriter;
oPWriter = new PrintWriter(connectionSocket.getOutputStream(), false);
System.out.println("Streams erstellt...");
Date today = new Date();
Thread.sleep(10);
oPWriter.println("HTTP/1.1 200 OK");
oPWriter.println("Content-Type: text/html");
oPWriter.println("Server: Bot");
// this blank line signals the end of the headers
oPWriter.println("");
// Send the HTML page
oPWriter.println("<H1>Welcome to the Ultra Mini-WebServer</H2><br><H2>"+today.toString());
oPWriter.flush();
oPWriter.close();
connectionSocket.shutdownOutput();
while(connectionSocket.isOutputShutdown() == false)
{
Thread.sleep(100);
}
connectionSocket.close();
while(connectionSocket.isClosed() == false)
{
Thread.sleep(100);
}
}
catch (IOException | InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
new Thread(rSession).start();
return rSession;
}
I tried already some things, like the last two while-loops with sleep. Any Idea how i even can debug this?
Problem found! There was no real Problem with my code, it was the stupid chrome browser establishing connnections without using them. Or maybe there used for secret things that i cant see.

Sockets don't work as excpected with LAN or NET, but perfectly locally

I'm programming a network software with Java, but I have a real problem using my application through a "true" network.
Let a software be a host, and listening for client connexions.
Here is my Server loop :
public void run() {
while (mServerSocket != null) {
try {
Socket wClient = mServerSocket.accept();
System.out.println("Client connecté");
wClient.setSoTimeout(50);
wClient.setTcpNoDelay(false);
Client c = new Client(wClient);
synchronized(this) {
mWaitingClients.add(c);
c.start();
}
} catch(Exception ex) {
System.out.println("Server error : " + ex.getMessage());
}
}
}
When a client tried to connect to the server, I use this function :
public Client connect(InetAddress addr, int port) throws Exception {
Socket socket = new Socket(addr, port);
socket.setSoTimeout(50);
socket.setTcpNoDelay(false);
Client c = new Client(socket);
c.start();
return c;
}
And here is the client loop :
public void run() {
try {
ObjectOutputStream out = new ObjectOutputStream(mSocket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(mSocket.getInputStream());
while(mSocket.isConnected() && !mSocket.isClosed()) {
for (int i = 0; i < mOutMessages.size(); i++) {
Message msg = mOutMessages.get(i);
out.writeObject(msg);
}
out.flush();
mOutMessages.clear();
Thread.sleep(50);
out.reset();
while(true) {
try {
Message m = (Message) in.readObject();
mInMessages.add(m);
} catch (Exception e) {
break;
}
}
Thread.sleep(50);
}
} catch(Exception ex) {
try {
mSocket.close();
} catch(Exception exx) {
exx.printStackTrace();
}
ex.printStackTrace();
}
}
Some other parts of the program do Message and put them in the Output list of the Client (mOutMessages).
Some other parts of the program read Message from the mInMessages of the Client.
But something is wrong with this. It works fine locally (server and client on the same computer), but fail or is hazardous (some messages are sent but never received) using two computers (with LAN or through the Internet).
Server ever detect connexions from the clients, send "handshake" messages to the client, but the client never receives them.
I'm more a C programmer than a Java one, and I never had this kind of problem using libc Sockets, so, why my way of doing is wrong ?
Thank you !
Edit :
My Server is created using this function :
public void open(int port) throws Exception {
mServerSocket = new ServerSocket(port);
start(); // Call the run mentionned above.
}
Edit :
Here is my solution, maybe it's not perfect but it works !
public void run() {
try {
BufferedOutputStream buf_out = new BufferedOutputStream(
mSocket.getOutputStream()
);
BufferedInputStream buf_in = new BufferedInputStream(
mSocket.getInputStream()
);
ObjectOutputStream out = new ObjectOutputStream(buf_out);
out.flush();
ObjectInputStream in = new ObjectInputStream(buf_in);
while(mSocket.isConnected() && !mSocket.isClosed()) {
for (int i = 0; i < mOutMessages.size(); i++) {
Message msg = mOutMessages.get(i);
out.writeObject(msg);
out.flush();
}
mOutMessages.clear();
out.reset();
while(true) {
try {
Message m = (Message) in.readObject();
mInMessages.add(m);
} catch (Exception e) {
break;
}
}
}
} catch(Exception ex) {
try {
mSocket.close();
} catch(Exception exx) {
exx.printStackTrace();
}
ex.printStackTrace();
}
If I understand right, both client and server use the run method. If both client and server happen to write sufficiently large messages (not fitting in involved buffers) at the same time then you get a deadlock because neither partner advances to reading (which would drain full buffers). Due to network delays, this might only happen in the non-local scenario, i.e. there may be enough time to pile up enough messages in the mOutMessages buffer.
Note that documentation of Socket.setSoTimeout (which you used) only says that it affects read()s. (For example, in my JDK, ObjectOutputStream seems to use a BlockDataOutputStream with a buffer size of 1024 bytes).
I recommend to either use a separate thread for reading/writing or (if you know the maximum messages size) use a sufficiently large buffer (by wrapping the SocketOutputStream in a BufferedOutputStream). If you opt for larger buffers, you may also want to write one message at a time (and try to read messages after each).

Categories

Resources