• IPC 之 Socket 的使用


    一、概述

      我们知道在开发中,即时通讯、设备间的通信都是使用 Socket 实现,那当然用它来实现进程间通信更是不成问题。Socket 即套接字,是一个对 TCP / IP协议进行封装 的编程调用接口(API) 。通过Socket,我们才能在 Andorid 平台上通过 TCP/IP 协议进行开发。Socket 不是一种协议,而是一个编程调用接口(API),属于传输层(主要解决数据如何在网络中传输)。

      Socket的使用类型主要有两种:

    • 流套接字(streamsocket) :基于 TCP 协议,采用 流的方式 提供可靠的字节流服务
    • 数据报套接字(datagramsocket):基于 UDP 协议,采用 数据报文 提供数据打包发送的服务

      对于 TCP 和 UDP 的讲述,这里就不做说明。

    二、Socket 实现 IPC

      IPC 中两个重要角色: 客户端和服务端,示例工程仍旧是两个工程。我们先看服务端如何使用 Socket :

      1. 服务端

      服务端 Service 完整代码:

    public class SocketService extends Service {
    
        private String[] mDefinedMsg = new String[]{"你好啊,哈哈", "请问你叫什么名字啊?", "今天北京天气不错啊", "你知道吗,我可以和多个人同时聊天", "给你讲个笑话吧,爱笑的人运气不会太差。"};
    
        int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
        int KEEP_ALIVE_TIME = 1;
        TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
        BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>();
        ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES, NUMBER_OF_CORES * 2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, taskQueue);
    
        private boolean isServiceDestroy = false;
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            executorService.execute(new TcpServer());
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            isServiceDestroy = true;
        }
    
        private class TcpServer implements Runnable {
    
            @Override
            public void run() {
                ServerSocket serverSocket = null;
                try {
                    //接听本地8688接口
                    serverSocket = new ServerSocket(8688);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                while (!isServiceDestroy && serverSocket != null) {
                    //接受客户端请求
                    try {
                        Socket client = serverSocket.accept();
                        executorService.execute(new TalkWithClient(client));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        private class TalkWithClient implements Runnable {
    
            private Socket socket;
    
            TalkWithClient(Socket socket) {
                this.socket = socket;
            }
    
            @Override
            public void run() {
                try {
                    //用于接收客户端信息
                    BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    //用于向客户端发送信息
                    PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
                    out.println("欢迎来到聊天室");
                    while (!isServiceDestroy) {
                        String info = in.readLine();
                        if (info == null) { //客户端断开连接
                            break;
                        }
                        Log.i("CLIENT_INFO", info);
                        int i = new Random().nextInt(mDefinedMsg.length);
                        String msg = mDefinedMsg[i];
                        out.println(msg);
                        Log.i("Server_INFO", msg);
    
                    }
                    out.close();
                    in.close();
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

      在服务端通过循环的方式轮询是否有客户端接入,并判断服务一旦停止,停止循环。 accpet() 方法在没有客户端接入时,会阻塞当前线程,等到有客户端接入时会继续执行。

     while (!isServiceDestroy && serverSocket != null) {
              //接受客户端请求
              try {
                 Socket client = serverSocket.accept();
                 executorService.execute(new TalkWithClient(client));
              } catch (IOException e) {
              e.printStackTrace();
          }
     }

      TalkWithClient 是一个 Runnable 实现类,用来与客户端进行交互。通过循环的方式读取客户端传递过来的信息,代码中 readLine() 方法也会阻塞,直到客户端有消息过来继续执行。

    while (!isServiceDestroy) {
                        String info = in.readLine();
                        if (info == null) { //客户端断开连接
                            break;
                        }
                        Log.i("CLIENT_INFO", info);
                        int i = new Random().nextInt(mDefinedMsg.length);
                        String msg = mDefinedMsg[i];
                        out.println(msg);
                        Log.i("Server_INFO", msg);
    
                    }

      2. 客户端

      客户端完整代码:

      

    public class MainActivity extends AppCompatActivity {
    
        private static final int MESSAGE_SOCKET_CONNECTED = 1;
        private static final int MESSAGE_RECEIVE_NEW_MSG = 2;
    
        int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
        int KEEP_ALIVE_TIME = 1;
        TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
        BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>();
        ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES, NUMBER_OF_CORES * 2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, taskQueue);
    
        private Socket mClientSocket;
        private PrintWriter mPrintWriter;
        @SuppressLint("HandlerLeak")
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MESSAGE_SOCKET_CONNECTED:
                        btnSend.setEnabled(true);
                        break;
                    case MESSAGE_RECEIVE_NEW_MSG:
                        msgContainer.setText(String.format("%s%s", msgContainer.getText(), msg.obj));
                        break;
                }
            }
        };
        private TextView msgContainer;
        private EditText etMsg;
        private TextView btnSend;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            msgContainer = findViewById(R.id.msg_container);
            etMsg = findViewById(R.id.msg);
            btnSend = findViewById(R.id.btn_send);
            btnSend.setEnabled(false);
    
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    connectTCPServer();
                }
            });
        }
    
        @Override
        protected void onDestroy() {
            if (mClientSocket != null) {
                try {
                    mClientSocket.shutdownInput();
                    mClientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            super.onDestroy();
        }
    
        public void sendMsg(View view) {
            final String msg = etMsg.getText().toString();
            if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        mPrintWriter.println(msg);
                    }
                });
                etMsg.setText("");
                String time = formatTimes(System.currentTimeMillis());
                String showMsg = "client" + time + ":" + msg + "
    ";
                msgContainer.setText(String.format("%s%s", msgContainer.getText(), showMsg));
            }
        }
    
        private void connectTCPServer() {
            Socket socket = null;
            while (socket == null) {
                try {
                    socket = new Socket("localhost", 8688);
                    mClientSocket = socket;
                    mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(mClientSocket.getOutputStream())), true);
                    mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
                } catch (IOException e) {
                    SystemClock.sleep(1000);
                    //retry
                }
            }
            //接收服务器消息
            try {
                BufferedReader br = new BufferedReader(new InputStreamReader(mClientSocket.getInputStream()));
                while (!MainActivity.this.isFinishing()) {
                    String msg = br.readLine();
                    Log.i("CLIENT_RECEIVE", msg);
                    if (msg != null) {
                        String time = formatTimes(System.currentTimeMillis());
                        String showMsg = "server" + time + ":" + msg + "
    ";
                        mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showMsg).sendToTarget();
                    }
                }
                mPrintWriter.close();
                br.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @SuppressLint("SimpleDateFormat")
        private String formatTimes(long millis) {
            return "(" + new SimpleDateFormat("HH:mm:ss").format(new Date(millis)) + ")";
        }
    }

      连接服务端:

      connectTCPServer() 方法是用来连接服务端以及接收服务器消息,接收服务器消息仍然通过循环方式轮询。

      注意:

        Socket 实现 IPC 是网络操作,所以一定记得声明权限:

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

      最终能够进行传输数据,这里数据可以是任意数据,示例中只是传递了字符串。效果:

          

      示例工程代码:

       链接: https://pan.baidu.com/s/1Dq710Z4-vVsrNvgGUky09w 密码: 52fv

     

  • 相关阅读:
    查询SGA,PGA pool 内存分配情况
    为2229岁的人解释一下什么叫工作
    关于log的一些脚本
    关于ARM公司的cortex系列
    git reset 小结
    git push 小结
    git push 小结
    关于ubuntu的aptget 包
    TTL接口 液晶屏 与 LVDS接口 液晶屏的 区别
    git reset 小结
  • 原文地址:https://www.cnblogs.com/aimqqroad-13/p/8991559.html
Copyright © 2020-2023  润新知