Android Socket通信(四) -- UDP与TCP结合传输数据

系列文章:
Android Socket通信(一) – 初识与相遇
Android Socket通信(二) --UDP,单播,广播和多播(组播)
Android Socket通信(三) – TCP 配置和传递基础数据
Android Socket通信(四) – UDP与TCP结合传输数据

在这篇文章中,你将学习到:

  • 学习通过 UDP 获取不同设备的ip和端口
  • 通过 TCP 进行相互通信
  • 实践一个案例

今天要实现的效果:
在这里插入图片描述

一、案例分析

在前面几章中,我们已经学习了 tcp 和 udp 基础的方法;这一章中,我们对它进行一个总结,设想一个案例,即我想与B设备相互通信;
但是并不知道B设备的ip和端口,但我们可以通过 udp 发送广播,找到设备 ip 和端口,再进行 tcp 通信;大概流程如下:
在这里插入图片描述
客户端端通过 udp 广播 搜索设备,如果A或者B设备对广播的协议有响应,则发送自己的ip和端口给客户端,客户端拿到之后,再与之进行双向通信。

二、进行UDP通过获取IP

在第二章已经学习了 UDP 广播的用法,代码改动不大,发送广播那里,我们进行一个数据包装,采用 ByteBuffer,毕竟我们不想让每个人都与之通信。

客户端中发送广播:

    /**
     * 发送广播
     * @throws IOException
     */
    public static void sendBroadcast() throws IOException {
        DatagramSocket socket = new DatagramSocket();

        ByteBuffer byteBuffer = ByteBuffer.allocate(128);
        //发送特定数据
        byteBuffer.putInt(Constans.CMD_BROAD);
        byteBuffer.putInt(Constans.BROADCAST_PORT);
        DatagramPacket packet = new DatagramPacket(byteBuffer.array(),
                byteBuffer.position(),
                InetAddress.getByName(Constans.BROADCAST_IP),
                Constans.PORT);
        socket.send(packet);
        socket.close();
    }

通过广播,发送 cmd 和 要回送的 port ;接着,在服务端中,解析相应数据:

ByteBuffer byteBuffer = ByteBuffer.wrap(bytes,0,length);
  int cmd = byteBuffer.getInt();
  int responsePort = byteBuffer.getInt();
  System.out.println("客户端: " + ip + "\tport: " + port +"\t回送接口: "+responsePort);
  /**
   * 给客户端发送消息  cmd 必须匹配
   */
  if (Constans.CMD_BROAD == cmd) {
      ByteBuffer buffer = ByteBuffer.allocate(256);
      buffer.putInt(Constans.CMD_BRO_RESPONSE);
      buffer.putInt(Constans.TCP_PORT);
      buffer.put(("我是设备: "+ UUID.randomUUID().toString()).getBytes());
      System.out.println(buffer.position());
      DatagramPacket receivePacket = new DatagramPacket(buffer.array(),
              buffer.position(),
              packet.getAddress(), //目标地址
              responsePort);      //广播端口

      socket.send(receivePacket);
  }

当 cmd 是 CMD_BROAD 才回送数据,然后把 tcp 的端口也回送回去;然后回到 client 中的 listener 去监听:

ByteBuffer buffer = ByteBuffer.wrap(bytes,0,length);
int cmd = buffer.getInt();
if (Constans.CMD_BRO_RESPONSE == cmd) {
    int tcpPort = buffer.getInt();
    int pos = buffer.position();
    String msg = new String(bytes,pos,length - pos);
    System.out.println("监听到: " + ip + "\ttcpPort: " + tcpPort + "\t信息: " + msg+" "+length+" "+pos);

    if (msg.length() > 0) {
        DeviceInfo device = new DeviceInfo(ip, tcpPort, msg);
        devices.add(device);
    }
    //成功收取到一份
    searchLatch.countDown();
}

打印如下:
在这里插入图片描述
在这里插入图片描述

三、进行 TCP 双向通信

拿到 TCP 的设备之后,就可以进行通信了,代码基于第一章。
首先,我们先改动 客户端,让发送和接收分开:
发送

    /**
     * 发送数据
     * @param socket
     */
    static void sendData(Socket socket){
        try {
            //终端输入流
            BufferedReader osReader = new BufferedReader(new InputStreamReader(System.in));
            PrintStream ps = new PrintStream(socket.getOutputStream());
            boolean isFinish = false;
            do {
                String msg = osReader.readLine();
                ps.println(msg);
                if ("bye".equalsIgnoreCase(msg)){
                    isFinish = true;
                }

            }while (!isFinish);
            osReader.close();
            ps.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

接收

    /**
     * 监听数据
     */
    static class ReaderListener extends Thread{
        Socket socket;
        boolean isFinish = false;
        public ReaderListener(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            super.run();
            try {
                BufferedReader responseReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                do {
                    String response = responseReader.readLine();
                    //当服务器关闭连接了,则 IO 不再阻塞,会返回null
                    if (response == null){
                        System.out.println("连接断开");
                        break;
                    }else {
                        System.out.println(response);
                    }
                }while (!isFinish);
                responseReader.close();
            } catch (IOException e) {
               // e.printStackTrace();
            }finally {
                exit();
            }

        }
        public void exit(){
            isFinish =true;
            if (socket != null){
                try {
                    socket.close();
                    socket = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

这样,我们就能监听服务端的信息了,接着,我们需要改变一下服务端的;当终端也输入时,发送给其他客户端。
这里,我们也把发送和接收分开:
为了能并发,我们这里都是采用线程去处理:
发送

/**
     * 发送数据
     */
    class writerHandle {
        private PrintStream ps ;
        private ExecutorService executorService ;
        private boolean isFinish = false;
        public writerHandle(OutputStream os) {
            ps = new PrintStream(os);
            executorService = Executors.newSingleThreadExecutor();
        }

        public void exit(){
            isFinish = true;
            ps.close();
            executorService.shutdown();
        }
        public void sendMsg(String msg){
            executorService.execute(new sendSync(msg));
        }

        class sendSync implements Runnable{
            String str;

            public sendSync(String str) {
                this.str = str;
            }

            @Override
            public void run() {
                if (writerHandle.this.isFinish){
                    return;
                }
                ps.println(str);
            }
        }


    }

接收

/**
     * 数据读取监听类
     */
    class ReaderListener extends Thread{
        InputStream inputStream;
        boolean isFinish = false;
        public ReaderListener(InputStream inputStream) {
            this.inputStream = inputStream;
        }

        @Override
        public void run() {
            super.run();
            try {
                BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
                do {
                    String msg = br.readLine();
                    if (msg != null){
                        System.out.println("client: "+msg);
                    }else{
                        System.out.println("连接已断开");
                        break;
                    }

                } while (!isFinish);
            } catch (IOException e) {
               // e.printStackTrace();
            }finally {
                exit();
            }
        }

        public void exit(){
            isFinish = true;
            if (inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

这样就完成了双向通信了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章