在这只做了一个简单的例子,没有用到数据库,思路就是客户端发送信息到服务器端,服务器端转发所有数据到客户端,校验服务器端发来消息是否是自己发出的,如果是自己发出的,则不显示自己的消息


  • 贴一下Android客户端的源码 -

MainActivity.Java




  1. package com.zml.chatproject; 

  2. import android.os.AsyncTask; 
  3. import android.os.Bundle; 
  4. import android.os.Handler; 
  5. import android.os.Message; 
  6. import android.support.v7.app.AppCompatActivity; 
  7. import android.text.Editable; 
  8. import android.text.TextUtils; 
  9. import android.text.TextWatcher; 
  10. import android.util.Log; 
  11. import android.view.KeyEvent; 
  12. import android.view.View; 
  13. import android.view.WindowManager; 
  14. import android.widget.Button; 
  15. import android.widget.EditText; 
  16. import android.widget.ListView; 
  17. import android.widget.Toast; 

  18. import com.google.gson.Gson; 

  19. import java.io.DataInputStream; 
  20. import java.io.DataOutputStream; 
  21. import java.io.IOException; 
  22. import java.net.Socket; 
  23. import java.util.ArrayList; 
  24. import java.util.List; 


  25. /** 
  26.  * @author 郑明亮   @email 1072307340@qq.com 
  27.  * @Time:2016/4/20 14:25 
  28.  * @version 1.0 
  29.  * TODO 
  30.  */ 
  31. public class MainActivity extends AppCompatActivity implements View.OnClickListener, TextWatcher { 
  32.     private static final String TAG = "MainActivity"; 
  33.     public static final int SENDMESSAGE = 0x004; 
  34.     public static final String name = System.currentTimeMillis()+""; 
  35.     List<Msg> list ; 
  36.     ListView mListView; 
  37.     EditText edit; 
  38.     Button bt_send; 
  39.     Socket socket; 
  40.     public static final int SHOWMSG = 0x003; 
  41.     private DataInputStream mDataInputStream = null; 
  42.     private DataOutputStream mDataOutputStream = null; 
  43.     private boolean Conneted = false; 
  44.     Handler mHandler = new Handler() { 
  45.         @Override 
  46.         public void handleMessage(Message msg) { 
  47.             super.handleMessage(msg); 
  48.             Log.i(TAG,"执行到handle"); 
  49.             if (msg.what == SENDMESSAGE) { 
  50.                 Msg xiaoxi = (Msg) msg.obj; 
  51.                 Log.i(TAG,"handler:"+xiaoxi.toString()); 
  52.                 list.add(xiaoxi); 
  53.                 //设置适配器 
  54.                 mListView.setAdapter(new MyAdapter(MainActivity.this, list)); 
  55.                 mListView.setSelection(mListView.getCount()-1); 
  56.             } 
  57.         } 
  58.     }; 

  59.     @Override 
  60.     protected void onCreate(Bundle savedInstanceState) { 
  61.         super.onCreate(savedInstanceState); 
  62.         setContentView(R.layout.activity_main); 
  63.         mListView = (ListView) findViewById(R.id.listView); 
  64.         edit = (EditText) findViewById(R.id.et_edit); 
  65.         edit.addTextChangedListener(this); 
  66.         bt_send = (Button) findViewById(R.id.bt_send); 
  67.         bt_send.setOnClickListener(this); 
  68.         list = new ArrayList<>(); 
  69.    new AsyncTask<Void,Void,String>(){ 
  70.        @Override 
  71.        protected String doInBackground(Void... params) { 
  72.            try { 
  73.                socket = new Socket("172.18.40.182", 9999); 
  74. //               socket = new Socket("115.28.167.152", 9999); 
  75.                connect(); 
  76.                ClientThread thread = new ClientThread(); 
  77.                thread.run(); 

  78.            } catch (IOException e) { 
  79.                e.printStackTrace(); 
  80.            } 
  81.            return null; 
  82.        } 
  83.    }.execute(); 

  84.     } 

  85.     @Override 
  86.     public boolean onKeyDown(int keyCode, KeyEvent event) { 
  87.         switch (event.getAction()){ 
  88.             case KeyEvent.KEYCODE_HOME: 
  89.                 Toast.makeText(MainActivity.this,"就是不让你退出,O(∩_∩)O哈哈哈~",Toast.LENGTH_LONG).show(); 
  90.                 Log.i(TAG,"KeyEvent.KEYCODE_HOME"+KeyEvent.KEYCODE_HOME); 
  91.                 break; 
  92.             case KeyEvent.KEYCODE_MOVE_HOME: 
  93.                 Toast.makeText(MainActivity.this,"就是不让你退出,O(∩_∩)O哈哈哈~",Toast.LENGTH_LONG).show(); 
  94.                 Log.i(TAG,"KeyEvent.KEYCODE_MOVE_HOME"+KeyEvent.KEYCODE_MOVE_HOME); 
  95.                 break; 
  96.         } 
  97.         return true; 
  98.     } 
  99.     @Override 
  100.     public void onAttachedToWindow() 

  101.     { // TODO Auto-generated method stub 

  102.         this.getWindow().setType(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); 

  103.         super.onAttachedToWindow(); 

  104.     } 
  105.     @Override 
  106.     public void onClick(View v) { 
  107.         switch (v.getId()){ 
  108.             case R.id.bt_send: 
  109.                 String show = edit.getText().toString().trim(); 
  110.                 if (TextUtils.isEmpty(show)){ 

  111.                 }else { 
  112.                     edit.setText(""); 
  113.                     Msg msg = new Msg(); 
  114.                     msg.setFlag(Msg.TO); 
  115.                     msg.setMsg(show); 
  116.                     msg.setUsername(name); 
  117.                     list.add(msg); 
  118.                     mListView.setAdapter(new MyAdapter(MainActivity.this,list)); 
  119.                     mListView.setSelection(mListView.getCount()-1); 
  120.                     try {if (mDataOutputStream==null){ 
  121.                         mDataOutputStream = new DataOutputStream(socket.getOutputStream()); 
  122.                     } 
  123.                         Gson gson = new Gson(); 
  124.                       show =  gson.toJson(msg); 
  125.                         mDataOutputStream.writeUTF(show); 
  126.                         mDataOutputStream.flush(); 
  127.                         Log.i(TAG,"发送成功:"+show); 
  128.                     } catch (IOException e) { 
  129.                         e.printStackTrace(); 
  130.                     } 

  131.                 } 
  132.                 break; 
  133.         } 
  134.     } 

  135.     @Override 
  136.     public void beforeTextChanged(CharSequence s, int start, int count, int after) { 
  137.         bt_send.setEnabled(false); 
  138.     } 

  139.     @Override 
  140.     public void onTextChanged(CharSequence s, int start, int before, int count) { 
  141.         if (s.length()>0){ 
  142.             bt_send.setEnabled(true); 
  143.         } 
  144.     } 

  145.     @Override 
  146.     public void afterTextChanged(Editable s) { 

  147.     } 

  148.     /** 
  149.      * 开启线程,接收消息 
  150.      */ 
  151.     private class ClientThread implements Runnable { 
  152.         @Override 
  153.         public void run() { 
  154.             while (Conneted) { 
  155.                 try { 
  156.                     String str = mDataInputStream.readUTF(); 
  157.                     Log.i(TAG,"子线程得到数据:"+str); 
  158.                     Gson gson = new Gson(); 
  159.                     Msg msg = gson.fromJson(str,Msg.class); 
  160.                     msg.setFlag(Msg.FROM); 
  161.                     Message message = mHandler.obtainMessage(); 
  162.                     message.what = SENDMESSAGE; 
  163.                     message.obj = msg; 
  164.                     mHandler.sendMessage(message); 

  165.                 } catch (IOException e) { 
  166.                     e.printStackTrace(); 
  167.                 } 


  168.             } 
  169.         } 
  170.     } 

  171.     /** 
  172.      * 打开连接 
  173.      */ 
  174.     public void connect() { 
  175.         try { 
  176.             mDataInputStream = new DataInputStream(socket.getInputStream()); 
  177.             mDataOutputStream = new DataOutputStream(socket.getOutputStream()); 
  178.             if (socket.isConnected()){ 
  179.                 Log.i(TAG, "连接上了"); 
  180.             }else { 
  181.                 Log.i(TAG, "连接失败"); 
  182.             } 

  183.             Conneted = true; 
  184.         } catch (IOException e) { 
  185.             e.printStackTrace(); 
  186.         } 

  187.     } 

  188.     /** 
  189.      * 断开与服务器的连接 
  190.      */ 
  191.     public void disconnect() { 
  192.         try { 
  193.             mDataInputStream.close(); 
  194.             mDataOutputStream.close(); 
  195.             socket.close(); 
  196.         } catch (IOException e) { 
  197.             e.printStackTrace(); 
  198.         } 

  199.     } 

  200.     @Override 
  201.     protected void onDestroy() { 
  202.         super.onDestroy(); 
  203.         disconnect(); 
  204.     } 
  205. }


