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

本文详细介绍了基于Android客户端socket编程实现消息推送的过程,包括Server端和Client端的实现方式,涉及SocketMessage类、MyServer类以及SocketThread类的使用,以及Android客户端MainActivity的实现。实现了消息的接收、发送和显示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


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

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

所以在这里也分别创建了两个工程SocketServer和SocketClient
这里写图片描述



1.SocketServer工程

我们先来看一下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);  
        }  

    }  

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

资源下载地址:http://download.youkuaiyun.com/detail/zhangjm_123/8258019

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



本文转自:http://blog.youkuaiyun.com/zhangjm_123/article/details/41924151

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值