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

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

前端布局如下:



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

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





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


  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在两台ESP32之间通过WiFi进行通信,可以使用ESP32的WiFi库和网络通信协议,如TCP或UDP。在发送和接收数据时,可以将数据打包成特定的格式,以便在接收端解析和识别。 例如,可以使用JSON格式来打包数据。发送端将变量打包成JSON格式的字符串,并通过WiFi发送到接收端。接收端接收到数据后,可以将数据解析成JSON格式,并从中提取所需的变量值。 以下是一个使用JSON格式进行数据通信的示例代码: 发送端代码: ``` #include <WiFi.h> #include <ArduinoJson.h> const char* ssid = "your_SSID"; const char* password = "your_PASSWORD"; const char* host = "192.168.1.2"; // 接收端的IP地址 void setup() { Serial.begin(9600); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); } } void loop() { // 将变量打包成JSON格式的字符串 DynamicJsonDocument doc(1024); doc["data"] = 123; String jsonString; serializeJson(doc, jsonString); // 发送数据 WiFiClient client; if (client.connect(host, 80)) { client.println("POST /data HTTP/1.1"); client.println("Host: " + String(host)); client.println("Content-Type: application/json"); client.println("Content-Length: " + String(jsonString.length())); client.println(); client.println(jsonString); } delay(1000); } ``` 接收端代码: ``` #include <WiFi.h> #include <ArduinoJson.h> const char* ssid = "your_SSID"; const char* password = "your_PASSWORD"; WiFiServer server(80); void setup() { Serial.begin(9600); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); } server.begin(); } void loop() { WiFiClient client = server.available(); if (client) { while (client.connected()) { // 接收数据 String request = client.readStringUntil('\r'); if (request.indexOf("POST /data") != -1) { String jsonString = client.readStringUntil('\r'); // 解析JSON格式的数据 DynamicJsonDocument doc(1024); deserializeJson(doc, jsonString); int data = doc["data"]; Serial.println(data); } client.stop(); } } delay(1000); } ``` 在这个例子中,发送端将变量"data"的值设为123,并将其打包成JSON格式的字符串。发送端通过WiFi将数据发送到接收端的IP地址,并使用POST请求发送数据。接收端接收到数据后,从JSON字符串中解析出"data"的值,并将其打印到串口上。 你可以根据自己的需求修改这个例子,打包和解析的数据可以是任何格式,只要发送端和接收端约定好即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值