android之同一wifi下两台设备通过UDP进行通讯

参考文章地址:http://www.cocoachina.com/android/20171016/20806.html

前端布局如下:

android之同一wifi下两台设备通过UDP进行通讯_第1张图片

Activity中的全部代码,其中使用了butterknife实现View中控件的实例化。

package com.example.a260219.myapplication;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.net.DatagramPacket;

import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;
import utils.UDPSocket;


public class AndroidUdpNewAcitivity extends AppCompatActivity {

    @InjectView(R.id.tv_receive_ip)
    EditText tvReceiveIp;
    @InjectView(R.id.tv_receive_port)
    TextView tvReceivePort;
    @InjectView(R.id.btn_start)
    Button btnStart;
    @InjectView(R.id.tv_service_msg)
    EditText tvServiceMsg;
    @InjectView(R.id.btn_send)
    Button btnSend;
    @InjectView(R.id.btn_reset)
    Button btnReset;
    @InjectView(R.id.tv_received_ip)
    TextView tvReceivedIp;
    @InjectView(R.id.tv_receive_msg)
    TextView tvReceiveMsg;

    private UDPSocket socket;
    private String rip;//接收端ip
    private String sip;//服务端ip
    private int port;
    private String message;
    private DatagramPacket receivePacket;
    int count = 0;
    public Handler mhandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            receivePacket = (DatagramPacket) msg.obj;
            String strReceive = new String(receivePacket.getData(), 0, receivePacket.getLength());
            tvReceivedIp.setText(receivePacket.getAddress().getHostAddress());
            tvReceivePort.setText(receivePacket.getPort() + "");
            tvReceiveMsg.setText(strReceive);
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_new_udp);
        ButterKnife.inject(this);
    }

    public void myToast(String str) {
        Toast.makeText(AndroidUdpNewAcitivity.this, str, Toast.LENGTH_SHORT).show();
    }

    @OnClick({R.id.btn_start, R.id.btn_send,R.id.btn_reset})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_start:
                if(TextUtils.isEmpty(tvReceiveIp.getText())||TextUtils.isEmpty(tvReceivePort.getText())){
                    myToast("接收端ip或者port不能为空");
                    return;
                }

                rip=tvReceiveIp.getText().toString();
                port=Integer.parseInt(tvReceivePort.getText().toString());

                if(socket==null){
                    socket = new UDPSocket(rip, port, this);
                }
                //注册回调函数
                socket.setUIUpdateListener(new UDPSocket.UIUpdateListener() {
                    @Override
                    public void upUI(DatagramPacket receivePacket) {
                        Message msg = new Message();
                        msg.obj = receivePacket;
                        mhandler.sendMessage(msg);
                    }
                });
                break;
            case R.id.btn_send:
                if(TextUtils.isEmpty(tvServiceMsg.getText().toString())){
                    myToast("发送消息不能为空");
                    return;
                }
                message=tvServiceMsg.getText().toString();
                ++count;//记录点击发送的次数
                socket.sendMessage(message+count);
                break;
            case R.id.btn_reset:
                tvReceivedIp.setText("");
                tvReceiveMsg.setText("");
                break;
        }
    }


}
---------------------------
UDPSocket.java中的代码:
 //接口规范
    public interface  UIUpdateListener {
        void upUI(DatagramPacket receivePacket);
    }
    public void setUIUpdateListener(UIUpdateListener listener){
            startUDPSocket(listener);
    }

    public void startUDPSocket(UIUpdateListener listener) {
        if (client != null) return;
        try {
         
            client = new DatagramSocket(CLIENT_PORT);

            if (receivePacket == null) {
          
                receivePacket = new DatagramPacket(receiveByte, BUFFER_LENGTH);
            }

            startSocketThread(listener);
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    /**
     * 接收数据
     */
    private void startSocketThread(final UIUpdateListener listener) {
        clientThread = new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "clientThread is running...");
                receiveMessage(listener);
            }
        });
        isThreadRunning = true;
        clientThread.start();
 
    }

    /**
     * 处理接受到的消息
     */
    private void receiveMessage(UIUpdateListener listener) {
        while (isThreadRunning) {
            try {
                if (client != null) {
                    client.receive(receivePacket);
                }
                lastReceiveTime = System.currentTimeMillis();
                Log.d(TAG, "receive packet success...");
            } catch (IOException e) {
                Log.e(TAG, "UDP数据包接收失败!线程停止");
                stopUDPSocket();
                e.printStackTrace();
                return;
            }

            if (receivePacket == null || receivePacket.getLength() == 0) {
                Log.e(TAG, "无法接收UDP数据或者接收到的UDP数据为空");
                continue;
            }

            //解析接收到的 json 信息

            // 每次接收完UDP数据后,重置长度。否则可能会导致下次收到数据包被截断。
            if (receivePacket != null) {
                listener.upUI(receivePacket);
                receivePacket.setLength(BUFFER_LENGTH);
            }
        }
    }

    public void stopUDPSocket() {
        isThreadRunning = false;
        receivePacket = null;
        if (clientThread != null) {
            clientThread.interrupt();
        }
        if (client != null) {
            client.close();
            client = null;
        }
        if (timer != null) {
            timer.exit();
        }

/**
 * 发送
 */
public void sendMessage(final String message) {
    this.BROADCAST_MSG=message;
    mThreadPool.execute(new Runnable() {
        @Override
        public void run() {
            try {
                InetAddress targetAddress = InetAddress.getByName(BROADCAST_IP);

                DatagramPacket packet = new DatagramPacket(message.getBytes(), message.length(), targetAddress, CLIENT_PORT);

                client.send(packet);

                // 数据发送事件
                Log.d(TAG, "数据发送成功");

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

        }
    });
}		
    }

-----------------
总结:
注意在发送之前,需要先点击开启。
在开启的方法中,红色加粗部分的代码 socket.setUIUpdateListener, 是注册的回调函数(后续解释这个回调函数),在这个函数中执行了接受消息的语句。接受和发送消息,需要使用 DatagramSocket套接字和DatagramPacket数据包组装发送和接收的数据, 因为发送和接受信息会阻塞线程,所以发送和接受的方法需要写在子线程中。
发送端:伪代码
1.首先,创建一个发送的DatagramSocket
client = new DatagramSocket(CLIENT_PORT);
2.获取接收端的ip地址
InetAddress local = null;
try {
// 换成服务器端IP
local = InetAddress. getByName (ip);
} catch (UnknownHostException e) {
e.printStackTrace();
}
3.定义发送信息的字节数组
byte[] messageByte = message.getBytes();
4.创建DatagramPacket包组装数据,然后发送出去
DatagramPacket p = new DatagramPacket(messageByte, msg_length, local,
server_port);
try {
s.send(p);
} catch (IOException e) {
e.printStackTrace();
}
接收端:
1.创建一个接收消息的字节数组,并规定数组的长度
byte[] message = new byte[1024];
2.创建接收的DatagramSocket
client = new DatagramSocket(CLIENT_PORT);
3.创建接收数据的DatagramPacket
receivePacket = new DatagramPacket(receiveByte, BUFFER_LENGTH);
client.receive(receivePacket);

补充:说说回调函数。
参考地址: https://blog.csdn.net/a78270528/article/details/46918601
在UDPSocket类中接收到数据以后,需要把接收的数据显示在界面上,使用的回调函数。
什么是回调函数, 简单来说:回调函数就是预留给系统调用的函数,而且我们往往知道该函数被调用的时机。就好比这个项目中,回调函数是预留给Activity调用的,当接收到信息的时候进行回调,接收到信息进行UI界面更新的过程就叫”回调“。
如下图:

android之同一wifi下两台设备通过UDP进行通讯_第2张图片



1.在UDPSocket类中定义一个接口(规范)
2.在Activity中注册接口,提前告诉系统这里有UI的方法等待调用,该方法具体实现更新UI。
3.在UDPSocket类中定义方法开启接受信息的方法,收到消息后调用更新UI的接口,告诉系统要调用注册的监听里的方法了。


你可能感兴趣的:(android)