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();
                }
            }
        }
    }

這樣就完成了雙向通信了。

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