Android之Socket群组聊天

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

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

    MainActivity.Java

  1. package com.zml.chatproject; 
  2.  
  3. import android.os.AsyncTask; 
  4. import android.os.Bundle; 
  5. import android.os.Handler; 
  6. import android.os.Message; 
  7. import android.support.v7.app.AppCompatActivity; 
  8. import android.text.Editable; 
  9. import android.text.TextUtils; 
  10. import android.text.TextWatcher; 
  11. import android.util.Log; 
  12. import android.view.KeyEvent; 
  13. import android.view.View; 
  14. import android.view.WindowManager; 
  15. import android.widget.Button; 
  16. import android.widget.EditText; 
  17. import android.widget.ListView; 
  18. import android.widget.Toast; 
  19.  
  20. import com.google.gson.Gson; 
  21.  
  22. import java.io.DataInputStream; 
  23. import java.io.DataOutputStream; 
  24. import java.io.IOException; 
  25. import java.net.Socket; 
  26. import java.util.ArrayList; 
  27. import java.util.List; 
  28.  
  29.  
  30. /** 
  31.  * @author 郑明亮   @email 1072307340@qq.com 
  32.  * @Time:2016/4/20 14:25 
  33.  * @version 1.0 
  34.  * TODO 
  35.  */ 
  36. public class MainActivity extends AppCompatActivity implements View.OnClickListener, TextWatcher { 
  37.     private static final String TAG = "MainActivity"; 
  38.     public static final int SENDMESSAGE = 0x004; 
  39.     public static final String name = System.currentTimeMillis()+""; 
  40.     List<Msg> list ; 
  41.     ListView mListView; 
  42.     EditText edit; 
  43.     Button bt_send; 
  44.     Socket socket; 
  45.     public static final int SHOWMSG = 0x003; 
  46.     private DataInputStream mDataInputStream = null; 
  47.     private DataOutputStream mDataOutputStream = null; 
  48.     private boolean Conneted = false; 
  49.     Handler mHandler = new Handler() { 
  50.         @Override 
  51.         public void handleMessage(Message msg) { 
  52.             super.handleMessage(msg); 
  53.             Log.i(TAG,"执行到handle"); 
  54.             if (msg.what == SENDMESSAGE) { 
  55.                 Msg xiaoxi = (Msg) msg.obj; 
  56.                 Log.i(TAG,"handler:"+xiaoxi.toString()); 
  57.                 list.add(xiaoxi); 
  58.                 //设置适配器 
  59.                 mListView.setAdapter(new MyAdapter(MainActivity.this, list)); 
  60.                 mListView.setSelection(mListView.getCount()-1); 
  61.             } 
  62.         } 
  63.     }; 
  64.  
  65.     @Override 
  66.     protected void onCreate(Bundle savedInstanceState) { 
  67.         super.onCreate(savedInstanceState); 
  68.         setContentView(R.layout.activity_main); 
  69.         mListView = (ListView) findViewById(R.id.listView); 
  70.         edit = (EditText) findViewById(R.id.et_edit); 
  71.         edit.addTextChangedListener(this); 
  72.         bt_send = (Button) findViewById(R.id.bt_send); 
  73.         bt_send.setOnClickListener(this); 
  74.         list = new ArrayList<>(); 
  75.    new AsyncTask<Void,Void,String>(){ 
  76.        @Override 
  77.        protected String doInBackground(Void... params) { 
  78.            try { 
  79.                socket = new Socket("172.18.40.182", 9999); 
  80. //               socket = new Socket("115.28.167.152", 9999); 
  81.                connect(); 
  82.                ClientThread thread = new ClientThread(); 
  83.                thread.run(); 
  84.  
  85.            } catch (IOException e) { 
  86.                e.printStackTrace(); 
  87.            } 
  88.            return null; 
  89.        } 
  90.    }.execute(); 
  91.  
  92.     } 
  93.  
  94.     @Override 
  95.     public boolean onKeyDown(int keyCode, KeyEvent event) { 
  96.         switch (event.getAction()){ 
  97.             case KeyEvent.KEYCODE_HOME: 
  98.                 Toast.makeText(MainActivity.this,"就是不让你退出,O(∩_∩)O哈哈哈~",Toast.LENGTH_LONG).show(); 
  99.                 Log.i(TAG,"KeyEvent.KEYCODE_HOME"+KeyEvent.KEYCODE_HOME); 
  100.                 break; 
  101.             case KeyEvent.KEYCODE_MOVE_HOME: 
  102.                 Toast.makeText(MainActivity.this,"就是不让你退出,O(∩_∩)O哈哈哈~",Toast.LENGTH_LONG).show(); 
  103.                 Log.i(TAG,"KeyEvent.KEYCODE_MOVE_HOME"+KeyEvent.KEYCODE_MOVE_HOME); 
  104.                 break; 
  105.         } 
  106.         return true; 
  107.     } 
  108.     @Override 
  109.     public void onAttachedToWindow() 
  110.  
  111.     { // TODO Auto-generated method stub 
  112.  
  113.         this.getWindow().setType(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); 
  114.  
  115.         super.onAttachedToWindow(); 
  116.  
  117.     } 
  118.     @Override 
  119.     public void onClick(View v) { 
  120.         switch (v.getId()){ 
  121.             case R.id.bt_send: 
  122.                 String show = edit.getText().toString().trim(); 
  123.                 if (TextUtils.isEmpty(show)){ 
  124.  
  125.                 }else { 
  126.                     edit.setText(""); 
  127.                     Msg msg = new Msg(); 
  128.                     msg.setFlag(Msg.TO); 
  129.                     msg.setMsg(show); 
  130.                     msg.setUsername(name); 
  131.                     list.add(msg); 
  132.                     mListView.setAdapter(new MyAdapter(MainActivity.this,list)); 
  133.                     mListView.setSelection(mListView.getCount()-1); 
  134.                     try {if (mDataOutputStream==null){ 
  135.                         mDataOutputStream = new DataOutputStream(socket.getOutputStream()); 
  136.                     } 
  137.                         Gson gson = new Gson(); 
  138.                       show =  gson.toJson(msg); 
  139.                         mDataOutputStream.writeUTF(show); 
  140.                         mDataOutputStream.flush(); 
  141.                         Log.i(TAG,"发送成功:"+show); 
  142.                     } catch (IOException e) { 
  143.                         e.printStackTrace(); 
  144.                     } 
  145.  
  146.                 } 
  147.                 break; 
  148.         } 
  149.     } 
  150.  
  151.     @Override 
  152.     public void beforeTextChanged(CharSequence s, int start, int count, int after) { 
  153.         bt_send.setEnabled(false); 
  154.     } 
  155.  
  156.     @Override 
  157.     public void onTextChanged(CharSequence s, int start, int before, int count) { 
  158.         if (s.length()>0){ 
  159.             bt_send.setEnabled(true); 
  160.         } 
  161.     } 
  162.  
  163.     @Override 
  164.     public void afterTextChanged(Editable s) { 
  165.  
  166.     } 
  167.  
  168.     /** 
  169.      * 开启线程,接收消息 
  170.      */ 
  171.     private class ClientThread implements Runnable { 
  172.         @Override 
  173.         public void run() { 
  174.             while (Conneted) { 
  175.                 try { 
  176.                     String str = mDataInputStream.readUTF(); 
  177.                     Log.i(TAG,"子线程得到数据:"+str); 
  178.                     Gson gson = new Gson(); 
  179.                     Msg msg = gson.fromJson(str,Msg.class); 
  180.                     msg.setFlag(Msg.FROM); 
  181.                     Message message = mHandler.obtainMessage(); 
  182.                     message.what = SENDMESSAGE; 
  183.                     message.obj = msg; 
  184.                     mHandler.sendMessage(message); 
  185.  
  186.                 } catch (IOException e) { 
  187.                     e.printStackTrace(); 
  188.                 } 
  189.  
  190.  
  191.             } 
  192.         } 
  193.     } 
  194.  
  195.     /** 
  196.      * 打开连接 
  197.      */ 
  198.     public void connect() { 
  199.         try { 
  200.             mDataInputStream = new DataInputStream(socket.getInputStream()); 
  201.             mDataOutputStream = new DataOutputStream(socket.getOutputStream()); 
  202.             if (socket.isConnected()){ 
  203.                 Log.i(TAG, "连接上了"); 
  204.             }else { 
  205.                 Log.i(TAG, "连接失败"); 
  206.             } 
  207.  
  208.             Conneted = true; 
  209.         } catch (IOException e) { 
  210.             e.printStackTrace(); 
  211.         } 
  212.  
  213.     } 
  214.  
  215.     /** 
  216.      * 断开与服务器的连接 
  217.      */ 
  218.     public void disconnect() { 
  219.         try { 
  220.             mDataInputStream.close(); 
  221.             mDataOutputStream.close(); 
  222.             socket.close(); 
  223.         } catch (IOException e) { 
  224.             e.printStackTrace(); 
  225.         } 
  226.  
  227.     } 
  228.  
  229.     @Override 
  230.     protected void onDestroy() { 
  231.         super.onDestroy(); 
  232.         disconnect(); 
  233.     } 
  234. }

