欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-03-31 16:31:28
原理 最近用socket写了一个消息推送的demo,在这里和大家分享一下。 主要实现了:一台手机向另外一台手机发送消息,这两台手机可以随时*发送文本消息进行通信,类似...

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

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

效果图:

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

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

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

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

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

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

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

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

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

我们先来看一下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工程

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

该工程是一个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> 

效果图:

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

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

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