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<Socket> mClientList = new ArrayList<Socket>();
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中的布局界面大致如下:

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

 @Override
public 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中添加网络权限:

<uses-permission android:name="android.permission.INTERNET"/>

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

<activity android:name=".MainActivity"
        android:screenOrientation="portrait">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <service android:name=".SocketServerService"
        android:process="hdc.socket.server"/>

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

//--获取ip地址
public static String getIpAddress() {
    if(TextUtils.isEmpty(mIpAddress)){
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> 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

参与评论 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

hdc_星空古路

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值