android进程间通讯(3)–使用Socket

前言:本文记录android进程间通讯的另一种通讯方式–Socket。Socket也称“套接字”,是网络通讯中的概念,它分为流式套接字和用户数据报套接字两种,分别对应网络传输层的TCP和UDP协议。TCP协议是面向连接的协议,提供稳定的双向通讯功能,TCP连接的建立需要经过“三次握手”,具有很高的稳定性。UDP是无连接的,提供不稳定的单向通讯功能,当然UDP也可以实现双向通讯,UDP效率更高,但是不能保证数据一定能正确传输,存在丢包的问题。

一.Socket通讯模型

由于Socket可以使用TCP和UDP两种协议实现通讯,所以其基本通讯模型如图(图凑合看吧):

因为Socket是C/S结构,所以应用进程A和应用进程B一个作为服务器一个作为客户端,通过Socket实现双向通讯。比如进程A作为服务器,进程B作为客户端,Socket通过IP地址和端口发送数据经过网络解析,最终传输到客户端进程B。

二.Socket使用

下面将通过具体的项目来演示使用Socket来进行进程间通讯。使用Socket通讯,不仅可以实现同一台设备上的不同进程通讯,同时能实现不同设备间的进程通讯,因为这是基于网络的通讯。由于TCP协议比较常用,下面使用TCP协议通过Socket建立连接实现进程间通讯。

1.创建服务器

创建一个服务器的单例管理类,用来管理服务器的创建启动,接收客户端消息,发送消息至客户端以及断开连接等。具体代码如下:

