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