3

When I try to send a customobject (See Content.java) from my client to the server with ObjectOutputStream, I get StreamCorruptedException after the first object is sent. So if I try to send another object I get the exception, (it works the first time). I have googled and read a LOT of stuff, and now I'm about to give up, so I ask for your help.

Client.java

public class Client extends Thread {
private final static String TAG ="Client";
private final static String IP = "10.0.2.2";
private final static int PORT = 12345;
private Socket s;
private static ObjectOutputStream out;
private static ObjectInputStream in;
//private PrintWriter out;
//private BufferedReader in;
private TextView tv;
private Content c = new Content("");

public Client(TextView tv) {
    this.tv = tv;

}

public void run() {
    s = null;
    out = null;
    in = null;
    String res;

    try {
        s = new Socket(IP, PORT);
        Log.v(TAG, "C: Connected to server" + s.toString());

        out = new ObjectOutputStream(s.getOutputStream());
        in = new ObjectInputStream(s.getInputStream());

        //out = new PrintWriter(s.getOutputStream(), true);
        //in = new BufferedReader(new InputStreamReader(s.getInputStream()));
        //c.setText("PING to server from client");
        //out.writeObject(c);


        while((c = (Content)in.readObject()) != null) {
            try {
                    res = c.getText();
                    Log.i(TAG, res);
            } catch (Exception e) {
                Log.e("readobject", e.toString());
            }
        }

    } catch(Exception e) {
        Log.e("run @ client", e.toString());
    } finally {
        try {
            out.close();
            in.close();
            s.close();
        } catch(IOException e) {
            Log.e(TAG, e.toString());
        }
    }
}

public String setText() throws Exception{
    return in.toString();

}

public void sendText(String text) {
    Content cont = new Content(text);
    try {
        out.writeObject(cont);
    } catch(Exception e) {
        e.printStackTrace();
        Log.e("writeObject", e.toString());
    } finally {
        try {
            out.flush();
            out.close();
            s.close();
            Log.i(TAG, "Object sent");
        } catch (Exception e){}
    }


}
}

Content.java

public class Content implements Serializable{
private String text;

public Content(String text) {
    this.text = text;
}

public String getText() {
    return text;
}

public void setText(String text) {
    this.text = text;
}
}

Stack:

04-24 17:09:12.345: WARN/System.err(520): java.io.StreamCorruptedException
04-24 17:09:12.355: WARN/System.err(520): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1707)
04-24 17:09:12.355: WARN/System.err(520): at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1660)
04-24 17:09:12.365: WARN/System.err(520): at client.android.Client.sendText(Client.java:83)
04-24 17:09:12.365: WARN/System.err(520): at client.android.ClientActivity.sendToServer(ClientActivity.java:38)
04-24 17:09:12.365: WARN/System.err(520): at java.lang.reflect.Method.invokeNative(Native Method)
04-24 17:09:12.365: WARN/System.err(520): at java.lang.reflect.Method.invoke(Method.java:521)
04-24 17:09:12.365: WARN/System.err(520): at android.view.View$1.onClick(View.java:2026)
04-24 17:09:12.365: WARN/System.err(520): at android.view.View.performClick(View.java:2364)
04-24 17:09:12.365: WARN/System.err(520): at android.view.View.onTouchEvent(View.java:4179)
04-24 17:09:12.365: WARN/System.err(520): at android.widget.TextView.onTouchEvent(TextView.java:6541)
04-24 17:09:12.375: WARN/System.err(520): at android.view.View.dispatchTouchEvent(View.java:3709)
04-24 17:09:12.375: WARN/System.err(520): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
0    4-24 17:09:12.385: WARN/System.err(520): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
04-24 17:09:12.385: WARN/System.err(520): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
04-24 17:09:12.385: WARN/System.err(520): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
04-24 17:09:12.385: WARN/System.err(520): at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1659)
04-24 17:09:12.385: WARN/System.err(520): at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
04-24 17:09:12.385: WARN/System.err(520): at android.app.Activity.dispatchTouchEvent(Activity.java:2061)
04-24 17:09:12.395: WARN/System.err(520): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1643)
04-24 17:09:12.395: WARN/System.err(520): at android.view.ViewRoot.handleMessage(ViewRoot.java:1691)
04-24 17:09:12.395: WARN/System.err(520): at android.os.Handler.dispatchMessage(Handler.java:99)
04-24 17:09:12.395: WARN/System.err(520): at android.os.Looper.loop(Looper.java:123)
04-24 17:09:12.395: WARN/System.err(520): at android.app.ActivityThread.main(ActivityThread.java:4363)
04-24 17:09:12.395: WARN/System.err(520): at java.lang.reflect.Method.invokeNative(Native Method)
04-24 17:09:12.395: WARN/System.err(520): at java.lang.reflect.Method.invoke(Method.java:521)
04-24 17:09:12.395: WARN/System.err(520): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
04-24 17:09:12.395: WARN/System.err(520): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
04-24 17:09:12.395: WARN/System.err(520): at dalvik.system.NativeStart.main(Native Method)

