Android socket 编程 实现消息推送(二)

来源:互联网 发布:validator.js 编辑:程序博客网 时间:2024/06/03 19:04

上篇文章Android socket 编程 实现消息推送(一)讲到socket编程的消息推送原理,现在我们来看看基于Android客户机socket编程实现消息推送的过程。


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

所以在这里也分别创建了两个工程SocketServerSocketClient

1.SocketServer工程


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

[java] view plain copy
  1. public class SocketMessage {  
  2.   
  3.     public int to;//socketID,指发送给谁  
  4.     public int from;//socketID,指谁发送过来的  
  5.     public String msg;//消息内容  
  6.     public String time;//接收时间  
  7.     public SocketThread thread;//socketThread下面有介绍  
  8. }  

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

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

[java] view plain copy
  1. package com.jimstin.server;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.BufferedWriter;  
  5. import java.io.InputStreamReader;  
  6. import java.io.OutputStreamWriter;  
  7. import java.net.ServerSocket;  
  8. import java.net.Socket;  
  9. import java.text.SimpleDateFormat;  
  10. import java.util.ArrayList;  
  11. import java.util.Date;  
  12.   
  13. import org.json.JSONObject;  
  14.   
  15.   
  16. import com.jimstin.msg.SocketMessage;  
  17.   
  18. public class MyServer {  
  19.   
  20.     private boolean isStartServer;  
  21.     private ServerSocket mServer;  
  22.     /** 
  23.      * 消息队列,用于保存SocketServer接收来自于客户机(手机端)的消息 
  24.      */  
  25.     private ArrayList<SocketMessage> mMsgList = new ArrayList<SocketMessage>();  
  26.     /** 
  27.      * 线程队列,用于接收消息。每个客户机拥有一个线程,每个线程只接收发送给自己的消息 
  28.      */  
  29.     private ArrayList<SocketThread> mThreadList = new ArrayList<SocketThread>();  
  30.       
  31.     /** 
  32.      * 开启SocketServer 
  33.      */  
  34.     private void startSocket() {  
  35.         try {  
  36.             isStartServer = true;  
  37.             int prot = 2000;//端口可以自己设置,但要和Client端的端口保持一致  
  38.             mServer = new ServerSocket(prot);//创建一个ServerSocket  
  39.             System.out.println("启动server,端口:"+prot);  
  40.             Socket socket = null;  
  41.             int socketID = 0;//Android(SocketClient)客户机的唯一标志,每个socketID表示一个Android客户机  
  42.             //开启发送消息线程  
  43.             startSendMessageThread();  
  44.             //用一个循环来检测是否有新的客户机加入  
  45.             while(isStartServer) {  
  46.                 //accept()方法是一个阻塞的方法,调用该方法后,  
  47.                 //该线程会一直阻塞,直到有新的客户机加入,代码才会继续往下走  
  48.                 socket = mServer.accept();  
  49.                 //有新的客户机加入后,则创建一个新的SocketThread线程对象  
  50.                 SocketThread thread = new SocketThread(socket, socketID++);  
  51.                 thread.start();  
  52.                 //将该线程添加到线程队列  
  53.                 mThreadList.add(thread);  
  54.             }  
  55.               
  56.         } catch (Exception e) {  
  57.             e.printStackTrace();  
  58.         }  
  59.     }  
  60.       
  61.     /** 
  62.      * 开启推送消息线程,如果mMsgList中有SocketMessage,则把该消息推送到Android客户机 
  63.      */  
  64.     public void startSendMessageThread() {  
  65.         new Thread(){  
  66.             @Override  
  67.             public void run() {  
  68.                 super.run();  
  69.                 try {  
  70.                     /*如果isStartServer=true,则说明SocketServer已启动, 
  71.                     用一个循环来检测消息队列中是否有消息,如果有,则推送消息到相应的客户机*/  
  72.                     while(isStartServer) {  
  73.                         //判断消息队列中的长度是否大于0,大于0则说明消息队列不为空  
  74.                         if(mMsgList.size() > 0) {  
  75.                             //读取消息队列中的第一个消息  
  76.                             SocketMessage from = mMsgList.get(0);  
  77.                             for(SocketThread to : mThreadList) {  
  78.                                 if(to.socketID == from.to) {  
  79.                                     BufferedWriter writer = to.writer;  
  80.                                     JSONObject json = new JSONObject();  
  81.                                     json.put("from", from.from);  
  82.                                     json.put("msg", from.msg);  
  83.                                     json.put("time", from.time);  
  84.                                     //writer写进json中的字符串数据,末尾记得加换行符:"\n",否则在客户机端无法识别  
  85.                                     //因为BufferedReader.readLine()方法是根据换行符来读取一行的  
  86.                                     writer.write(json.toString()+"\n");  
  87.                                     //调用flush()方法,刷新流缓冲,把消息推送到手机端  
  88.                                     writer.flush();  
  89.                                     System.out.println("推送消息成功:"+from.msg+">> to socketID:"+from.to);  
  90.                                     break;  
  91.                                 }  
  92.                             }  
  93.                             //每推送一条消息之后,就要在消息队列中移除该消息  
  94.                             mMsgList.remove(0);  
  95.                         }  
  96.                         Thread.sleep(200);  
  97.                     }  
  98.                 } catch (Exception e) {  
  99.                     e.printStackTrace();  
  100.                 }  
  101.             }  
  102.         }.start();  
  103.     }  
  104.       
  105.     /** 
  106.      * 定义一个SocketThread类,用于接收消息 
  107.      * 
  108.      */  
  109.     public class SocketThread extends Thread {  
  110.           
  111.         public int socketID;  
  112.         public Socket socket;//Socket用于获取输入流、输出流  
  113.         public BufferedWriter writer;//BufferedWriter 用于推送消息  
  114.         public BufferedReader reader;//BufferedReader 用于接收消息  
  115.           
  116.         public SocketThread(Socket socket, int count) {  
  117.             socketID = count;  
  118.             this.socket = socket;  
  119.             System.out.println("新增一台客户机,socketID:"+socketID);  
  120.         }  
  121.           
  122.         @Override  
  123.         public void run() {  
  124.             super.run();  
  125.   
  126.             try {  
  127.                 //初始化BufferedReader  
  128.                 reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));  
  129.                 //初始化BufferedWriter  
  130.                 writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8"));  
  131.                 //如果isStartServer=true,则说明SocketServer已经启动,  
  132.                 //现在需要用一个循环来不断接收来自客户机的消息,并作其他处理  
  133.                 while(isStartServer) {  
  134.                     //先判断reader是否已经准备好  
  135.                     if(reader.ready()) {  
  136.                         /*读取一行字符串,读取的内容来自于客户机 
  137.                         reader.readLine()方法是一个阻塞方法, 
  138.                         从调用这个方法开始,该线程会一直处于阻塞状态, 
  139.                         直到接收到新的消息,代码才会往下走*/  
  140.                         String data = reader.readLine();  
  141.                         //讲data作为json对象的内容,创建一个json对象  
  142.                         JSONObject json = new JSONObject(data);  
  143.                         //创建一个SocketMessage对象,用于接收json中的数据  
  144.                         SocketMessage msg = new SocketMessage();  
  145.                         msg.to = json.getInt("to");  
  146.                         msg.msg = json.getString("msg");  
  147.                         msg.from = socketID;  
  148.                         msg.time = getTime(System.currentTimeMillis());  
  149.                         //接收到一条消息后,将该消息添加到消息队列mMsgList  
  150.                         mMsgList.add(msg);  
  151.                         System.out.println("收到一条消息:"+json.getString("msg")+" >>>> to socketID:"+json.getInt("to"));  
  152.                     }  
  153.                     //睡眠100ms,每100ms检测一次是否有接收到消息  
  154.                     Thread.sleep(100);  
  155.                 }  
  156.                   
  157.             } catch (Exception e) {  
  158.                 e.printStackTrace();  
  159.             }    
  160.               
  161.         }  
  162.     }  
  163.     /** 
  164.      * 获取指定格式的时间字符串,通过毫秒转换日期 
  165.      * @param millTime 
  166.      */  
  167.     private String getTime(long millTime) {  
  168.         Date d = new Date(millTime);  
  169.         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
  170.         return sdf.format(d);  
  171.     }  
  172.     public static void main(String[] args) {  
  173.         MyServer server = new MyServer();  
  174.         server.startSocket();  
  175.     }  
  176.   
  177. }  