Msg.java 消息实体类



  1. package com.zml.chatproject; 

  2. /** 
  3.  * Created by bri on 2016/4/16. 
  4.  */ 

  5. /** 
  6.  * 消息实体类 
  7.  */ 
  8. public class Msg { 
  9.     public static final int FROM = 0x001; 
  10.     public static final int TO = 0x002; 

  11.     /** 
  12.      * 发送聊天消息 
  13.      */ 
  14.     private String msg; 
  15.     /** 
  16.      * 标识符,表示是发送方还是接收方 
  17.      */ 
  18.     private int flag; 
  19.     /** 
  20.      * 用户名 
  21.      */ 
  22.     private String username; 

  23.     @Override 
  24.     public String toString() { 
  25.         return "Msg{" + 
  26.                 "msg='" + msg + '\\'' + 
  27.                 ", flag=" + flag + 
  28.                 ", username='" + username + '\\'' + 
  29.                 '}'; 
  30.     } 

  31.     public String getMsg() { 
  32.         return msg; 
  33.     } 

  34.     public void setMsg(String msg) { 
  35.         this.msg = msg; 
  36.     } 

  37.     public int getFlag() { 
  38.         return flag; 
  39.     } 

  40.     public void setFlag(int flag) { 
  41.         this.flag = flag; 
  42.     } 

  43.     public String getUsername() { 
  44.         return username; 
  45.     } 

  46.     public void setUsername(String username) { 
  47.         this.username = username; 
  48.     } 
  49. }