EDIT: added ClientActivity.java ClientActivity.java

public class ClientActivity extends Activity {
private EditText et;
private Client c;
private TextView tv;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    et =(EditText)findViewById(R.id.clientTxt);
    tv = (TextView)findViewById(R.id.recievedTxt);

    c = new Client(tv);
    c.start();

    try {
        tv.setText(c.setText());
    } catch (Exception e) {}


}

public void sendToServer(View v) throws Exception{
    String text = et.getText().toString();
    Log.i("EdittextVALUE", text);
    c.sendText(text);

}

}

Server.java

public class Server extends Thread {
private static final String TAG = "ServerThread";
private static final int PORT = 12345;

public void run() {
    ServerSocket ss = null;
    Socket s = null;
    String res = "";

    try {
        Log.i(TAG, "Start server");
        ss = new ServerSocket(PORT);
        Log.i(TAG, "ServerSocket created waiting for Client..");
        while(true) {
            s = ss.accept();
            Log.v(TAG, "Client connected");
            Connection c = new Connection(s);
        }
    }catch(IOException e) {
        e.printStackTrace();
    } finally {
        try {
            //out.close();
            //in.close();
            s.close();
            ss.close();
        } catch (IOException e) {}
    }
}

Connection.java

public class Connection extends Thread {
private Socket socket;
private static ObjectOutputStream out;
private static ObjectInputStream in;
private final String TAG = "ConnectionClass";

public Connection(Socket socket) {
    try {
        this.socket = socket;
        out = new ObjectOutputStream(socket.getOutputStream());
        in = new ObjectInputStream(socket.getInputStream());

        this.start();

    } catch (IOException ex) {
        ex.printStackTrace();
        Log.e(TAG, ex.toString());
    }

}

public void run() {
    String res = "";
    Content c = null;
    try {
        while(true) {
        while((c = (Content)in.readObject()) != null) {
            try {

                    res = c.getText();
                    Log.i(TAG, res);

            } catch (Exception e) {
                Log.e("lololololo", e.toString());
            }
        }
        }
    } catch (Exception ex) {
        Log.e(TAG, ex.toString());
    } finally {
        try {
            socket.close();
            in.close();
            out.close();
        } catch (Exception e) {}
    }

}

ServerActivity.java

public class ServerActivity extends Activity {
public Server server;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    server = new Server();
    server.start();
}
}
user713821
  • 349
  • 5
  • 14
  • I do not think the stacktrace matches the code given. Where are you calling the "sendText" method? – born2snipe Apr 24 '11 at 16:49
  • The sendText is called when a button is pushed in ClientActivity. So I added the code for ClientActivity in the main post – user713821 Apr 24 '11 at 16:57
  • It looks like to me you are closing your socket after you sent your message to the server and by the time you want to receive data from the server the socket is closed which would not allow your InputStream to work. – born2snipe Apr 24 '11 at 17:17
  • `readObject()` does not return null unless you wrote a nul, and specifically not at end of stream: it throws an `EOFException`. Your read loop is therefore incorrect. – user207421 Jan 28 '15 at 17:54

1 Answers1

2

EDIT: I've added an array in the receiver to close all streams when the receiver stops.

You should redesign your protocol layer. In both devices you have to create a ServerSocket to listen for new Sockets. Obviously, if you call any read() method the current thread will enter in a blocked state, so you need to use a secondary thread. You need to start() and stop() the receiver and use a listener to notify the socket creations. A possible implementation (it can be improved a lot, but the core is this):

Receiver.java

public class Receiver{
private ArrayList<SocketStream> socketStreams;
    private OnNewSocketListener listener;
    private ServerSocket server;
    private Thread thread;
    private int port;