2.SocketClient工程

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

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

[html] view plain copy
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     tools:context=".MainActivity"  
  6.     android:orientation="vertical" >  
  7.       
  8.     <LinearLayout   
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="wrap_content"  
  11.         android:orientation="horizontal">  
  12.         <EditText   
  13.             android:id="@+id/ip_edt"  
  14.             android:layout_width="0dp"  
  15.             android:layout_height="wrap_content"  
  16.             android:layout_weight="1"  
  17.             android:hint="ip"  
  18.             android:text="172.16.1.200"/>  
  19.         <EditText   
  20.             android:id="@+id/port_edt"  
  21.             android:layout_width="0dp"  
  22.             android:layout_height="wrap_content"  
  23.             android:layout_weight="1"  
  24.             android:hint="port"  
  25.             android:text="2000"/>  
  26.     </LinearLayout>  
  27.     <Button   
  28.         android:id="@+id/start_btn"  
  29.         android:layout_width="match_parent"  
  30.         android:layout_height="wrap_content"  
  31.         android:text="start"/>  
  32.     <EditText   
  33.         android:id="@+id/socket_id_edt"  
  34.         android:layout_width="match_parent"  
  35.         android:layout_height="wrap_content"  
  36.         android:hint="socketID"/>  
  37.       
  38.   
  39.       
  40.     <EditText   
  41.         android:id="@+id/msg_edt"  
  42.         android:layout_width="match_parent"  
  43.         android:layout_height="wrap_content"  
  44.         android:minLines="5"  
  45.         android:hint="content"  
  46.         android:gravity="top"  
  47.         />  
  48.     <Button   
  49.         android:id="@+id/send_btn"  
  50.         android:layout_width="match_parent"  
  51.         android:layout_height="wrap_content"  
  52.         android:text="send"/>  
  53.     <TextView   
  54.         android:id="@+id/console_txt"  
  55.         android:layout_width="match_parent"  
  56.         android:layout_height="0dp"  
  57.         android:layout_weight="1"/>      
  58.     <Button   
  59.         android:id="@+id/clear_btn"  
  60.         android:layout_width="match_parent"  
  61.         android:layout_height="wrap_content"  
  62.         android:text="clear"/>  
  63. </LinearLayout>  