public class SocketServerManager {private static SocketServerManager socketServerManager;private ServerSocket serverSocket;private List mClientList = new ArrayList();private ExecutorService mExecutors = null; // 线程池对象private SocketServerManager(){}public static SocketServerManager getInstance(){    if(socketServerManager==null){       synchronized (SocketServerManager.class){           if(socketServerManager==null){               socketServerManager=new SocketServerManager();           }       }    }    return socketServerManager;}public void startSocketServer(){    new Thread(new Runnable() {        @Override        public void run() {            startServerSync();        }    }).start();}private void startServerSync(){    try {        Log.i("socket_hdc "," server start");        serverSocket = new ServerSocket(9002);        serverSocket.getInetAddress().getHostAddress();        mExecutors = Executors.newCachedThreadPool(); // 创建线程池        Socket client = null;        /*         * 用死循环等待多个客户端的连接,连接一个就启动一个线程进行管理         */        while (true) {            client = serverSocket.accept();            Log.i("socket_hdc "," server get socket");            // 把客户端放入集合中            mClientList.add(client);            mExecutors.execute(new SocketHandle(client)); // 启动一个线程,用以守候从客户端发来的消息        }    } catch (Exception e) {        e.printStackTrace();    }}class SocketHandle implements Runnable {    private Socket socket;    private BufferedReader bufferedReader = null;    private BufferedWriter bufferedWriter = null;    private String message = "";    public SocketHandle(Socket socket) {        this.socket = socket;        try {            bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));// 获得输入流对象            bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));// 创建输出流对象            // 客户端只要一连到服务器,便发送连接成功的信息            message = "服务器地址:" + this.socket.getInetAddress();            this.sendMessage(message);            message = "当前连接的客户端总数:" + mClientList.size();            this.sendMessage(message);        } catch (IOException e) {            e.printStackTrace();        }    }    @Override    public void run() {        try {            while (true) {                if ((message = bufferedReader.readLine()) != null) {                    if(message.equals("quit")){                        closeSocket();                        break;                    }                    // 接收客户端发过来的信息message,然后转发给客户端。                    message = "服务器收到 " + ":" + message;                    this.sendMessage(message);                }            }        } catch (Exception e) {            e.printStackTrace();        }    }    public void closeSocket() throws IOException {        mClientList.remove(socket);        bufferedReader.close();        bufferedWriter.close();        message = "主机:" + socket.getInetAddress() + "关闭连接\n目前在线:" + mClientList.size();        socket.close();        this.sendMessage(message);    }    public void sendMessage(String msg) {        try {            Log.i("socket_hdc"," server send msg "+msg);            bufferedWriter.write(msg);            bufferedWriter.newLine();            bufferedWriter.flush();        } catch (IOException e) {            e.printStackTrace();        }    }}
2.创建客户端

创建一个客户端的单例管理类,用来管理客户的创建连接,接收服务器发送的消息,发送消息至服务器以及断开连接等。具体代码如下:

public class SocketManager {private static SocketManager socketManager;private int port=9002;private Socket socket;private ExecutorService executorService;private BufferedReader bufferedReader = null;private BufferedWriter bufferedWriter = null;private SocketManager(){    executorService= Executors.newCachedThreadPool();}public static SocketManager getInstance(){    if(socketManager==null){        synchronized (SocketManager.class){            if(socketManager==null){                socketManager=new SocketManager();            }        }    }    return socketManager;}public void connectSocket(Context context, final Handler handler){    Thread thread =new Thread(new Runnable() {        @Override        public void run() {            try {                Log.i("socket_hdc","connect start");                socket =new Socket(DeviceInfoUtil.getIpAddress(),port);                Log.i("socket_hdc"," socket "+(socket==null));                bufferedReader =new BufferedReader(new InputStreamReader(socket.getInputStream()));// 创建输入流对象                bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));// 创建输出流对象                while (true) {//死循环守护,监控服务器发来的消息                    if (!socket.isClosed()) {//如果服务器没有关闭                        if (socket.isConnected()) {//连接正常                            if (!socket.isInputShutdown()) {//如果输入流没有断开                                String getLine;                                if ((getLine = bufferedReader.readLine()) != null) {//读取接收的信息                                    getLine += "\n";                                    Message message=new Message();                                    message.obj=getLine;                                    Log.i("socket_hdc",getLine);                                    message.what=MainActivity.SOCKET_MSG;                                    handler.sendMessage(message);//通知UI更新                                } else {                                }                            }                        }                    }                }            } catch (IOException e) {                e.printStackTrace();            }        }    });    thread.start();}public void sendMessage(final String msg) {    if(executorService!=null&&socket!=null){        executorService.execute(new Runnable() {            @Override            public void run() {                try {                    Log.i("socket_hdc"," send msg "+msg);                    bufferedWriter.write(msg);                    bufferedWriter.newLine();                    bufferedWriter.flush();                } catch (IOException e) {                    e.printStackTrace();                }            }        });    }}public void closeSocket() throws IOException {    bufferedReader.close();    bufferedWriter.close();    if(socket!=null){        socket.close();    }}
3.搭建主界面

在MainActivity中的布局界面大致如下:

各个按钮的点击事件如下代码所示:

 @Overridepublic void onClick(View view) {    switch (view.getId()){        case R.id.socket_server_start_btn://--启动服务器,即启动一个Service,创建SocketServer            Intent intent =new Intent(this,SocketServerService.class);            startService(intent);            break;        case R.id.socket_conn_btn://--创建间客户端Socket,建立连接            SocketManager.getInstance().connectSocket(this,mHandler);            break;        case R.id.socket_disconnect_btn://--关闭连接,通过发送quit消息通知服务器关闭            SocketManager.getInstance().sendMessage("quit");            try {                SocketManager.getInstance().closeSocket();            } catch (IOException e) {                e.printStackTrace();            }            break;        case R.id.socket_send_msg_btn://--客户端向服务器发送消息            String msg =socket_send_msg_et.getText().toString();            Log.i("socket_hdc"," btn send "+msg);            SocketManager.getInstance().sendMessage(msg);            break;    }}
4.AndroidManifest文件

由于Socket使用网络协议通讯,所以首先要在Manifest中添加网络权限:

既然是模拟进程间通讯,那么创建两个不同的进程,如下:

                                                

其中MainActivity在主进程中,SocketServerService的进程设置问hdc.socket.server。
在主进程中创建客户端Socket,在hdc.socket.server进程中创建服务器。通过客户端向服务器发送消息,服务器接收到消息后,再向客户端发送消息即反馈,客户端接收到服务器反馈的消息后,通过TextView来进行展示。需要注意的是,在手机上创建Socket的时候需要知道手机的IP地址,可以通过以下方法获取。

//--获取ip地址public static String getIpAddress() {    if(TextUtils.isEmpty(mIpAddress)){        try {            for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {                NetworkInterface intf = en.nextElement();                for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {                    InetAddress inetAddress = enumIpAddr.nextElement();                    if (!inetAddress.isLoopbackAddress()) {                        mIpAddress = inetAddress.getHostAddress().toString();                    }                }            }        } catch (SocketException ex) {            ex.printStackTrace();        }    }    return mIpAddress;}

总结:18年第一天上班,完结android进程间通讯的最后一个Socket通讯。Socket在android进程通讯中的使用不是很多,大多是通过Binder(http://blog.csdn.net/xingkong_hdc/article/details/79273005). 但是SystemService进程与Zygote进程之间是通过Socket的方式进行通讯的。
关于Socket的这篇Demo的完整项目,已分享到github:
https://github.com/xingkonghdc/SocketTest

更多相关文章

  1. eoe android客户端源码剖析(一)动画启动界面
  2. Android进程说明
  3. Android即时通讯和sns开源项目汇总
  4. Android(IPC)进程间通讯1:详解Binder由来?
  5. Android性能:内存篇之进程内存管理
  6. FirefoxOS 系统进程初步分析 底层系统继承自 android
  7. 微信开放平台开发第三方授权登陆(三):Android客户端
  8. 【整理一点资料】 Andorid的进程和线程模型
  9. 详解Android客户端与服务器交互方式

随机推荐

  1. Android 学习成品
  2. android点击文本框之外的地方隐藏键盘
  3. Android设置振铃
  4. Android(安卓)图片的加载与保存
  5. 改变Android中默认Dialog的样式
  6. android 2.2 eclipse
  7. Android开发实践 网络通信 URL、URLConne
  8. android editText 软键盘enter键图标的设
  9. Android debug.keystore的密码
  10. Android:网络框架android-async-http 1.4