Android Socket communication (1)-communication based on UDP protocol

Recently, the function needs to use the UDP protocol to communicate with the server. I haven't touched this one before, so I spent some time to understand and implement it. Let me record and share it.

First of all, we need to know that the UDP communication protocol is an implementation of Socket communication. Socket communication generally has two communication methods: based on TCP protocol and based on UDP protocol . The differences and advantages and disadvantages of the two will not be mentioned. Here we mainly talk about the implementation based on UDP protocol.

Fundamental

UDP-based communication is achieved through the java.net.DatagramSocket class. We commonly use connect(), disConnect(), send(), and receive() methods. We can easily distinguish the usage by the method name. Another thing to know is the DatagramPacket class. If DatagramSocket is compared to a pipeline, DatagramPacket is the item transported in the pipeline, that is, it is the carrier of data.

DatagramSocket:

public DatagramSocket()throws SocketException {
    this(0);
}

public DatagramSocket(int aPort)throws SocketException {
    checkPort(aPort);
    createSocket(aPort, Inet4Address.ANY);
}

public DatagramSocket(int aPort, InetAddress addr)throws SocketException {
    checkPort(aPort);
    createSocket(aPort, (addr == null) ? Inet4Address.ANY : addr);
}

For the three construction methods, we can specify the port and IP or not. When sending data, specify in the data packet.

DatagramPacket :

public DatagramPacket(byte[]data, int length) {…}
public DatagramPacket(byte[]data, int offset, int length) {…}
public DatagramPacket(byte[]data, int offset, int length, InetAddress host, int aPort) {…}
public DatagramPacket(byte[]data, int length, InetAddress host, int port) {…}

so we can see that ip and port can be set in the data packet, so it is ok if you don't specify it when connecting.

Basic operation

1. Connect new creates a DatagramSocket object, sets the port and IP, and connect();
2. send creates a DatagramPacket object, socket.send(packet) sends data;
3. Receive creates a DatagramPacket object, socket.receive(package) receives data;

Create a connection

Use the connect method to create a Socket connection

public void connect() {
    if (mSocket == null || mSocket.isClosed()) {
        try {
            //获取连接 ip:192.168.1.3  port:11069
            InetAddress address = InetAddress.getByName(Command.udp_address);
            mSocket = new DatagramSocket();
            mSocket.connect(address, Command.udp_port);
            messageQueue = new ArrayList < String > ();
            //开启接收线程
            mReceiveThread = new ReceiveThread();
            mReceiveThread.start();
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }
}

Send data regularly to keep communication

If you want to maintain communication, you must use threads. If you send data regularly, you can use Handler, or you can use AlarmManager. Just use Handler here.

When I wrote, I read a bunch of blogs. When sending, a new Thread is used to send data. After sending, the code for receiving data is written, and there is no recycling or what, it can only be recycled through GC. Of course, it is generally not Something is wrong, but there are actually big problems! Because my project requires some corresponding operations after receiving the response from the server, then, then, because every thread is waiting to receive data, after the server responds with a piece of data, every thread here responds (Reply to the server with a different piece of data), 10s create a thread to send data, the server will reply after a period of time, and then it explodes instantly!

Therefore, receiving and sending should be separated and cannot be blocked in the same thread. At first I thought of the principle and mechanism of Handler. I wanted to imitate it in an endless loop to fetch data from a queue. If there is data, send it. The idea is that it can be sent without problems, but when I want to remove it from the list after the sending is successful After the data, it will not work, the thread is blocked. The specific reason should be the deadlock, because the data list is defined in the main thread, and then it has been occupied by the worker thread, and the object cannot be obtained for the remove operation. After waiting for a certain period of time, it crashed directly (my analysis is this of).

Ok, then write two threads, one SendThread, one ReceiveThread, and 10s timing sending through Handler control.

Send thread:

public class SendThread extends Thread {
    @ Override
    public void run() {
        super.run();
        try {
            if (mSocket == null || mSocket.isClosed())
                return;
            if (messageQueue.size() < 1)
                return;
            //发送
            final String data = messageQueue.get(0);
            byte[]datas = data.getBytes();
            InetAddress address = InetAddress.getByName(Command.udp_address);
            final DatagramPacket packet = new DatagramPacket(datas, datas.length, address, Command.udp_port);
            mSocket.send(packet);
            Logs.e("ConnectManager", "send success data is:" + data);
            messageQueue.remove(0);

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

Receiving thread:

private class ReceiveThread extends Thread {
    @ Override
    public void run() {
        super.run();
        if (mSocket == null || mSocket.isClosed())
            return;
        try {
            byte datas[] = new byte[512];
            DatagramPacket packet = new DatagramPacket(datas, datas.length, address, Command.udp_port);
            mSocket.receive(packet);
            String receiveMsg = new String(packet.getData()).trim();
            Logs.e("ConnectManager", "receive msg data is:" + receiveMsg);
            mHandler.sendEmptyMessage(2);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

We can see that the data is only received once in ReceiveThread, so how to maintain communication? Receive messages? Handler! After receiving the message, we send a message through the Handler. What does this message do? Restart this thread, which means it will continue to receive data. In this case, it is guaranteed that only one thread is receiving data.

private Handler mHandler = new Handler() {
    @ Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        if (msg.what == 2) {
            mReceiveThread.interrupt();
            mReceiveThread = null;
            mReceiveThread = new ReceiveThread();
            mReceiveThread.start();
        }
    }
};

Similarly, sending data regularly is also realized by Handler. Send a message every 10s, let the socket send a data packet, and send yourself the same message sent 10s later, anyway, you control yourself.

Send a message:

public void sendPackageRegister() {
    String content = "(1001," + Command.deviceId + ",register)";
    messageQueue.add(content);
    mSendThread.interrupt();
    mSendThread = null;
    mSendThread = new SendThread();
    mSendThread.start();
}

Finally, remember to stop the Handler and keep sending data and stop the thread.

Work hard, go beyond!

Personal website: http://www.lizebin.info

github:https://github.com/lizebinbin

Guess you like

Origin blog.csdn.net/lizebin_bin/article/details/62423373