Msg.java 消息实体类

  1. package com.zml.chatproject; 
  2.  
  3. /** 
  4.  * Created by bri on 2016/4/16. 
  5.  */ 
  6.  
  7. /** 
  8.  * 消息实体类 
  9.  */ 
  10. public class Msg { 
  11.     public static final int FROM = 0x001; 
  12.     public static final int TO = 0x002; 
  13.  
  14.     /** 
  15.      * 发送聊天消息 
  16.      */ 
  17.     private String msg; 
  18.     /** 
  19.      * 标识符,表示是发送方还是接收方 
  20.      */ 
  21.     private int flag; 
  22.     /** 
  23.      * 用户名 
  24.      */ 
  25.     private String username; 
  26.  
  27.     @Override 
  28.     public String toString() { 
  29.         return "Msg{" + 
  30.                 "msg='" + msg + '\'' + 
  31.                 ", flag=" + flag + 
  32.                 ", username='" + username + '\'' + 
  33.                 '}'; 
  34.     } 
  35.  
  36.     public String getMsg() { 
  37.         return msg; 
  38.     } 
  39.  
  40.     public void setMsg(String msg) { 
  41.         this.msg = msg; 
  42.     } 
  43.  
  44.     public int getFlag() { 
  45.         return flag; 
  46.     } 
  47.  
  48.     public void setFlag(int flag) { 
  49.         this.flag = flag; 
  50.     } 
  51.  
  52.     public String getUsername() { 
  53.         return username; 
  54.     } 
  55.  
  56.     public void setUsername(String username) { 
  57.         this.username = username; 
  58.     } 
  59. }