    public static interface OnNewSocketListener{
        void onNewSocket (Stream stream);
    }

    public Receiver (int port, OnNewSocketListener listener){
        this.listener = listener;
            this.port = port;
    }

    public synchronized start (){
        if (thread != null) return;

        server = new ServerSocket (port);
        thread = new Thread (new Runnable (){
                @Override
                public void run (){
                    try{
                        running = true;
                        while (running){
                        socketStreams.add (stream);
                                                    //See Stream.java below
                            listener.onNewSocket (new Stream (server.accept ()));
                        }
                    }catch (SocketException e){
                        //stop() has been called
                    }catch (IOException e){
                        //Error handling
                    }
                }
            }).start ();
        }
    }

    public synchronized void stop (){
        if (thread == null) return;

        running = false;
        try{
            if (server != null){
                server.close ();
            }
        }catch (IOException e){}

    for (SocketStream stream: socketStreams){
        stream.close ();
    }
    socketStreams.clear ();

        thread = null;
    }
}

Then you need another class that starts a thread when you want to read and write to this socket. To read you need another thread. You also need another listener to notify the object read and to notify when the other device closes the stream. You need a startReading() and close() methods (if you stop reading the socket it will close):

Stream.java

public class Stream{
    private Socket socket;
    private OnCloseStreamListener closeListener;
    private OnReadObjectListener readListener;
    private ObjectInputStream in;
    private ObjectOutputStream out;
    private Thread thread;

    public static interface OnReadObjectListener{
        void onReadObject (Object obj);
    }

    public static interface OnCloseStreamListener{
        void onCloseStream ();
    }

    //Used by the receiver to create an input socket
    public Stream (Socket socket){
        this.socket = socket;
        out = new ObjectOutputStream (socket.getOutputStream ());
    }

    //Used by the user to create an output socket, when the client wants to create a socket with the server
    public Stream (String address, int port){
        socket = new Socket (address, port);
        out = new ObjectOutputStream (socket.getOutputStream ());
    }

    public void setOnCloseStreamListener (OnCloseStreamListener listener){
        closeListener = listener;
    }

    public void setOnReadObjectListener (OnReadObjectListener listener){
        readListener = listener;
    }

    public synchronized void startReading (){
        if (thread != null) return;

        thread = new Thread (new Runnable (){
            @Override
            public void run (){
                try{
                    in = new ObjectInputStream (socket.getInputStream ());
                    reading = true;
                    while (reading){
                        Object obj = in.readObject ();
                        if (obj == null){
                            //The other device has closed its socket stream
                            reading = false;
                            closeListener.onCloseSocketStream ();
                        }else{
                            readListener.onReadObject (obj);
                        }
                    }
                }catch (SocketException e){
                    //stopReading() has been called
                }catch (IOException e){
                    //Error handling
                }
            }
        }).start ();
    }

    public synchronized void writeObject (Object obj){
        out.writeObject (obj);
        out.flush;
    }

    public synchronized void close (){
        if (thread != null){
            reading = false;
            socket.close ();
            in.close ();
            out.close ();
            thread = null;
        }else{
            socket.close ();
            in.close ();
        }
    }
}

Usage:
Server

Receiver receiver = new Receiver (5000, new Receiver.OnNewSocketListener (){
    @Override
    void onNewSocket (Stream stream){
        stream.setOnCloseStreamListener (new Stream.OnCloseStreamListener (){
            @Override
            void onCloseStream (){
                //Stream is closed automatically, don't need to call close()
                //Do something
            }
        });
        stream.setOnReadObjectListener (new Stream.OnReadObjectListener (){
            @Override
            void onReadObject (Object obj){
                //Do something with obj
                if (obj.isDoingSomeMaliciousActivities ()){
                    stream.close ();
                }else if (obj.isDoingGoodStuff){
                                    stream.writeObject (new GoodStuff ());
                            }
            }
        });
        stream.startReading ();
    }
});
receiver.start ();
Thread.sleep (10000);
receiver.stop ();

Client

