ObjectOutputStream и java.io.StreamCorruptedException

#java #android #objectoutputstream

#java #Android #objectoutputstream

Вопрос:

Когда я пытаюсь отправить пользовательский объект (см. Content.java ) от моего клиента к серверу с ObjectOutputStream я получаю StreamCorruptedException после отправки первого объекта. Поэтому, если я пытаюсь отправить другой объект, я получаю исключение (это работает с первого раза). Я погуглил и прочитал МНОГО материала, и теперь я собираюсь сдаться, поэтому я прошу вашей помощи.

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;
}
}
  

Стек:

 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)
  

РЕДАКТИРОВАТЬ: добавлено 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();
}
}
  

Комментарии:

1. Я не думаю, что трассировка стека соответствует указанному коду. Где вы вызываете метод «SendText»?

2. Текст отправки вызывается при нажатии кнопки в ClientActivity. Итак, я добавил код для ClientActivity в основной пост

3. Мне кажется, что вы закрываете свой сокет после того, как отправили свое сообщение на сервер, и к тому времени, когда вы захотите получить данные с сервера, сокет закрыт, что не позволит вашему InputStream работать.

4. readObject() не возвращает null, если вы не написали nul, и, в частности, не в конце потока: он выдает EOFException . Следовательно, ваш цикл чтения некорректен.

Ответ №1:

РЕДАКТИРОВАТЬ: я добавил массив в приемник, чтобы закрыть все потоки, когда приемник останавливается.

Вам следует изменить свой уровень протокола. На обоих устройствах вы должны создать, ServerSocket чтобы прослушивать новые Socket файлы. Очевидно, что при вызове любого read() метода текущий поток перейдет в заблокированное состояние, поэтому вам нужно использовать вторичный поток. Вам нужно запустить () и остановить () приемник и использовать прослушиватель для уведомления о создании сокета. Возможная реализация (ее можно значительно улучшить, но суть в этом):

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;
    }
}
  

Тогда вам нужен другой класс, который запускает поток, когда вы хотите прочитать и записать в этот сокет. Для чтения вам нужен другой поток. Вам также нужен другой прослушиватель, чтобы уведомлять о прочитанном объекте и уведомлять, когда другое устройство закрывает поток. Вам нужны методы startReading() и 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 ();
        }
    }
}
  

Использование:
Сервер

 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 ();
  

Клиент

 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 ();
  

Этот код является ядром уровня сокетов. Это не сработает, потому что я это не тестировал. Сначала вам нужно понять код, чтобы продолжить работу с протоколом.
Примечание: Не бойтесь использовать всех слушателей, которые, по вашему мнению, вам нужны, потому что вы создаете слой, который уведомляет о событиях. Это похоже на взаимодействие с пользователем при нажатии кнопки, но с сокетами.

Комментарии:

1. Хорошо, итак, я добавил код на стороне сервера, у него также есть Content-class, но он уже опубликован. В # 5, что вы предлагаете мне сделать здесь вместо этого? При чтении большого количества другого кода у меня сложилось впечатление, что мне пришлось закрыть поток, чтобы записать то, что я поместил в поток

2. Какова ваша цель? Можете ли вы объяснить, каких шагов вы хотите достичь? Пример: Одно устройство отправляет объект на другое устройство, и это второе устройство что-то делает.

3. Предполагается, что это коммуникационная часть игры. Одно устройство что-то делает и отправляет эту информацию в объекте через writeObject на сервер, или, если сервер что-то сделал, он должен отправить эту информацию клиентам. Итак, я хочу, чтобы сервер и клиенты могли постоянно получать объекты. Так, может быть, какой-то слушатель? А затем отправлять объекты с помощью buttonPush.

4. В вашем случае оба устройства являются клиентами и серверами одновременно, потому что устройство может получать (роль сервера) и отправлять (роль клиента) объект в любой момент. Завтра я отредактирую свой ответ.

5. @GagleKas Вау, спасибо!! Я просмотрел код и, думаю, я его понимаю. Я раньше не так часто использовал потоки и прослушиватели, но вы заставляете это выглядеть «просто». У меня действительно есть несколько вопросов: 1.почему Thread.sleep(10000) и receiver.stop() / stream.stop() завершают работу клиента и сервера? 2. в конце клиента написано stream.write(xxx.()); это опечатка, должно ли это быть stream.writeObject(obj) или? 3. почему цикл if в конце клиента? 4. где я должен написать код на сервере и клиенте? В конструкторе или как сервер общедоступного класса {весь код с вашего сервера-code }?