MyAdapter 数据适配器

  1. package com.zml.chatproject; 
  2.  
  3. import android.content.Context; 
  4. import android.util.Log; 
  5. import android.view.LayoutInflater; 
  6. import android.view.View; 
  7. import android.view.ViewGroup; 
  8. import android.widget.BaseAdapter; 
  9. import android.widget.ListAdapter; 
  10. import android.widget.TextView; 
  11.  
  12. import java.util.List; 
  13.  
  14. /** 
  15.  * Created by bri on 2016/4/17. 
  16.  */ 
  17. public class MyAdapter extends BaseAdapter implements ListAdapter { 
  18.     private static final String TAG = "MyAdapter"; 
  19.  
  20.     Context context; 
  21.     List<Msg> message; 
  22.     public MyAdapter(Context context, List<Msg> message) { 
  23.         this.context = context; 
  24.         this.message = message; 
  25.     } 
  26.  
  27.     @Override 
  28.     public int getCount() { 
  29.         return message.size(); 
  30.     } 
  31.  
  32.     @Override 
  33.     public Object getItem(int position) { 
  34.         return null; 
  35.     } 
  36.  
  37.     @Override 
  38.     public long getItemId(int position) { 
  39.         return 0; 
  40.     } 
  41.  
  42.     @Override 
  43.     public View getView(int position, View convertView, ViewGroup parent) { 
  44.  
  45.         if (convertView == null){ 
  46.         convertView = LayoutInflater.from(context).inflate(R.layout.activity_main_item,null); 
  47.  
  48.  
  49.         } 
  50.         TextView tv_from = ViewHolder.get(convertView,R.id.tv_chatting_from); 
  51.         TextView tv_to = ViewHolder.get(convertView,R.id.tv_chatting_to); 
  52. //        tv_from.setText(message.getMsg()); 
  53.         Log.i(TAG,"接收成功"+message.get(position).getMsg()); 
  54.         if (message.get(position).getFlag()==(Msg.FROM)){ 
  55.             if (message.get(position).getUsername().equals(MainActivity.name)){ 
  56.                 tv_from.setVisibility(View.GONE); 
  57.                 tv_to.setVisibility(View.GONE);} 
  58.             else { Log.i(TAG,"接收成功FROM"+message.get(position).getMsg()); 
  59.                 tv_from.setText(message.get(position).getMsg()); 
  60.                 tv_from.setVisibility(View.VISIBLE); 
  61.                 tv_to.setVisibility(View.GONE);} 
  62.  
  63.  
  64. //            Toast.makeText(context,"from:"+message.get(position).getMsg(),Toast.LENGTH_LONG).show(); 
  65.         }if (message.get(position).getFlag()==(Msg.TO)){ 
  66. //            Toast.makeText(context,"to:"+message.get(position).getMsg(),Toast.LENGTH_LONG).show(); 
  67.             Log.i(TAG,"接收成功TO"+message.get(position).getMsg()); 
  68.             tv_to.setText(message.get(position).getMsg()); 
  69.             tv_from.setVisibility(View.GONE); 
  70.             tv_to.setVisibility(View.VISIBLE); 
  71.  
  72.         } 
  73.  
  74.  
  75.         return convertView; 
  76.  
  77.  
  78.     } 
  79.  
  80. }

