2013-10-24 56 views
0

我是Java中的TCP协议的新手。我想创建一个服务器 - 客户端聊天界面。我可以将消息从客户端发送到服务器,但是当我从服务器发送消息到客户端时,我的android应用程序突然崩溃。 这里是我的code..for Android客户端..Java服务器无法发送消息到Android客户端

public class chatWithServer extends Fragment 
{ 
    private ListView mList; 
    private ArrayList<String> arrayList; 
    private MyCustomAdapter mAdapter; 

    Button send; 
    EditText editText; 
    String serverMessage; 
    PrintWriter out; 
    BufferedReader in; 
    private static final String HOST = "192.168.48.1"; 
    private static final int PORT = 5000; 
    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
      Bundle savedInstanceState) 
    { 
     //super.onCreate(savedInstanceState); 
     final View myFragmentView = inflater.inflate(
       R.layout.activity_main, container, false); 
     arrayList = new ArrayList<String>(); 

     editText = (EditText)myFragmentView. findViewById(R.id.editText); 
     send = (Button)myFragmentView.findViewById(R.id.send_button); 
     send.setOnClickListener(onClickSend(myFragmentView)); 

     //relate the listView from java to the one created in xml 
     mList = (ListView)myFragmentView.findViewById(R.id.list); 
     mAdapter = new MyCustomAdapter(getActivity(), arrayList); 
     mList.setAdapter(mAdapter); 


     connectServerTask(); 

     return myFragmentView; 

    } 
    //-------->By clicking Send Button<---------////// 
    private OnClickListener onClickSend(final View myFragmentView) { 
     return new OnClickListener() { 

      @Override 
      public void onClick(View v) { 

       String message = editText.getText().toString(); 

       //add the text in the arrayList 
       arrayList.add("c: " + message); 

       //sends the message to the server 
       sendToServer(message); 

       //refresh the list 
       mAdapter.notifyDataSetChanged(); 
       editText.setText(""); 
      } 


     }; 
    } 

    private void connectServerTask() { 
     Runnable runnable= new Runnable() { 

      @Override 
      public void run() { 

       setSocket(); 
      } 
      private void setSocket() { 
       try { 
        // Create Socket instance 
        Socket socket = new Socket(HOST, PORT); 

        out = new PrintWriter(new BufferedWriter(new     OutputStreamWriter(socket.getOutputStream())), true); 
        out.println("Connected to Client"); 
        try{ 
        in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
        serverMessage =in.readLine(); 
        if(serverMessage!=null) { 
         //sends the message to the server 
        chatUp(serverMessage); 
        } 
        serverMessage=null; 



       } catch (UnknownHostException e) { 
        e.printStackTrace(); 
       } finally{ 
        socket.close(); 
       } 
        }catch (IOException e) { 
        e.printStackTrace(); 
       } 

      } 
         private void chatUp(String message) { 

       arrayList.add(message); 
       mAdapter.notifyDataSetChanged(); 
      }   

     }; 
     new Thread(runnable).start(); 

    } 
    //method of sending message to server 
public void sendToServer(String message){ 



     if (out != null && !out.checkError()) 
      { 
       out.println(message); 
       out.flush(); 
      }; 
    } 

    } 

Here is the code for Java-Server 

    public class TCPServer extends Thread { 

     public static final int SERVERPORT = 5000; 
     private boolean running = false; 
     private PrintWriter mOut; 
     private OnMessageReceived messageListener; 

     public static void main(String[] args) { 

      //opens the window where the messages will be received and sent 
      ServerBoard frame = new ServerBoard(); 
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
      frame.pack(); 
      frame.setVisible(true); 

     } 

     /** 
    * Constructor of the class 
    * @param messageListener listens for the messages 
    */ 
    public TCPServer(OnMessageReceived messageListener) { 
     this.messageListener = messageListener; 
    } 


    /** 
    * Method to send the messages from server to client 
    * @param message the message sent by the server 
    */ 
    public void sendMessage(String message){ 
     if (mOut != null && !mOut.checkError()) { 
      mOut.println(message); 
      mOut.flush(); 
     } 
    } 

    @SuppressWarnings("resource") 
    @Override 
    public void run() { 
     super.run(); 

     running = true; 

     try { 
      System.out.println("S: Connecting..."); 

      //create a server socket. A server socket waits for requests to come in over the network. 
      ServerSocket serverSocket = new ServerSocket(SERVERPORT); 

      //create client socket... the method accept() listens for a connection to be made to this socket and accepts it. 
      Socket client = serverSocket.accept(); 
      System.out.println("S: Receiving..."); 

      try { 

       //sends the message to the client 
       mOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true); 

       //read the message received from client 
       BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream())); 

       //in this while we wait to receive messages from client (it's an infinite loop) 
       //this while it's like a listener for messages 
       while (running) { 
        String message = in.readLine(); 

        if (message != null && messageListener != null) { 
         //call the method messageReceived from ServerBoard class 
         messageListener.messageReceived(message); 
        } 
       } 

      } catch (Exception e) { 
       System.out.println("S: Error"); 
       e.printStackTrace(); 
      } finally { 
       client.close(); 
       System.out.println("S: Done."); 
      } 

     } catch (Exception e) { 
      System.out.println("S: Error"); 
      e.printStackTrace(); 
     } 

    } 


    //Declare the interface. The method messageReceived(String message) will must be implemented in the ServerBoard 
    //class at on startServer button click 
    public interface OnMessageReceived { 
     public void messageReceived(String message); 
    } 

} 