MyAdapter 数据适配器



  1. package com.zml.chatproject; 

  2. import android.content.Context; 
  3. import android.util.Log; 
  4. import android.view.LayoutInflater; 
  5. import android.view.View; 
  6. import android.view.ViewGroup; 
  7. import android.widget.BaseAdapter; 
  8. import android.widget.ListAdapter; 
  9. import android.widget.TextView; 

  10. import java.util.List; 

  11. /** 
  12.  * Created by bri on 2016/4/17. 
  13.  */ 
  14. public class MyAdapter extends BaseAdapter implements ListAdapter { 
  15.     private static final String TAG = "MyAdapter"; 

  16.     Context context; 
  17.     List<Msg> message; 
  18.     public MyAdapter(Context context, List<Msg> message) { 
  19.         this.context = context; 
  20.         this.message = message; 
  21.     } 

  22.     @Override 
  23.     public int getCount() { 
  24.         return message.size(); 
  25.     } 

  26.     @Override 
  27.     public Object getItem(int position) { 
  28.         return null; 
  29.     } 

  30.     @Override 
  31.     public long getItemId(int position) { 
  32.         return 0; 
  33.     } 

  34.     @Override 
  35.     public View getView(int position, View convertView, ViewGroup parent) { 

  36.         if (convertView == null){ 
  37.         convertView = LayoutInflater.from(context).inflate(R.layout.activity_main_item,null); 


  38.         } 
  39.         TextView tv_from = ViewHolder.get(convertView,R.id.tv_chatting_from); 
  40.         TextView tv_to = ViewHolder.get(convertView,R.id.tv_chatting_to); 
  41. //        tv_from.setText(message.getMsg()); 
  42.         Log.i(TAG,"接收成功"+message.get(position).getMsg()); 
  43.         if (message.get(position).getFlag()==(Msg.FROM)){ 
  44.             if (message.get(position).getUsername().equals(MainActivity.name)){ 
  45.                 tv_from.setVisibility(View.GONE); 
  46.                 tv_to.setVisibility(View.GONE);} 
  47.             else { Log.i(TAG,"接收成功FROM"+message.get(position).getMsg()); 
  48.                 tv_from.setText(message.get(position).getMsg()); 
  49.                 tv_from.setVisibility(View.VISIBLE); 
  50.                 tv_to.setVisibility(View.GONE);} 


  51. //            Toast.makeText(context,"from:"+message.get(position).getMsg(),Toast.LENGTH_LONG).show(); 
  52.         }if (message.get(position).getFlag()==(Msg.TO)){ 
  53. //            Toast.makeText(context,"to:"+message.get(position).getMsg(),Toast.LENGTH_LONG).show(); 
  54.             Log.i(TAG,"接收成功TO"+message.get(position).getMsg()); 
  55.             tv_to.setText(message.get(position).getMsg()); 
  56.             tv_from.setVisibility(View.GONE); 
  57.             tv_to.setVisibility(View.VISIBLE); 

  58.         } 


  59.         return convertView; 


  60.     } 

  61. }


