之前做了一个安卓和设备通讯的 Socket 封装模块,用于和设备进行数据交流,极大地解决了旧代码的复杂与混乱,并且提供了扩展性。接下来我会分好几篇博客慢慢介绍这个通讯模块的各个部分,一步一步的将想要的设计,实现成详细的功能!

下面先简略介绍下 Socket 的运用。

文档准备

Java 中的 socket 代码在 java.net 包下,能够在安卓官方文档下看看,不过是英文的:

developer.android.google.cn/reference/k…

developer.android.google.cn/reference/k…

客户端运用

这儿先简略地介绍下 Socket 客户端的用法,后面封装,毕竟和设备衔接就靠客户端了。

简略用法
public void onClick(View view){
        new Thread(){
            @Override
            public void run() {
                super.run();
                try {
                    //1.创立监听指定服务器地址以及指定服务器监听的端口号
                    Socket socket = new Socket("111.111.11.11", 12306);//111.111.11.11为我这个本机的IP地址,端口号为12306.
                    //2.拿到客户端的socket对象的输出流发送给服务器数据
                    OutputStream os = socket.getOutputStream();
                    //写入要发送给服务器的数据
                    os.write(et.getText().toString().getBytes());
                    os.flush();
                    socket.shutdownOutput();
                    //拿到socket的输入流,这儿存储的是服务器回来的数据
                    InputStream is = socket.getInputStream();
                    //解析服务器回来的数据
                    InputStreamReader reader = new InputStreamReader(is);
                    BufferedReader bufReader = new BufferedReader(reader);
                    String s = null;
                    final StringBuffer sb = new StringBuffer();
                    while((s = bufReader.readLine()) != null){
                        sb.append(s);
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            tv.setText(sb.toString());
                        }
                    });
                    //3、封闭IO资源(注:实际开发中需要放到finally中)
                    bufReader.close();
                    reader.close();
                    is.close();
                    os.close();
                    socket.close();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
简略封装

学完 Socket 地简略用法,下面我们把它封装到 Service 里边去。下面是简化的代码,只说 Socket 衔接和接纳,其他东西在后续内容里边慢慢说:

public class ConnectService extends Service {
    /** Socket相关,IP及端口号 **/
    private String  ip      = "192.168.1.110";
    private int     port    = 2050;
    private int     wait    = 5000;
    private Socket mSocket;
    private Thread rxThread;
    //初始化衔接
    public void setUpConnection() throws IOException {
        //封闭已有 socket
        stopExistSocket();
        //创立新的 socket
        createSocket();
        //发动接纳线程
        startRxThread();
    }
    private void stopExistSocket() throws IOException {
        if (mSocket != null) {
            try {
                mSocket.close();
                mSocket = null;
            } catch (IOException e) {
                e.printStackTrace();
                throw e;
            }
        }
    }
    private void createSocket() throws IOException {
        SocketAddress sa = new InetSocketAddress(ip, port);
        mSocket = new Socket();
        try{
            //设置信息及超时时间
            mSocket.connect(sa, wait);
            boolean isConnect = mSocket.isConnected();
            mSocket.setKeepAlive(isConnect);
        } catch(IOException e) {
            mSocket = null;
            throw e;
        }
    }
    private void startRxThread() {
        if (rxThread == null || !rxThread.isAlive()) {
            rxThread = initReceiveThread();
            rxThread.start();
        }
    }
    //接受线程
    private Thread initReceiveThread() {
        return new Thread(new Runnable() {
            @SuppressWarnings("ResultOfMethodCallIgnored")
            @Override
            public void run() {
                try {
                    InputStream is;
                    while (mSocket != null) {
                        is = mSocket.getInputStream();
                        byte[] ackbuf    = new byte[4];  //消息号
                        int ackrs       = is.read(ackbuf, 0, 4);
                        if (ackrs < 0) {
                            Thread.sleep(1000);
                            continue;
                        }
                        //TODO 自行处理数据
                        ...
                    }
                }catch(IOException e) {
                    e.printStackTrace();
                } catch(Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
简略讲讲

实际这儿就三步,封闭已有 socket、创立新的 socket、发动接纳线程,都很简略。假如说有什么缺点的话,就是没有合理的封闭流吧,有时间我再改改!

服务端代码

和设备衔接用不到 Socket 服务端,所以这儿我就写个简略的比如吧!封装了一个模仿的服务端,一看就懂:

public class SimulateUtil {
	public static class SocketServer extends Thread {
        private Socket remotePeer;
        private SocketServer(Socket remotePeer) {
            this.remotePeer = remotePeer;
        }
        public void run() {
            try {
                InputStream is = null;
                while(remotePeer!= null) {
                    isHxz = remotePeer.getInputStream();
                    //写的很大,能够写成常量
                    byte[] ackbuf = new byte[2560];
                    int len = is.read(ackbuf, 0, 2560);
                    if (len <= 0) {
                        Thread.sleep(1000);
                        continue;
                    }
                    //假装过一会回来数据
                    Thread.sleep(1000L + ((int) (Math.random() * 10)) * 100L);
                    remotePeer.getOutputStream().write("hey boy!".getBytes());
                }
            } catch(IOException e) {
                e.printStackTrace();
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
    }
	/**
     * 发动模仿进程
     */
    public static void startSimulate() {
        new Thread(() -> {
            try {
                ServerSocket serverSocket = new ServerSocket(54321);
                while (true) {
                    Socket remotePeer = serverSocket.accept();
                    new SocketServer(remotePeer).start();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

下面是运用方法,application 中发动就行:

SimulateUtil.startSimulate();

留意点:

  • 这儿的 IP 是本地地址(127.0.0.1)

  • 端口号我们设置的 54321

  • 封闭流什么的,能够参考下下面代码:

    	@Override
        public void run() {	
    		InputStreamReader reader = null;
            BufferedReader bufReader = null;
            OutputStream os = null; 
            try {
                reader = new InputStreamReader(socket.getInputStream());
                bufReader = new BufferedReader(reader);
                String s = null;
                StringBuffer sb = new StringBuffer();
                while((s = bufReader.readLine()) != null){
                    sb.append(s);
                }
                System.out.println("服务器:"+sb.toString());
                //封闭输入流
                socket.shutdownInput();
                //回来给客户端数据
                os = socket.getOutputStream();
                os.write(("我是服务端,客户端发给我的数据就是:"+sb.toString()).getBytes());
                os.flush();
                socket.shutdownOutput();
            } catch (IOException e2) {
                e2.printStackTrace();
            } finally{//封闭IO资源
                if(reader != null){
                    try {
                        reader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if(bufReader != null){
                    try {
                        bufReader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if(os != null){
                    try {
                        os.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    

结语

好了,这儿简略讲了一下 socket 的运用,用的都是 TCP 衔接,由于这儿是我写的《自定义安卓 socket 通讯模块系列》的一部分,所以更多的偏向了详细的运用,假如读者想要更多的了解 socket 的运用,能够参考下面的文章,我觉得写的非常好:

www.jianshu.com/p/fb4dfab4e…

别的,假如读者想继续了解我的《自定义安卓 socket 通讯模块系列》文章,能够点击链接跳转检查哦!

end