Android 简单的UDP通信

作者: iot_xc | 来源:发表于2019-07-11 18:04 被阅读4次

    本来公司想做不经过服务器转发,两个设备进行P2P通信的, 经过调研发现好像只有电信的4G可以实现打洞, 移动和联通都不能直接进行P2P。然后就写了个demo测试一下udp通信。查询网上的资料都是已经知道客户端和服务端(其实UDP没有服务端之说)的地址直接进行通信,而我要进行的是两个客户端事先不知道对方的ip和port,那应该怎么通信呢?肯定要有一个中间者去将它们联系起来啊, 具体的做法是:
    一个服务端S, 一个客户端A, 一个客户端B
    A去登陆服务器,这时服务器可以得到A的地址和端口, 并保存下来

    A登陆.jpg

    B也去登陆服务器,同样服务器保存了B的地址和端口


    B登陆.jpg

    这时A去获取在线的设备,服务器会把在线的ip和port返回,A就有了B的信息,这时就可以给B发送消息了


    A向B发消息.jpg
    B接收到A的消息.jpg B发送消息给A.jpg
    A收到B的消息.jpg

    客户端代码:

    class UdpThread : Thread(), Runnable {
    
        //定义一个socket
        private lateinit var socket: DatagramSocket
    
        //服务器的地址和端口号
        private var serverAddress = "192.168.1.17"
        private var serverPort = 9000
    
        //需要进行通信的客户端ip和port
        private var clientIP = ""
        private var clientPort = 1
    
        private var socketListener: SocketListener? = null
    
        init {
            try {
                //初始化一个socket, 并监听6000端口
                socket = DatagramSocket(6000)
                Log.d("socket", "init socket")
            } catch (e: SocketException) {
                e.printStackTrace()
                Log.d("socket", e.message)
            }
        }
    
        fun setSocketListener(socketListener: SocketListener) {
            this.socketListener = socketListener
        }
    
        fun sendPackData(messageBean: MessageBean, address: String, port: Int) {
            val packs = Gson().toJson(messageBean).toByteArray()
            try {
                socket.send(DatagramPacket(packs, packs.size, InetAddress.getByName(address), port))
                socketListener?.sendSocketData(messageBean.toString())
            } catch (io: IOException) {
                io.printStackTrace()
                Log.d("socket", io.message)
                socketListener?.error(io)
            }
        }
    
        override fun run() {
            super.run()
            Log.d("socket", "start run")
    
            try {
                while (true) {
                    //接收数据
                    val receiveBytes = ByteArray(1024)
                    val packet = DatagramPacket(receiveBytes, receiveBytes.size)
                    socket.receive(packet)
    
                    //解析数据
                    val receiveData = packet.data
                    val json = String(Utils.subBytes(receiveData, 0, packet.length))
                    val messageBean = Gson().fromJson(json, MessageBean::class.java)
                    when (messageBean.protocol) {
                        //登陆结果
                        Cons.PROTOCOL_LOGIN_ACK -> socketListener?.receiveSocketData(messageBean.data)
                        //获取在线设备结果
                        Cons.PROTOCOL_GET_ONLINE_ACK -> socketListener?.receiveSocketData(messageBean.data)
                        //请求连接结果
                        Cons.PROTOCOL_CONNECT_ACK -> socketListener?.receiveSocketData(messageBean.data)
                        else -> socketListener?.receiveSocketData(messageBean.data)
                    }
                }
            } catch (se: SocketException) {
                se.printStackTrace()
                Log.d("socket", se.message)
                socketListener?.error(se)
            } catch (io: IOException) {
                io.printStackTrace()
                Log.d("socket", io.message)
                socketListener?.error(io)
            } finally {
                Log.d("socket", "close")
                socket.close()
            }
    
        }
    
        interface SocketListener {
            /**
             * 接收到的数据
             */
            fun receiveSocketData(socketData: String)
    
            /**
             * 发送数据
             */
            fun sendSocketData(packs: String)
    
            /**
             * 发生错误
             */
            fun error(e: Throwable)
        }
    }
    

    服务端代码:

    public class UdpServerThread extends Thread implements Runnable {
    
    
        // 数据报套接字
        private DatagramSocket datagramSocket;
        // 用以接收数据报
        private DatagramPacket datagramPacket;
    
        private Map<String, String> loginMap = new HashMap<>();
    
        public UdpServerThread() {
            try {
                datagramSocket = new DatagramSocket(9000);
                System.out.println("Server Start and listenering 9000");
            } catch (SocketException e) {
                e.printStackTrace();
                System.out.println(e.getMessage());
            }
        }
    
        @Override
        public void run() {
            super.run();
            try {
                while (true){
                    //接收数据包
                    byte[] receiveData = new byte[1024];
                    datagramPacket = new DatagramPacket(receiveData, receiveData.length);
                    datagramSocket.receive(datagramPacket);
                    String ip = ((InetSocketAddress)datagramPacket.getSocketAddress()).getAddress().getHostAddress();
                    int port = datagramPacket.getPort();
    
                    byte[] datas = datagramPacket.getData();
                    String json = new String(Utils.subBytes(datas, 0, datagramPacket.getLength()));
                    System.out.println(json);
                    MessageBean messageBean = new Gson().fromJson(json, MessageBean.class);
                    //登陆
                    if (messageBean.getProtocol() == Cons.PROTOCOL_LOGIN){
                        boolean isLogin = false;
                        for (Map.Entry<String, String> entry : loginMap.entrySet()) {
                            if (Integer.valueOf(entry.getKey()) == messageBean.getId()){
                                isLogin = true;
                                break;
                            }
                        }
                        if (!isLogin){
                            //保存登陆信息
                            loginMap.put(String.valueOf(messageBean.getId()), ip + ":" + port);
                            String packs = new Gson().toJson(new MessageBean(0, Cons.PROTOCOL_LOGIN_ACK, "login success:" + messageBean.getId()));
                            sendPacketData(packs.getBytes(), ip, port);
                        }else{
                            String packs = new Gson().toJson(new MessageBean(0, Cons.PROTOCOL_LOGIN_ACK, "already login:" + messageBean.getId()));
                            sendPacketData(packs.getBytes(), ip, port);
                        }
                    }else if (messageBean.getProtocol() == Cons.PROTOCOL_GET_ONLINE){
                        String clients = Utils.getMapToString(loginMap);
                        String packts = new Gson().toJson(new MessageBean(0, Cons.PROTOCOL_GET_ONLINE_ACK, clients));
                        sendPacketData(packts.getBytes(), ip, port);
                    }
                }
            }catch (SocketException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 关闭socket
                if (datagramSocket != null) {
                    datagramSocket.close();
                }
            }
        }
    
        private void sendPacketData(byte[] packs, String address, int port) {
            try {
                datagramSocket.send(new DatagramPacket(packs, packs.length, InetAddress.getByName(address), port));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    demo地址: https://pan.baidu.com/s/1PlhVU60QGH0ViwvcqHEuaQ 提取码: deus

    相关文章

      网友评论

        本文标题:Android 简单的UDP通信

        本文链接:https://www.haomeiwen.com/subject/htndkctx.html