UDP通信协议是Socket通信的一种实现方式,Socket通信一般有两种通信方式:基于TCP协议、基于UDP协议。
基于UDP的通信都是通过java.net.DatagramSocket这个类来实现的,常用的有connect()、disConnect()、send()、receive()几个方法。DatagramPacket是数据的载体。
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);
}
三个构造方法,我们可以指定端口和IP,也可以不指定,在发送数据的时候,在数据包中指定。
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) {…}
我们可以看到可以在数据包中设置ip和端口,所以如果连接时不指定也是可以的
1、 connect new出一个DatagramSocket对象,设置端口和IP,connect();
2、 send 创建一个DatagramPacket对象,socket.send(packet)发送数据;
3、 receive 创建一个DatagramPacket对象,socket.receive(package)接收数据;
构参:
public UDPSocketClient() {
try {
this.mSocket = new DatagramSocket();
this.mIsStop = false;
this.mIsClosed = false;
} catch (SocketException e) {
if (__IEsptouchTask.DEBUG) {
Log.e(TAG, "SocketException");
}
e.printStackTrace();
}
}
发送函数:
public void sendData(byte[][] data, int offset, int count,
String targetHostName, int targetPort, long interval) {
if ((data == null) || (data.length <= 0)) {
if (__IEsptouchTask.DEBUG) {
Log.e(TAG, "sendData(): data == null or length <= 0");
}
return;
}
for (int i = offset; !mIsStop && i < offset + count; i++) {
if (data[i].length == 0) {
continue;
}
try {
// Log.i(TAG, "data[" + i + " +].length = " + data[i].length);
DatagramPacket localDatagramPacket = new DatagramPacket(
data[i], data[i].length,
InetAddress.getByName(targetHostName), targetPort);
this.mSocket.send(localDatagramPacket);
} catch (UnknownHostException e) {
if (__IEsptouchTask.DEBUG) {
Log.e(TAG, "sendData(): UnknownHostException");
}
e.printStackTrace();
mIsStop = true;
break;
} catch (IOException e) {
if (__IEsptouchTask.DEBUG) {
Log.e(TAG, "sendData(): IOException, but just ignore it");
}
// for the Ap will make some troubles when the phone send too many UDP packets,
// but we don't expect the UDP packet received by others, so just ignore it
}
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
e.printStackTrace();
if (__IEsptouchTask.DEBUG) {
Log.e(TAG, "sendData is Interrupted");
}
mIsStop = true;
break;
}
}
if (mIsStop) {
close();
}
}
我们略去他处理IP等信息的复杂算法
看到this.mSocket.send(localDatagramPacket);
代码,则是将UDP广播发送核心代码
构参
public UDPSocketServer(int port, int socketTimeout, Context context) {
this.mContext = context;
this.buffer = new byte[64];
this.mReceivePacket = new DatagramPacket(buffer, 64);
try {
this.mServerSocket = new DatagramSocket(port);
this.mServerSocket.setSoTimeout(socketTimeout);
this.mIsClosed = false;
WifiManager manager = (WifiManager) mContext
.getSystemService(Context.WIFI_SERVICE);
mLock = manager.createMulticastLock("test wifi");
Log.d(TAG, "mServerSocket is created, socket read timeout: "
+ socketTimeout + ", port: " + port);
} catch (IOException e) {
Log.e(TAG, "IOException");
e.printStackTrace();
}
}
接收代码
public byte[] receiveSpecLenBytes(int len) {
Log.d(TAG, "receiveSpecLenBytes() entrance: len = " + len);
try {
acquireLock();
mServerSocket.receive(mReceivePacket);
byte[] recDatas = Arrays.copyOf(mReceivePacket.getData(), mReceivePacket.getLength());
Log.d(TAG, "received len : " + recDatas.length);
for (int i = 0; i < recDatas.length; i++) {
Log.e(TAG, "recDatas[" + i + "]:" + recDatas[i]);
}
Log.e(TAG, "receiveSpecLenBytes: " + new String(recDatas));
if (recDatas.length != len) {
Log.w(TAG,
"received len is different from specific len, return null");
return null;
}
return recDatas;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
核心代码mServerSocket.receive(mReceivePacket);接收数据
向上推,调用此方法的对象_EsptouchTask的_ListenAsyn
异步线程Thread,while循环中,可以想到,若要做到通讯,那么必会用到线程,因为项目配置是只进行一次的,不是即时通讯的项目,不涉及线程问题。所以从逻辑上最好不要在同一个线程里发送和接收数据,分别创建两个线程
例:
发送线程:
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();
}
}
}
接收线程:
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();
}
}
}
mHandler.sendEmptyMessage(2)用来
重启线程
我们回到demo
在Activity中,点击配置按钮会触发一下AsyncTask,
AsyncTask的onPreExecute()方法不看,doInBackground方法,
@Override
protected List doInBackground(String... params) {
int taskResultCount = -1;
synchronized (mLock) {
// !!!NOTICE
String apSsid = mWifiAdmin.getWifiConnectedSsidAscii(params[0]);
String apBssid = params[1];
String apPassword = params[2];
String taskResultCountStr = params[3];
taskResultCount = Integer.parseInt(taskResultCountStr);
mEsptouchTask = new EsptouchTask(apSsid, apBssid, apPassword, EsptouchDemoActivity.this);
mEsptouchTask.setEsptouchListener(myListener);
}
List resultList = mEsptouchTask.executeForResults(taskResultCount);
return resultList;
}
看方法;
mEsptouchTask.executeForResults(taskResultCount);
此方法回到上面的发送数据中,
此方法完成后主线程更新UI
@Override
protected void onPostExecute(List result) {
mProgressDialog.getButton(DialogInterface.BUTTON_POSITIVE)
.setEnabled(true);
mProgressDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
"Confirm");
IEsptouchResult firstResult = result.get(0);
// check whether the task is cancelled and no results received
if (!firstResult.isCancelled()) {
int count = 0;
// max results to be displayed, if it is more than maxDisplayCount,
// just show the count of redundant ones
final int maxDisplayCount = 5;
// the task received some results including cancelled while
// executing before receiving enough results
if (firstResult.isSuc()) {
StringBuilder sb = new StringBuilder();
for (IEsptouchResult resultInList : result) {
sb.append("Esptouch success, bssid = "
+ resultInList.getBssid()
+ ",InetAddress = "
+ resultInList.getInetAddress()
.getHostAddress() + "\n");
count++;
if (count >= maxDisplayCount) {
break;
}
}
if (count < result.size()) {
sb.append("\nthere's " + (result.size() - count)
+ " more result(s) without showing\n");
}
mProgressDialog.setMessage(sb.toString());
} else {
mProgressDialog.setMessage("Esptouch fail");
}
}
}
此时在Dialog中展示需要的信息,配置成功