ViewHoder 一个超实用的通用ViewHoder类

  1. package com.zml.chatproject; 
  2.  
  3. import android.content.Context; 
  4. import android.util.Log; 
  5. import android.view.LayoutInflater; 
  6. import android.view.View; 
  7. import android.view.ViewGroup; 
  8. import android.widget.BaseAdapter; 
  9. import android.widget.ListAdapter; 
  10. import android.widget.TextView; 
  11.  
  12. import java.util.List; 
  13.  
  14. /** 
  15.  * Created by bri on 2016/4/17. 
  16.  */ 
  17. public class MyAdapter extends BaseAdapter implements ListAdapter { 
  18.     private static final String TAG = "MyAdapter"; 
  19.  
  20.     Context context; 
  21.     List<Msg> message; 
  22.     public MyAdapter(Context context, List<Msg> message) { 
  23.         this.context = context; 
  24.         this.message = message; 
  25.     } 
  26.  
  27.     @Override 
  28.     public int getCount() { 
  29.         return message.size(); 
  30.     } 
  31.  
  32.     @Override 
  33.     public Object getItem(int position) { 
  34.         return null; 
  35.     } 
  36.  
  37.     @Override 
  38.     public long getItemId(int position) { 
  39.         return 0; 
  40.     } 
  41.  
  42.     @Override 
  43.     public View getView(int position, View convertView, ViewGroup parent) { 
  44.  
  45.         if (convertView == null){ 
  46.         convertView = LayoutInflater.from(context).inflate(R.layout.activity_main_item,null); 
  47.  
  48.  
  49.         } 
  50.         TextView tv_from = ViewHolder.get(convertView,R.id.tv_chatting_from); 
  51.         TextView tv_to = ViewHolder.get(convertView,R.id.tv_chatting_to); 
  52. //        tv_from.setText(message.getMsg()); 
  53.         Log.i(TAG,"接收成功"+message.get(position).getMsg()); 
  54.         if (message.get(position).getFlag()==(Msg.FROM)){ 
  55.             if (message.get(position).getUsername().equals(MainActivity.name)){ 
  56.                 tv_from.setVisibility(View.GONE); 
  57.                 tv_to.setVisibility(View.GONE);} 
  58.             else { Log.i(TAG,"接收成功FROM"+message.get(position).getMsg()); 
  59.                 tv_from.setText(message.get(position).getMsg()); 
  60.                 tv_from.setVisibility(View.VISIBLE); 
  61.                 tv_to.setVisibility(View.GONE);} 
  62.  
  63.  
  64. //            Toast.makeText(context,"from:"+message.get(position).getMsg(),Toast.LENGTH_LONG).show(); 
  65.         }if (message.get(position).getFlag()==(Msg.TO)){ 
  66. //            Toast.makeText(context,"to:"+message.get(position).getMsg(),Toast.LENGTH_LONG).show(); 
  67.             Log.i(TAG,"接收成功TO"+message.get(position).getMsg()); 
  68.             tv_to.setText(message.get(position).getMsg()); 
  69.             tv_from.setVisibility(View.GONE); 
  70.             tv_to.setVisibility(View.VISIBLE); 
  71.  
  72.         } 
  73.  
  74.  
  75.         return convertView; 
  76.  
  77.  
  78.     } 
  79.  
  80. }
  • 好了,客户端的源码贴完了,接下来服务器端的源码就比较简单了