ViewHoder 一个超实用的通用ViewHoder类



  1. package com.zml.chatproject; 

  2. import android.content.Context; 
  3. import android.util.Log; 
  4. import android.view.LayoutInflater; 
  5. import android.view.View; 
  6. import android.view.ViewGroup; 
  7. import android.widget.BaseAdapter; 
  8. import android.widget.ListAdapter; 
  9. import android.widget.TextView; 

  10. import java.util.List; 

  11. /** 
  12.  * Created by bri on 2016/4/17. 
  13.  */ 
  14. public class MyAdapter extends BaseAdapter implements ListAdapter { 
  15.     private static final String TAG = "MyAdapter"; 

  16.     Context context; 
  17.     List<Msg> message; 
  18.     public MyAdapter(Context context, List<Msg> message) { 
  19.         this.context = context; 
  20.         this.message = message; 
  21.     } 

  22.     @Override 
  23.     public int getCount() { 
  24.         return message.size(); 
  25.     } 

  26.     @Override 
  27.     public Object getItem(int position) { 
  28.         return null; 
  29.     } 

  30.     @Override 
  31.     public long getItemId(int position) { 
  32.         return 0; 
  33.     } 

  34.     @Override 
  35.     public View getView(int position, View convertView, ViewGroup parent) { 

  36.         if (convertView == null){ 
  37.         convertView = LayoutInflater.from(context).inflate(R.layout.activity_main_item,null); 


  38.         } 
  39.         TextView tv_from = ViewHolder.get(convertView,R.id.tv_chatting_from); 
  40.         TextView tv_to = ViewHolder.get(convertView,R.id.tv_chatting_to); 
  41. //        tv_from.setText(message.getMsg()); 
  42.         Log.i(TAG,"接收成功"+message.get(position).getMsg()); 
  43.         if (message.get(position).getFlag()==(Msg.FROM)){ 
  44.             if (message.get(position).getUsername().equals(MainActivity.name)){ 
  45.                 tv_from.setVisibility(View.GONE); 
  46.                 tv_to.setVisibility(View.GONE);} 
  47.             else { Log.i(TAG,"接收成功FROM"+message.get(position).getMsg()); 
  48.                 tv_from.setText(message.get(position).getMsg()); 
  49.                 tv_from.setVisibility(View.VISIBLE); 
  50.                 tv_to.setVisibility(View.GONE);} 


  51. //            Toast.makeText(context,"from:"+message.get(position).getMsg(),Toast.LENGTH_LONG).show(); 
  52.         }if (message.get(position).getFlag()==(Msg.TO)){ 
  53. //            Toast.makeText(context,"to:"+message.get(position).getMsg(),Toast.LENGTH_LONG).show(); 
  54.             Log.i(TAG,"接收成功TO"+message.get(position).getMsg()); 
  55.             tv_to.setText(message.get(position).getMsg()); 
  56.             tv_from.setVisibility(View.GONE); 
  57.             tv_to.setVisibility(View.VISIBLE); 

  58.         } 


  59.         return convertView; 


  60.     } 

  61. }


  • 好了,客户端的源码贴完了,接下来服务器端的源码就比较简单了


