首页 > 系统 > Android > 正文

Android中使用socket通信实现消息推送的方法详解

2019-12-12 06:31:28
字体:
来源:转载
供稿:网友

原理
最近用socket写了一个消息推送的demo,在这里和大家分享一下。

主要实现了:一台手机向另外一台手机发送消息,这两台手机可以随时自由发送文本消息进行通信,类似我们常用的QQ。

效果图:

2016423101913983.jpg (574×1015)

2016423101940190.png (480×854)

2016423102054594.jpg (720×1280)

原理:手机通过socket发送消息到服务器,服务器每接收到一条消息之后,都会把这条消息放进一个messageList里面,服务器会不停地检测messageList是否含有消息,如果有的话就会根据messageList里面item的数据,推送到相应的另一端手机上面。

下面简单画了一个图来说明这个原理:

演示:手机客户端client1发送消息msg1到手机客户端client2,client2收到消息后回复msg2给client1

2016423102200108.png (585×438)

1.手机客户端client1发送一条“msg1”的文本消息到服务器;

2.服务器收到来自client1的“msg1”消息后,把它add进messageList里面;

3.服务器检测到messageList里面含有消息(开启服务器时就新建里一个检测messageList的线程,线程里面有一个死循环,用于不停检测messageList是否含有消息);

4.服务器读取消息数据,如读取到来自client1发给client2的消息“msg1”,那么服务器就把“msg1”推送到client2上;

5.client2检测到服务器推送的消息,做出相应的操作(如:震动、铃声、显示消息等);

6.client2接收到来自服务器推送的“msg1”消息后,client2也回复一条文本消息“msg2”给client1,此过程和client1发送消息给client2一样。

7.最后,client2就可以显示来自client1发送的消息“msg1”,而client1则可以显示来自client2的回复消息“msg2”。

实现过程
根据消息推送的原理图,我们的实现过程主要分为Server端和Client端,Server端采用Java的编程,而Client端则用Android编程。

所以在这里也分别创建了两个工程SocketServer和SocketClient

2016423102229375.jpg (269×207)

我们先来看一下SocketMessage.java类:

