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