Client.java 重写一个子线程

  1.  
  2. import java.io.DataInputStream; 
  3. import java.io.DataOutputStream; 
  4. import java.io.IOException; 
  5. import java.net.Socket; 
  6. import java.util.ArrayList; 
  7. import java.util.Collections; 
  8. import java.util.Iterator; 
  9. import java.util.List; 
  10.  
  11. /** 
  12.  * @author 郑明亮 
  13.  * @Time:2016年4月16日 下午9:01:53 
  14.  * @version 1.0 
  15.  */ 
  16.  
  17.  
  18. public class Client implements  Runnable { 
  19.     Socket socket; 
  20.     List<Client> clients  ; 
  21.     public static final int SHOWMSG = 0x003; 
  22.     private DataInputStream mDataInputStream = null; 
  23.     private DataOutputStream mDataOutputStream = null; 
  24.     private boolean Conneted = false; 
  25.     public Client(Socket socket){ 
  26.         this.socket = socket; 
  27.         try { 
  28.             mDataInputStream = new DataInputStream(socket.getInputStream()); 
  29.             mDataOutputStream = new DataOutputStream(socket.getOutputStream()); 
  30.             Conneted = true; 
  31.             clients =new ArrayList<Client>(); 
  32.         } catch (IOException e) { 
  33.             e.printStackTrace(); 
  34.         } 
  35.  
  36.     }; 
  37.  
  38.     /** 
  39.      * 发送消息 
  40.      */ 
  41.     public void send(String string){ 
  42.         try { 
  43.             mDataOutputStream.writeUTF(string);//向输入流中写入数据 
  44.             System.out.println("向输入流中写入数据"); 
  45.         } catch (IOException e) { 
  46.             e.printStackTrace(); 
  47. //            Server.clients.remove(this);//出错时,客户端可能已断线,移除当前客户端 
  48.             System.out.println("出错时,客户端可能已断线,移除当前客户端"); 
  49.         } 
  50.     } 
  51.     @Override 
  52.     public void run() { 
  53.     while(Conneted){ 
  54.         try { 
  55.             System.out.println("连接成功!"); 
  56.             //读取数据 
  57.             String str = mDataInputStream.readUTF(); 
  58.             clients = Server.clients; 
  59.             synchronized (clients) { 
  60.                 Iterator<Client> iterator =clients.iterator(); 
  61.                 while (iterator.hasNext()) { 
  62.                     Client client = iterator.next(); 
  63.                     //将读取的数据发送回去 
  64.                     System.out.println("将读取的数据发送回去"+str); 
  65.                     client.send(str); 
  66.                 } 
  67. //                Conneted = false; 
  68.             } 
  69.         } catch (IOException e) { 
  70.             e.printStackTrace(); 
  71.             Server.clients.remove(this); 
  72.             System.out.println("线程出现异常"); 
  73.         }finally{ 
  74.  
  75. //            try { 
  76. ////                mDataOutputStream.close(); 
  77. ////                mDataInputStream.close(); 
  78. //            } catch (IOException e) { 
  79. //                // TODO Auto-generated catch block 
  80. //                e.printStackTrace(); 
  81. //            } 
  82.         } 
  83.     } 
  84.     } 
  85.  
  86.  
  87. }

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. /** 
  9.  * @author 郑明亮 
  10.  * @Time:2016年4月16日 下午9:01:43 
  11.  * @version 1.0 
  12.  */ 
  13. public class Server { 
  14.     static boolean started = false; 
  15.     public static List<Client> clients = new ArrayList<>(); 
  16.  
  17.     public static void main(String a[]){ 
  18.     try { 
  19.         ServerSocket serverSocket = new ServerSocket(9999); 
  20.         System.out.println("开启服务,等待监听"); 
  21.         started = true; 
  22.         while (started) { 
  23.             System.out.println("开始监听"); 
  24.             Socket socket = serverSocket.accept(); 
  25.             Client client = new Client(socket);     
  26.             new Thread(client).start(); 
  27.             clients.add(client); 
  28.         } 
  29.  
  30.  
  31.     } catch (IOException e) { 
  32.         // TODO Auto-generated catch block 
  33.         e.printStackTrace(); 
  34.     } 
  35.  
  36. }
原文地址:https://www.cnblogs.com/android-blogs/p/5454781.html