public class SocketMessage {    public int to;//socketID,指发送给谁   public int from;//socketID,指谁发送过来的   public String msg;//消息内容   public String time;//接收时间   public SocketThread thread;//socketThread下面有介绍 } 

该类是一个消息类,用于表示消息是由谁发给谁的、消息内容是什么、接收时间是多少,只有几个属性,比较简单。

而MyServer.java类就相对比较多一些代码:

package com.jimstin.server;  import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date;  import org.json.JSONObject;   import com.jimstin.msg.SocketMessage;  public class MyServer {    private boolean isStartServer;   private ServerSocket mServer;   /**    * 消息队列,用于保存SocketServer接收来自于客户机(手机端)的消息    */   private ArrayList<SocketMessage> mMsgList = new ArrayList<SocketMessage>();   /**    * 线程队列,用于接收消息。每个客户机拥有一个线程,每个线程只接收发送给自己的消息    */   private ArrayList<SocketThread> mThreadList = new ArrayList<SocketThread>();      /**    * 开启SocketServer    */   private void startSocket() {     try {       isStartServer = true;       int prot = 2000;//端口可以自己设置,但要和Client端的端口保持一致       mServer = new ServerSocket(prot);//创建一个ServerSocket       System.out.println("启动server,端口:"+prot);       Socket socket = null;       int socketID = 0;//Android(SocketClient)客户机的唯一标志,每个socketID表示一个Android客户机       //开启发送消息线程       startSendMessageThread();       //用一个循环来检测是否有新的客户机加入       while(isStartServer) {         //accept()方法是一个阻塞的方法,调用该方法后,         //该线程会一直阻塞,直到有新的客户机加入,代码才会继续往下走         socket = mServer.accept();         //有新的客户机加入后,则创建一个新的SocketThread线程对象         SocketThread thread = new SocketThread(socket, socketID++);         thread.start();         //将该线程添加到线程队列         mThreadList.add(thread);       }            } catch (Exception e) {       e.printStackTrace();     }   }      /**    * 开启推送消息线程,如果mMsgList中有SocketMessage,则把该消息推送到Android客户机    */   public void startSendMessageThread() {     new Thread(){       @Override       public void run() {         super.run();         try {           /*如果isStartServer=true,则说明SocketServer已启动,           用一个循环来检测消息队列中是否有消息,如果有,则推送消息到相应的客户机*/           while(isStartServer) {             //判断消息队列中的长度是否大于0,大于0则说明消息队列不为空             if(mMsgList.size() > 0) {               //读取消息队列中的第一个消息               SocketMessage from = mMsgList.get(0);               for(SocketThread to : mThreadList) {                 if(to.socketID == from.to) {                   BufferedWriter writer = to.writer;                   JSONObject json = new JSONObject();                   json.put("from", from.from);                   json.put("msg", from.msg);                   json.put("time", from.time);                   //writer写进json中的字符串数据,末尾记得加换行符:"/n",否则在客户机端无法识别                   //因为BufferedReader.readLine()方法是根据换行符来读取一行的                   writer.write(json.toString()+"/n");                   //调用flush()方法,刷新流缓冲,把消息推送到手机端                   writer.flush();                   System.out.println("推送消息成功:"+from.msg+">> to socketID:"+from.to);                   break;                 }               }               //每推送一条消息之后,就要在消息队列中移除该消息               mMsgList.remove(0);             }             Thread.sleep(200);           }         } catch (Exception e) {           e.printStackTrace();         }       }     }.start();   }      /**    * 定义一个SocketThread类,用于接收消息    *    */   public class SocketThread extends Thread {          public int socketID;     public Socket socket;//Socket用于获取输入流、输出流     public BufferedWriter writer;//BufferedWriter 用于推送消息     public BufferedReader reader;//BufferedReader 用于接收消息          public SocketThread(Socket socket, int count) {       socketID = count;       this.socket = socket;       System.out.println("新增一台客户机,socketID:"+socketID);     }          @Override     public void run() {       super.run();        try {         //初始化BufferedReader         reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));         //初始化BufferedWriter         writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8"));         //如果isStartServer=true,则说明SocketServer已经启动,         //现在需要用一个循环来不断接收来自客户机的消息,并作其他处理         while(isStartServer) {           //先判断reader是否已经准备好           if(reader.ready()) {             /*读取一行字符串,读取的内容来自于客户机             reader.readLine()方法是一个阻塞方法,             从调用这个方法开始,该线程会一直处于阻塞状态,             直到接收到新的消息,代码才会往下走*/             String data = reader.readLine();             //讲data作为json对象的内容,创建一个json对象             JSONObject json = new JSONObject(data);             //创建一个SocketMessage对象,用于接收json中的数据             SocketMessage msg = new SocketMessage();             msg.to = json.getInt("to");             msg.msg = json.getString("msg");             msg.from = socketID;             msg.time = getTime(System.currentTimeMillis());             //接收到一条消息后,将该消息添加到消息队列mMsgList             mMsgList.add(msg);             System.out.println("收到一条消息:"+json.getString("msg")+" >>>> to socketID:"+json.getInt("to"));           }           //睡眠100ms,每100ms检测一次是否有接收到消息           Thread.sleep(100);         }                } catch (Exception e) {         e.printStackTrace();       }             }   }   /**    * 获取指定格式的时间字符串,通过毫秒转换日期    * @param millTime    */   private String getTime(long millTime) {     Date d = new Date(millTime);     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");     return sdf.format(d);   }   public static void main(String[] args) {     MyServer server = new MyServer();     server.startSocket();   }  } 

2.SocketClient工程

2016423102315850.jpg (276×358)

该工程是一个Android的工程,只有一个MainActivity.java和activity_main.xml文件,

先看一下activity_main.xml布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"   xmlns:tools="http://schemas.android.com/tools"   android:layout_width="match_parent"   android:layout_height="match_parent"   tools:context=".MainActivity"   android:orientation="vertical" >      <LinearLayout      android:layout_width="match_parent"     android:layout_height="wrap_content"     android:orientation="horizontal">     <EditText        android:id="@+id/ip_edt"       android:layout_width="0dp"       android:layout_height="wrap_content"       android:layout_weight="1"       android:hint="ip"       android:text="172.16.1.200"/>     <EditText        android:id="@+id/port_edt"       android:layout_width="0dp"       android:layout_height="wrap_content"       android:layout_weight="1"       android:hint="port"       android:text="2000"/>   </LinearLayout>   <Button      android:id="@+id/start_btn"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:text="start"/>   <EditText      android:id="@+id/socket_id_edt"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:hint="socketID"/>          <EditText      android:id="@+id/msg_edt"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:minLines="5"     android:hint="content"     android:gravity="top"     />   <Button      android:id="@+id/send_btn"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:text="send"/>   <TextView      android:id="@+id/console_txt"     android:layout_width="match_parent"     android:layout_height="0dp"     android:layout_weight="1"/>     <Button      android:id="@+id/clear_btn"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:text="clear"/> </LinearLayout> 

效果图:

2016423102346449.jpg (720×1280)

MainActivity.java类:

package com.jimstin.socketclient;  import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; import java.net.UnknownHostException; import java.text.SimpleDateFormat; import java.util.Date;  import org.json.JSONObject;  import com.tencent.stat.MtaSDkException; import com.tencent.stat.StatConfig; import com.tencent.stat.StatService;  import android.R.integer; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import android.app.Activity;  public class MainActivity extends Activity implements OnClickListener {    private EditText mIPEdt, mPortEdt, mSocketIDEdt, mMessageEdt;   private static TextView mConsoleTxt;      private static StringBuffer mConsoleStr = new StringBuffer();   private Socket mSocket;   private boolean isStartRecieveMsg;      private SocketHandler mHandler;   protected BufferedReader mReader;//BufferedWriter 用于推送消息   protected BufferedWriter mWriter;//BufferedReader 用于接收消息      @Override   protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);     initView();   }    private void initView() {     mIPEdt = (EditText) findViewById(R.id.ip_edt);     mPortEdt = (EditText) findViewById(R.id.port_edt);     mSocketIDEdt = (EditText) findViewById(R.id.socket_id_edt);     mMessageEdt = (EditText) findViewById(R.id.msg_edt);     mConsoleTxt = (TextView) findViewById(R.id.console_txt);     findViewById(R.id.start_btn).setOnClickListener(this);     findViewById(R.id.send_btn).setOnClickListener(this);     findViewById(R.id.clear_btn).setOnClickListener(this);     mHandler = new SocketHandler();   }    /**    * 初始化socket    */   private void initSocket() {     //新建一个线程,用于初始化socket和检测是否有接收到新的消息     Thread thread = new Thread(new Runnable() {              @Override       public void run() {         String ip = mIPEdt.getText().toString();//IP         int port = Integer.parseInt(mPortEdt.getText().toString());//Socket                  try {           isStartRecieveMsg = true;           mSocket = new Socket(ip, port);           mReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream(), "utf-8"));           mWriter = new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream(), "utf-8"));           while(isStartRecieveMsg) {             if(mReader.ready()) {               /*读取一行字符串,读取的内容来自于客户机               reader.readLine()方法是一个阻塞方法,               从调用这个方法开始,该线程会一直处于阻塞状态,               直到接收到新的消息,代码才会往下走*/               String data = mReader.readLine();               //handler发送消息,在handleMessage()方法中接收               mHandler.obtainMessage(0, data).sendToTarget();             }             Thread.sleep(200);           }           mWriter.close();           mReader.close();           mSocket.close();         } catch (Exception e) {           e.printStackTrace();         }        }     });     thread.start();   }      @Override   public void onClick(View v) {     switch (v.getId()) {     case R.id.send_btn:       send();       break;     case R.id.clear_btn:       mConsoleStr.delete(0, mConsoleStr.length());       mConsoleTxt.setText(mConsoleStr.toString());       break;     case R.id.start_btn:       if(!isStartRecieveMsg) {         initSocket();       }       break;     default:       break;     }   }    /**    * 发送    */   private void send() {     new AsyncTask<String, Integer, String>() {        @Override       protected String doInBackground(String... params) {         sendMsg();         return null;       }     }.execute();   }   /**    * 发送消息    */   protected void sendMsg() {     try {       String socketID = mSocketIDEdt.getText().toString().trim();       String msg = mMessageEdt.getText().toString().trim();       JSONObject json = new JSONObject();       json.put("to", socketID);       json.put("msg", msg);       mWriter.write(json.toString()+"/n");       mWriter.flush();       mConsoleStr.append("我:" +msg+"  "+getTime(System.currentTimeMillis())+"/n");       mConsoleTxt.setText(mConsoleStr);     } catch (Exception e) {       e.printStackTrace();     }   }    static class SocketHandler extends Handler {          @Override     public void handleMessage(Message msg) {       // TODO Auto-generated method stub       super.handleMessage(msg);       switch (msg.what) {       case 0:         try {           //将handler中发送过来的消息创建json对象           JSONObject json = new JSONObject((String)msg.obj);           mConsoleStr.append(json.getString("from")+":" +json.getString("msg")+"  "+getTime(System.currentTimeMillis())+"/n");           //将json数据显示在TextView中           mConsoleTxt.setText(mConsoleStr);         } catch (Exception e) {           e.printStackTrace();         }                  break;        default:         break;       }     }   }      @Override   public void onBackPressed() {     super.onBackPressed();     isStartRecieveMsg = false;   }      private static String getTime(long millTime) {     Date d = new Date(millTime);     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");     return sdf.format(d);   }    } 

以上代码的注释都比较详细,就不再多说了。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表