Android 网络(二) socket

一、一台手机向另外一台手机发送消息,类似QQ

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

手机1
手机2

实现过程主要分为Server端和Client端,Server端采用Java的编程,而Client端则用Android编程。所以在这里也分别创建了两个工程SocketServer和SocketClient。

1.SocketServer

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工程

SocketClient
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);
    }
    
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,793评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 上回我们讲到了dmzj漫画搜索爬虫(二),紧接着上一回的代码,我们继续进行深入的爬取分析,完成对于漫画的图片爬取。...
    浅浅的笑意阅读 3,686评论 4 5
  • 【导语】兄弟一生一起走,那些日子不再有!也许兄弟在身边,男人就更有一份自信,可并不是每个兄弟都是能够陪伴你到最后的...
    腼腆的蘑菇鱼阅读 487评论 0 0
  • 过去,收藏者的目光主要集中在字画和瓷器等主流收藏种类上,根雕艺术品受到冷落,一些根雕珍品也很难卖出高价。但是近年来...
    疯狂的木头阅读 367评论 2 5