UDP协议中文名是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,在第四层——传输层,处于IP协议的上一层。与所熟知的TCP(传输控制协议)协议一样,UDP协议直接位于IP(网际协议)协议的顶层。根据OSI(开放系统互连)参考模型,UDP和TCP都属于传输层协议。UDP协议的主要作用是将网络数据流量压缩成数据包的形式。一个典型的数据包就是一个二进制数据的传输单位。每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。UDP数据包是面向无连接的,用户发出数据包不需要得到确认,因此是是不可靠的传输。
Android的UDP的数据传输和JAVA里没有什么区别。
UDP工具类:
package com.wifi.udp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
/**
* UDP工具类
* @author Sunward
*
*/
public class UDPUtils implements Runnable {
public boolean keepRunning = true;//线程开始标志
public static final String TAG = "TEST";
//发送目的主机IP和端口
private static String SERVER_IP;
private static int SERVER_PORT;
//本机监听的端口
private static int LOCAL_PORT = 8929;
//发送的消息
private String message = "test";
//服务器接收的消息
private String receive;
//Handler传递的数据
private Message msg;
//Message传递的Buddle参数
private Bundle bundle;
//wifi名和密码
private String SSID,password;
public UDPUtils(){
}
public UDPUtils(String Server_IP, int Server_Port) {
SERVER_IP = Server_IP;
SERVER_PORT = Server_Port;
}
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
/**
* 线程停止标志
* @param keepRunning
*/
public void setKeepRunning(boolean keepRunning) {
this.keepRunning = keepRunning;
}
public boolean getKeepRunning(){
return this.keepRunning;
}
/**
* 服务端监听程序
*/
public void StartListen() {
keepRunning = getKeepRunning();
DatagramSocket socket = null;
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
try {
socket = new DatagramSocket(LOCAL_PORT);
socket.setBroadcast(true);
Log.i(TAG, "socket");
// socket.setSoTimeout(200);
} catch(Exception e) {
e.printStackTrace();
return;
}
while (keepRunning) {
try {
//等待客户机连接
packet.setData(data);
Log.e(TAG, "receive0");
socket.receive(packet);
receive = new String(packet.getData(), 0, packet.getLength());
msg = new Message();
bundle = new Bundle();
//把数据放到buddle中
bundle.putString("receive", receive);
//把buddle传递到message
msg.setData(bundle);
myHandler.sendMessage(msg);
} catch (Exception e) {
continue;
}
}
if (socket != null) {
socket.close();
socket = null;
}
}
//利用Handler将接收的数据实时打印出来
Handler myHandler = new Handler(){
@Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);
Bundle bundle=new Bundle();
//从传过来的message数据中取出传过来的绑定数据的bundle对象
bundle = msg.getData();
receive = bundle.getString("receive");
setMessage(receive);
}
};
public void sendControInfo(String message){
try {
DatagramSocket sendSocket = new DatagramSocket();
byte[] configInfo = message.getBytes();
InetAddress ip = InetAddress.getByName(SERVER_IP); //即目的IP
DatagramPacket sendPacket = new DatagramPacket(configInfo, configInfo.length, ip ,SERVER_PORT);// 创建发送类型的数据报:
sendSocket.send(sendPacket); // 通过套接字发送数据:
sendSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
StartListen();
}
}
MainActivity:
package com.wifi.main;
import java.lang.Thread.State;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Pattern;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.example.jushi_blub.R;
import com.wifi.udp.UDPUtils;
import com.wifi.utils.MyApplication;
public class MainActivity extends Activity implements OnClickListener{
public static final String TAG = "MainActivity";
private Button send_udp,receive_udp,coapServer,coapClient;
private long exitTime = 0;
//发送或者接收的文本
public static EditText send_msg,receive_msg;
//目的主机IP
private String SERVER_IP;
private int SERVER_PORT;
//本机监听端口
private int LOCAL_PORT;
private TextView infomation;
private UDPUtils udpUtils;
private String message;
private Thread thread;
private Map<String, Object> map;
//用户输入的灯泡ID
private int bulbID;
//用来存储全局变量,用于Activity之间的传递
private MyApplication myApplication;
//定时器,用来检测是否接收到成功消息
private Timer timer;
//灯泡的状态
private String status = "off";
/**
* 初始化
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
myApplication = (MyApplication)this.getApplicationContext();
map = myApplication.getMap();
if (!map.isEmpty()) {
SERVER_IP = map.get("IP").toString();
SERVER_PORT = Integer.parseInt(map.get("Port").toString().trim());
LOCAL_PORT = Integer.parseInt(map.get("LOCAL_PORT").toString().trim());
}else {
SERVER_IP = "192.168.0.107";
SERVER_PORT = 9090;
LOCAL_PORT = 8929;
}
udpUtils = new UDPUtils(SERVER_IP,SERVER_PORT, LOCAL_PORT);
infomation.append("目的IP: "+SERVER_IP+"\n"+"目的端口: "+SERVER_PORT+"\n");
infomation.append("本地端口: " +LOCAL_PORT);
}
/**
* 控件初始化
*/
public void initViews(){
send_udp = (Button) findViewById(R.id.send_udp);
send_msg = (EditText) findViewById(R.id.message);
receive_msg = (EditText) findViewById(R.id.receive);
infomation =(TextView) findViewById(R.id.information); send_udp.setOnClickListener(MainActivity.this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
/**
* 监听Back键按下事件,方法1:
* 注意:
* super.onBackPressed()会自动调用finish()方法,关闭
* 当前Activity.
*/
/* @Override
public void onBackPressed() {
super.onBackPressed();
} */
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO 按两次返回键退出应用程序
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
// 判断间隔时间 大于2秒就退出应用
if ((System.currentTimeMillis() - exitTime) > 2000) {
// 应用名
String applicationName = getResources().getString(
R.string.app_name);
String msg = "再按一次返回键退出";
//String msg1 = "再按一次返回键回到桌面";
Toast.makeText(MainActivity.this, msg, 0).show();
// 计算两次返回键按下的时间差
exitTime = System.currentTimeMillis();
} else {
// 关闭应用程序
finish();
// 返回桌面操作
// Intent home = new Intent(Intent.ACTION_MAIN);
// home.addCategory(Intent.CATEGORY_HOME);
// startActivity(home);
}
return true;
}
return super.onKeyDown(keyCode, event);
}
/**
* 菜单单击事件
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.wifi_config) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, WIFIActivity.class);
this.startActivity(intent);
return true;
}
if (id == R.id.ip_config) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, IPActivity.class);
this.startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.send_udp:
new Thread(){
@Override
public void run(){
udpUtils.sendControInfo("Hello");
}
}.start();thread = new Thread(udpUtils);
thread.start();
break;
default:
break;
}
}
/**
* 判断输入的是否为数字
* @param str
* @return
*/
public static boolean isInteger(String str) {
Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
return pattern.matcher(str).matches();
}
/**
* 判断开灯返回的数据
* @param receive
* @return
*/
@Override
protected void onDestroy() {
super.onDestroy();
//关闭线程
udpUtils.setKeepRunning(false);
}
}
效果图:
在同一个局域网下进行测试,目的主机是电脑IP地址是192.168.0.107,安装了TCP/UDPb捕获软件,手机发送开关信号电脑的软件能够接收到,并且电脑发送消息手机也能监听到。这里要注意:
1.手机的点击按钮事件中的发送数据一定要放在线程里运行,否则会报主线程不能处理网络请求的相关异常。
2.监听接收数据时,也要启动一个线程,在UDPUtils中监听数据的端口号不能和发送的端口号相同,且接收数据需要放到Handler中处理,否则不能实时将数据打印在手机上。