安卓 socket 的基本使用(服务端、客户端)

助手阿喜 2022年9月23日09:54:08Android Studio Java Android Java评论阅读102次4830字阅读16分6秒

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

下面先简单介绍下 Socket 的使用。

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

https://developer.android.google.cn/reference/kotlin/java/net/Socket

https://developer.android.google.cn/reference/kotlin/java/net/ServerSocket

客户端使用

这里先简单地介绍下 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 的使用,可以参考下面的文章,我觉得写的非常好:

https://www.jianshu.com/p/fb4dfab4eec1

另外,如果读者想继续了解我的《自定义安卓 socket 通信模块系列》文章,可以点击链接跳转查看哦!

end
————————————————
版权声明:本文为CSDN博主「捡一晌贪欢」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lfq88/article/details/118028400

 

 

 

 

 

 

 

 

 

 

 

 

 

weinxin
扫码入群
加群获取附件资源,请查看教程说的资源名称查找,也可咨询问题,阿喜有空的时候会帮忙回复,并非时刻在线。
助手阿喜