效果图:



MainActivity.java类:

[java] view plain copy
  1. package com.jimstin.socketclient;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.BufferedWriter;  
  5. import java.io.IOException;  
  6. import java.io.InputStreamReader;  
  7. import java.io.OutputStreamWriter;  
  8. import java.net.Socket;  
  9. import java.net.UnknownHostException;  
  10. import java.text.SimpleDateFormat;  
  11. import java.util.Date;  
  12.   
  13. import org.json.JSONObject;  
  14.   
  15. import com.tencent.stat.MtaSDkException;  
  16. import com.tencent.stat.StatConfig;  
  17. import com.tencent.stat.StatService;  
  18.   
  19. import android.R.integer;  
  20. import android.os.AsyncTask;  
  21. import android.os.Bundle;  
  22. import android.os.Handler;  
  23. import android.os.Message;  
  24. import android.util.Log;  
  25. import android.view.View;  
  26. import android.view.View.OnClickListener;  
  27. import android.widget.EditText;  
  28. import android.widget.TextView;  
  29. import android.widget.Toast;  
  30. import android.app.Activity;  
  31.   
  32. public class MainActivity extends Activity implements OnClickListener {  
  33.   
  34.     private EditText mIPEdt, mPortEdt, mSocketIDEdt, mMessageEdt;  
  35.     private static TextView mConsoleTxt;  
  36.       
  37.     private static StringBuffer mConsoleStr = new StringBuffer();  
  38.     private Socket mSocket;  
  39.     private boolean isStartRecieveMsg;  
  40.       
  41.     private SocketHandler mHandler;  
  42.     protected BufferedReader mReader;//BufferedWriter 用于推送消息  
  43.     protected BufferedWriter mWriter;//BufferedReader 用于接收消息  
  44.       
  45.     @Override  
  46.     protected void onCreate(Bundle savedInstanceState) {  
  47.         super.onCreate(savedInstanceState);  
  48.         setContentView(R.layout.activity_main);  
  49.         initView();  
  50.     }  
  51.   
  52.     private void initView() {  
  53.         mIPEdt = (EditText) findViewById(R.id.ip_edt);  
  54.         mPortEdt = (EditText) findViewById(R.id.port_edt);  
  55.         mSocketIDEdt = (EditText) findViewById(R.id.socket_id_edt);  
  56.         mMessageEdt = (EditText) findViewById(R.id.msg_edt);  
  57.         mConsoleTxt = (TextView) findViewById(R.id.console_txt);  
  58.         findViewById(R.id.start_btn).setOnClickListener(this);  
  59.         findViewById(R.id.send_btn).setOnClickListener(this);  
  60.         findViewById(R.id.clear_btn).setOnClickListener(this);  
  61.         mHandler = new SocketHandler();  
  62.     }  
  63.   
  64.     /** 
  65.      * 初始化socket 
  66.      */  
  67.     private void initSocket() {  
  68.         //新建一个线程,用于初始化socket和检测是否有接收到新的消息  
  69.         Thread thread = new Thread(new Runnable() {  
  70.               
  71.             @Override  
  72.             public void run() {  
  73.                 String ip = mIPEdt.getText().toString();//IP  
  74.                 int port = Integer.parseInt(mPortEdt.getText().toString());//Socket  
  75.                   
  76.                 try {  
  77.                     isStartRecieveMsg = true;  
  78.                     mSocket = new Socket(ip, port);  
  79.                     mReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream(), "utf-8"));  
  80.                     mWriter = new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream(), "utf-8"));  
  81.                     while(isStartRecieveMsg) {  
  82.                         if(mReader.ready()) {  
  83.                             /*读取一行字符串,读取的内容来自于客户机 
  84.                             reader.readLine()方法是一个阻塞方法, 
  85.                             从调用这个方法开始,该线程会一直处于阻塞状态, 
  86.                             直到接收到新的消息,代码才会往下走*/  
  87.                             String data = mReader.readLine();  
  88.                             //handler发送消息,在handleMessage()方法中接收  
  89.                             mHandler.obtainMessage(0, data).sendToTarget();  
  90.                         }  
  91.                         Thread.sleep(200);  
  92.                     }  
  93.                     mWriter.close();  
  94.                     mReader.close();  
  95.                     mSocket.close();  
  96.                 } catch (Exception e) {  
  97.                     e.printStackTrace();  
  98.                 }   
  99.             }  
  100.         });  
  101.         thread.start();  
  102.     }  
  103.       
  104.     @Override  
  105.     public void onClick(View v) {  
  106.         switch (v.getId()) {  
  107.         case R.id.send_btn:  
  108.             send();  
  109.             break;  
  110.         case R.id.clear_btn:  
  111.             mConsoleStr.delete(0, mConsoleStr.length());  
  112.             mConsoleTxt.setText(mConsoleStr.toString());  
  113.             break;  
  114.         case R.id.start_btn:  
  115.             if(!isStartRecieveMsg) {  
  116.                 initSocket();  
  117.             }  
  118.             break;  
  119.         default:  
  120.             break;  
  121.         }  
  122.     }  
  123.   
  124.     /** 
  125.      * 发送 
  126.      */  
  127.     private void send() {  
  128.         new AsyncTask<String, Integer, String>() {  
  129.   
  130.             @Override  
  131.             protected String doInBackground(String... params) {  
  132.                 sendMsg();  
  133.                 return null;  
  134.             }  
  135.         }.execute();  
  136.     }  
  137.     /** 
  138.      * 发送消息 
  139.      */  
  140.     protected void sendMsg() {  
  141.         try {  
  142.             String socketID = mSocketIDEdt.getText().toString().trim();  
  143.             String msg = mMessageEdt.getText().toString().trim();  
  144.             JSONObject json = new JSONObject();  
  145.             json.put("to", socketID);  
  146.             json.put("msg", msg);  
  147.             mWriter.write(json.toString()+"\n");  
  148.             mWriter.flush();  
  149.             mConsoleStr.append("我:" +msg+"   "+getTime(System.currentTimeMillis())+"\n");  
  150.             mConsoleTxt.setText(mConsoleStr);  
  151.         } catch (Exception e) {  
  152.             e.printStackTrace();  
  153.         }  
  154.     }  
  155.   
  156.     static class SocketHandler extends Handler {  
  157.           
  158.         @Override  
  159.         public void handleMessage(Message msg) {  
  160.             // TODO Auto-generated method stub  
  161.             super.handleMessage(msg);  
  162.             switch (msg.what) {  
  163.             case 0:  
  164.                 try {  
  165.                     //将handler中发送过来的消息创建json对象  
  166.                     JSONObject json = new JSONObject((String)msg.obj);  
  167.                     mConsoleStr.append(json.getString("from")+":" +json.getString("msg")+"   "+getTime(System.currentTimeMillis())+"\n");  
  168.                     //将json数据显示在TextView中  
  169.                     mConsoleTxt.setText(mConsoleStr);  
  170.                 } catch (Exception e) {  
  171.                     e.printStackTrace();  
  172.                 }  
  173.                   
  174.                 break;  
  175.   
  176.             default:  
  177.                 break;  
  178.             }  
  179.         }  
  180.     }  
  181.       
  182.     @Override  
  183.     public void onBackPressed() {  
  184.         super.onBackPressed();  
  185.         isStartRecieveMsg = false;  
  186.     }  
  187.       
  188.     private static String getTime(long millTime) {  
  189.         Date d = new Date(millTime);  
  190.         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
  191.         return sdf.format(d);  
  192.     }  
  193.       
  194. }  

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


资源下载地址:http://download.csdn.NET/detail/zhangjm_123/8258019


注意:先把Android端的apk分别安装到两台手机上面,再运行SocketServer,点击SocketClient的Start,在SocketServer的控制台上面就可以看到有新的客户机增加了(确保IP地址和端口的正确前提下)。然后输入socketID(socketID表示Android客户机的一个账号,就像QQ号一样,从0开始,一个socketID表示一个客户机)和content,点击send就可以发送消息了。

效果图请见Android socket 编程 实现消息推送(一)

有问题的欢迎留言。

5
0
0 0
原创粉丝点击