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

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

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

效果图:

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

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

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

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

我们先来看一下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的工程,只有一个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>

效果图:

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

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

时间: 2024-10-02 16:35:07

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

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

原理最近用socket写了一个消息推送的demo,在这里和大家分享一下. 主要实现了:一台手机向另外一台手机发送消息,这两台手机可以随时自由发送文本消息进行通信,类似我们常用的QQ. 效果图: 原理:手机通过socket发送消息到服务器,服务器每接收到一条消息之后,都会把这条消息放进一个messageList里面,服务器会不停地检测messageList是否含有消息,如果有的话就会根据messageList里面item的数据,推送到相应的另一端手机上面. 下面简单画了一个图来说明这个原理: 演示

线程-android中UDP socket通信的问题

问题描述 android中UDP socket通信的问题 我使用两个线程,一个用来发送 数据,一个用来接收数据,先开启接收数据线程,接收到数据之后然后回应给对方,但是发送一直停在SocketAddess s=pacakge.getSocketAddess这一步,pacakge是接收到的包.接收线程是这样的:while(true){ socket.receive(packge);}接收到包之后我开启发送线程就卡在上面 那步了,是不是 socket在处于阻塞状态时不能发送数据包啊? 解决方案 默认是

Java Web项目中使用Socket通信多线程、长连接的方法_java

很多时候在javaweb项目中我们需要用到Socket通信来实现功能,在web中使用Socket我们需要建立一个监听程序,在程序启动时,启动socket监听.我们的应用场景是在java项目中,需要外接如一个硬件设备,通过tcp通信,获取设备传上来的数据,并对数据做回应. 先看一下web的监听代码: import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class

iOS 下APNS推送处理函数详解

相比起Android,iOS在推送方面无疑惯例得更好.APNS(Apple Push Notification Service)是苹果公司提供的消息推送服务.其原理就是,第三方应用将要推送给用户的信息推送到苹果服务器,苹果服务器再通过统一的系统接口将这些信息推送到用户的手机上.如果对此不舍了解的朋友可以参见这篇文章:一步一步教你做ios 推送 本文着重叫在App端如何处理推送信息.主要涉及一下几个比较重要的函数,而这些函数都是AppDelegate类中: - (BOOL)application:

Android中GIF动图的播放控制和监听详解

前言 最近接手的项目里涉及到了 GIF 动图的播放与监听,在上一版本中对于 GIF 的处理是由 H5 来实现的,因为考虑到用户体验,因此现在的需求是将这块儿原生化,途中差点误入歧途!下面来看看详细的介绍吧. Android 中 GIF 动图处理与监听 刚开始第一个想到的便是 glide , 但是自认为 glide 不能够控制 GIF 以及去监听它,所以网上去搜寻别的方法.看到有一个方案是将图片分帧,一张张的去用逐帧动画来实现. 我开始怀疑给这个解决方案的人了,都什么时代了还做这种费力不讨好,大量

Android应用中图片浏览时实现自动切换功能的方法详解_Android

先给最终效果图: 当我们在最下边的gallery中切换图片时,上面的大图片会自动切换,切换时有动画效果哦,很简单的一个程序,有待完善更多的功能! activity代码: package cn.com.chenzheng_java; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.view.View; import android.view.V

Android中使用am命令实现在命令行启动程序详解_Android

在Android中,除了从界面上启动程序之外,还可以从命令行启动程序,使用的是命令行工具am. 复制代码 代码如下: usage: am [subcommand] [options]     start an Activity: am start [-D]         -D: enable debugging     send a broadcast Intent: am broadcast     start an Instrumentation: am instrument [flags

Android OnCreate()中获取控件高度与宽度两种方法详解_Android

Android OnCreate()中获取控件高度与宽度 试过在OnCreate()中获取控件高度与宽度的童鞋都知道,getWidth()与getHeight()方法返回是0,具体原因 看一下Activity的生命周期 就会明白. 上代码: 方法一: int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED); int h = View.MeasureSpec.makeMeasureSpec(0,View.Me

Android中系统自带锁WalkLock与KeyguardLock用法实例详解

本文实例讲述了Android中系统自带锁WalkLock与KeyguardLock用法.分享给大家供大家参考,具体如下: WalkLock - 顾名思义 唤醒锁 点亮屏幕用的 KeyguardLock - 顾名思义 键盘锁 解锁键盘用的 详细介绍: 1: WalkLock 唤醒锁 - WalkLock真的能点亮屏幕吗? 答案是肯定的. 可是有时候为什么不点亮屏幕,这个就是参数设置的问题了. 复制代码 代码如下:PowerManager.newWakeLock(PowerManager.FULL_