Client.java 重写一个子线程




  1. import java.io.DataInputStream; 
  2. import java.io.DataOutputStream; 
  3. import java.io.IOException; 
  4. import java.net.Socket; 
  5. import java.util.ArrayList; 
  6. import java.util.Collections; 
  7. import java.util.Iterator; 
  8. import java.util.List; 

  9. /** 
  10.  * @author 郑明亮 
  11.  * @Time:2016年4月16日 下午9:01:53 
  12.  * @version 1.0 
  13.  */ 


  14. public class Client implements  Runnable { 
  15.     Socket socket; 
  16.     List<Client> clients  ; 
  17.     public static final int SHOWMSG = 0x003; 
  18.     private DataInputStream mDataInputStream = null; 
  19.     private DataOutputStream mDataOutputStream = null; 
  20.     private boolean Conneted = false; 
  21.     public Client(Socket socket){ 
  22.         this.socket = socket; 
  23.         try { 
  24.             mDataInputStream = new DataInputStream(socket.getInputStream()); 
  25.             mDataOutputStream = new DataOutputStream(socket.getOutputStream()); 
  26.             Conneted = true; 
  27.             clients =new ArrayList<Client>(); 
  28.         } catch (IOException e) { 
  29.             e.printStackTrace(); 
  30.         } 

  31.     }; 

  32.     /** 
  33.      * 发送消息 
  34.      */ 
  35.     public void send(String string){ 
  36.         try { 
  37.             mDataOutputStream.writeUTF(string);//向输入流中写入数据 
  38.             System.out.println("向输入流中写入数据"); 
  39.         } catch (IOException e) { 
  40.             e.printStackTrace(); 
  41. //            Server.clients.remove(this);//出错时,客户端可能已断线,移除当前客户端 
  42.             System.out.println("出错时,客户端可能已断线,移除当前客户端"); 
  43.         } 
  44.     } 
  45.     @Override 
  46.     public void run() { 
  47.     while(Conneted){ 
  48.         try { 
  49.             System.out.println("连接成功!"); 
  50.             //读取数据 
  51.             String str = mDataInputStream.readUTF(); 
  52.             clients = Server.clients; 
  53.             synchronized (clients) { 
  54.                 Iterator<Client> iterator =clients.iterator(); 
  55.                 while (iterator.hasNext()) { 
  56.                     Client client = iterator.next(); 
  57.                     //将读取的数据发送回去 
  58.                     System.out.println("将读取的数据发送回去"+str); 
  59.                     client.send(str); 
  60.                 } 
  61. //                Conneted = false; 
  62.             } 
  63.         } catch (IOException e) { 
  64.             e.printStackTrace(); 
  65.             Server.clients.remove(this); 
  66.             System.out.println("线程出现异常"); 
  67.         }finally{ 

  68. //            try { 
  69. ////                mDataOutputStream.close(); 
  70. ////                mDataInputStream.close(); 
  71. //            } catch (IOException e) { 
  72. //                // TODO Auto-generated catch block 
  73. //                e.printStackTrace(); 
  74. //            } 
  75.         } 
  76.     } 
  77.     } 


  78. }


Server 服务器端,先运行起它来,再去部署客户端即可



  1. import java.io.IOException; 
  2. import java.net.ServerSocket; 
  3. import java.net.Socket; 
  4. import java.util.ArrayList; 
  5. import java.util.Collections; 
  6. import java.util.List; 

  7. /** 
  8.  * @author 郑明亮 
  9.  * @Time:2016年4月16日 下午9:01:43 
  10.  * @version 1.0 
  11.  */ 
  12. public class Server { 
  13.     static boolean started = false; 
  14.     public static List<Client> clients = new ArrayList<>(); 

  15.     public static void main(String a[]){ 
  16.     try { 
  17.         ServerSocket serverSocket = new ServerSocket(9999); 
  18.         System.out.println("开启服务,等待监听"); 
  19.         started = true; 
  20.         while (started) { 
  21.             System.out.println("开始监听"); 
  22.             Socket socket = serverSocket.accept(); 
  23.             Client client = new Client(socket);     
  24.             new Thread(client).start(); 
  25.             clients.add(client); 
  26.         } 


  27.     } catch (IOException e) { 
  28.         // TODO Auto-generated catch block 
  29.         e.printStackTrace(); 
  30.     } 

  31. }