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. Android作为服务端,PC推送消息
  2. Android进程说明
  3. eoe android客户端源码剖析(一)动画启动界面
  4. Android中App可分配内存的大小
  5. android socket
  6. Android(IPC)进程间通讯1:详解Binder由来?
  7. Android即时通讯和sns开源项目汇总
  8. Android性能:内存篇之进程内存管理
  9. 编程回忆之Android回忆(Android(安卓)Socket编程)

随机推荐

  1. 在Mac OS X下安裝Android開發環境和Eclip
  2. 基于android的NFS根文件系统的制作
  3. android 在布局文件中报错:This text fie
  4. 新手在路上! Android(安卓)UI界面设计
  5. Android(安卓)Bitmap内存限制问题
  6. Android智能手机网络防火墙开发的经验心
  7. 关于android读取ICCID、IMSI和IMEI
  8. 每个Android开发者必须知道的资源集锦
  9. 图说Android的8年演变史
  10. android v7包 正常导入使用方法