ServerBoard.java(服务器)

public class ServerBoard extends JFrame {//jFrame is for the desplaying the window and for nice GUI 
    private JTextArea messagesArea;//A JTextArea is a multi-line area that displays plain text. 
    private JButton sendButton; 
    private JTextField message;//JTextField is a lightweight component that allows the editing of a single line of text. 
    private JButton startServer; 
    private TCPServer mServer; 

    public ServerBoard() { 

     super("ServerBoard"); 

     JPanel panelFields = new JPanel(); 
     panelFields.setLayout(new BoxLayout(panelFields,BoxLayout.X_AXIS)); 

     JPanel panelFields2 = new JPanel(); 
     panelFields2.setLayout(new BoxLayout(panelFields2,BoxLayout.X_AXIS)); 

     //here we will have the text messages screen 
     messagesArea = new JTextArea(); 
     messagesArea.setColumns(30); 
     messagesArea.setRows(10); 
     messagesArea.setEditable(false); 

     sendButton = new JButton("Send"); 
     sendButton.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) { 
       // get the message from the text view 
       String messageText = message.getText(); 
       // add message to the message area 
       messagesArea.append("\n" + messageText); 
       // send the message to the client 
       mServer.sendMessage(messageText); 
       // clear text 
       message.setText(""); 
      } 
     }); 

     startServer = new JButton("Start"); 
     startServer.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) { 
       // disable the start button 
       startServer.setEnabled(false); 

       //creates the object OnMessageReceived asked by the TCPServer constructor 
       mServer = new TCPServer(new TCPServer.OnMessageReceived() { 
        @Override 
        //this method declared in the interface from TCPServer class is implemented here 
        //this method is actually a callback method, because it will run every time when it will be called from 
        //TCPServer class (at while) 
        public void messageReceived(String message) { 
         messagesArea.append("\n "+message); 
        } 
       }); 
       mServer.start(); 

      } 
     }); 

     //the box where the user enters the text (EditText is called in Android) 
     message = new JTextField(); 
     message.setSize(200, 20); 

     //add the buttons and the text fields to the panel 
     panelFields.add(messagesArea); 
     panelFields.add(startServer); 

     panelFields2.add(message); 
     panelFields2.add(sendButton); 

     getContentPane().add(panelFields); 
     getContentPane().add(panelFields2); 


     getContentPane().setLayout(new BoxLayout(getContentPane(),BoxLayout.Y_AXIS)); 

     setSize(300, 170); 
     setVisible(true); 
    } 
} 

--Edited--

10-24 17:04:07.039: E/AndroidRuntime(15911): FATAL EXCEPTION: Thread-1131 
10-24 17:04:07.039: E/AndroidRuntime(15911): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 
10-24 17:04:07.039: E/AndroidRuntime(15911): at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:5908) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:837) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at android.view.View.requestLayout(View.java:15792) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at android.view.View.requestLayout(View.java:15792) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at android.view.View.requestLayout(View.java:15792) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at android.view.View.requestLayout(View.java:15792) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at android.view.View.requestLayout(View.java:15792) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at android.view.View.requestLayout(View.java:15792) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at android.view.View.requestLayout(View.java:15792) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at android.widget.AbsListView.requestLayout(AbsListView.java:1837) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at android.widget.AdapterView$AdapterDataSetObserver.onChanged(AdapterView.java:813) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at android.widget.AbsListView$AdapterDataSetObserver.onChanged(AbsListView.java:5998) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at android.database.DataSetObservable.notifyChanged(DataSetObservable.java:37) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at android.widget.BaseAdapter.notifyDataSetChanged(BaseAdapter.java:50) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at com.example.cardioapp.database.chatWithServer$2.display(chatWithServer.java:142) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at com.example.cardioapp.database.chatWithServer$2.setSocket(chatWithServer.java:123) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at com.example.cardioapp.database.chatWithServer$2.run(chatWithServer.java:94) 
10-24 17:04:07.039: E/AndroidRuntime(15911): at java.lang.Thread.run(Thread.java:841) 
+0

请张贴错误输出,你得到你的观点。 – Zippo

+0

这不是一个网络问题。请阅读您的错误输出。 – Zippo

回答

0

只有原始创建视图层次结构的线程可以触摸它的 视图。

您的片段正在一个线程上创建视图,并且您正在(不同的)消息接收器线程中调用notifyDataSetChanged。

当日志中的调用堆栈显示时,notifyDataSetChanged会导致调用requestLayout,该调用尝试更新相关视图。

您需要确保notifyDataSetChanged调用在与创建视图相同的线程上运行。您可以通过为父活动调用runOnUiThread来执行此操作。

+0

我试图把日志的setSocket和显示方法。在那里,我发现更新baseAdapter(mAdapter.notifyDataSetChanged())时出错了。但我不知道如何为父Activity调用runOnUiThread。你能简单地说一下......吗? – Hiren

0

我明白之后谷歌的baseAdapter..and更新我的显示方法是负责更新baseadapter ..

private void display(String... message) { 

       arrayList.add(message[0]); 
       Log.e("setSocket", "arraylist"); 
       getActivity().runOnUiThread(new Runnable() { 

        @Override 
        public void run() { 

         Log.e("display", "runonuithread"); 
         mAdapter.notifyDataSetChanged(); 

        } 
       }); 

    } 
相关问题