Stream stream = new Stream ("localhost", 5000);
stream.setOnCloseStreamListener (new Stream.OnCloseStreamListener (){
    @Override
    void onCloseStream (){
        //Stream is closed automatically, don't need to call close()
        //Do something
    }
});
stream.setOnReadObjectListener (new Stream.OnReadObjectListener (){
    @Override
    void onReadObject (Object obj){
        //Do something with obj
        if (obj.isDoingSomeMaliciousActivities ()){
            stream.close ();
        }else if (obj.isDoingGoodStuff){
            stream.writeObject (new GoodStuff ());
        }
    }
});
stream.startReading ();
if (iHaveAGoodDay){
    stream.writeObject (new IamHappy ());
}else{
    stream.writeObject (new IwillHackYou ());
}
Thread.sleep (10000);
stream.close ();

This code is the core of the socket layer. It won't work because I don't tested it. You first need to understand the code to proceed with the protocol.
Note: Don't be afraid to use all the listeners you think you need because you are constructing a layer that notifies events. It's like the user interaction when presses a button but with sockets.

Gabriel Llamas
  • 18,244
  • 26
  • 87
  • 112
  • Ok, so I've added the server-side code, it also has the Content-class, but it's already posted. On #5, what do u suggest I do here instead? I got the impression when reading a lot of other code that I had to close the stream to write what I've put to the stream – user713821 Apr 24 '11 at 18:54
  • What is your goal? Can you explain what steps do you want to achieve? Example: One device sends an object to another device and this second device does something. – Gabriel Llamas Apr 24 '11 at 20:04
  • This is supposed to be the communication part of a game. One device does something, and sends this information in an object through writeObject to the server, or if the server has done something, it should send this info to the clients. So I want the server and clients to be able to receive objects all the time. So maybe a listener of some sort? And then send objects on buttonPush. – user713821 Apr 24 '11 at 20:50
  • In your case both devices are clients and servers at the same time, because a device can receive (server role) and send (client role) an object at any moment. Tomorrow I'll edit my answer. – Gabriel Llamas Apr 24 '11 at 22:50
  • @GagleKas Wow, thanks!! I have gone through the code and I think I understand it. I haven't used threads and listeners so much before but you make it look "easy". I do have some questions tho: 1.why the Thread.sleep(10000) and the receiver.stop()/stream.stop() the end of client and server? 2. at the end of client, it says stream.write(xxx.()); is this a typo, should it be stream.writeObject(obj) or? 3. why the if-loop at the end of client? 4. where should I write the code in the server and client? In the constructor, or like public class Server {all the code from the your server-code }? – user713821 Apr 25 '11 at 13:27
  • 1) Is an example, in your code the receiver.stop() will be called in the onStop() method of your activity. 2) Yes, it should be writeObject(). 3) Is an example to show that a client can read and write to the stream. 4) It depends on your implementation. Server: you can create the receiver in the constructor of a class X and call start() and stop from 2 other methods of the class X. Client: you can create the stream and set the listeners in a constructor of another class Y and use startReading() and close() from 2 other methods of class Y. – Gabriel Llamas Apr 25 '11 at 14:12
  • Remember to accept the solution (the tick below the left number). Thanks. – Gabriel Llamas Apr 25 '11 at 14:18
  • @GagleKas I can't seem to get the ArrayList to add new Streams as they are created. All of the lines from thread = new Thread(new Runnable() to }).start(); are red, and the error says: incompatible types, required: java.lang.Thread, found: void. And at Stream > startReading > run, there is this _if(message== null)_, is the "message" something I should get from _Object o = in.readObejct()_? – user713821 Apr 25 '11 at 17:12
  • @GagleKas nevermind the last entry, I managed to correct it. In Stream and Receiver I changed to thread.start(); instead of calling start() at the end of adding all the run method to runnable. Cause when start is called after thread = new Thread(new Runnable()).start(); we set thread to void, since that's what start() returns (or it doesn't return anything). And I changed the "message" == null, to object == null, and added the object as an variable before the while-loop. Just thought I'd share in case anyone else tries this. And thanks again!! – user713821 Apr 25 '11 at 21:06
  • Ops, yes, you are right. It's "obj == null", not "message == null" – Gabriel Llamas Apr 26 '11 at 07:27