近期接触了几个小的Android开发项目,根据需求要利用到网络传输中的UDP协议方式传输,遇到不少坑,在此分享一下在Android应用中UDP的一些简单技术功能实现,希望能帮到用得到的同僚。
需(wo)求(yao)分(gan)析(ma):
从PC上输入一串乱七八糟,然后能在我手机(某为powered by android)上显示出来。
当然这个需求有N种方法去实现,这里和大家分享一下我是如何用UDP/IP方式在Android上实现的。
1、写服务器:口号霸气点,但只实现能发包就行。java或C#或其他语言都没问题学过计算机网络的应该清楚网络分层细腰结构什么的,我用java花5分钟写了个小窗体。
2、写移动端:收到数据然后显示出来,这里可是有好几个坑待会儿跟大家敲小黑板。
接下来就是正文了:
服务器部分:
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
/********************************************************
* File name : UdpServer.java
* Author : Johan Version : 1.0 Date : 2018/4/6
* Description : // 输入信息通过UDP协议向移动端发送
* Fuction list : // 1.UdpServer
* 2.sendMessage
* 3.main
* Others : //
********************************************************/
public class UdpServer extends JFrame{
//// TODO 控件及支持项
private static final long serialVersionUID = 1L;
private Container con; // 容器
private JPanel pan; // 面板
private JTextField text; // 输入框
private JButton button; // 提交按钮
/***************************************
* Function name : UdpServer
* Description : 构造函数
* Variables :
***************************************/
UdpServer(){
super();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setBounds(200,200,200,150);
con = this.getContentPane();
pan = new JPanel();
text = new JTextField(15);
button = new JButton("发送");
// 点击按钮后向固定IP地址设备某端口发送数据包
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
String str = text.getText();
sendMessage(str, "192.168.43.113", 30000);
}
});
pan.add(text);
pan.add(button);
con.add(pan);
this.setVisible(true);
}
/***************************************
* Function name : sendMessage
* Description : 发送信息
* Variables : String msg, String IP, int PORT
***************************************/
public void sendMessage(String msg, String IP, int PORT){
try {
DatagramSocket sendSocket = new DatagramSocket();
InetAddress serverAddr = InetAddress.getByName(IP);
DatagramPacket outPacket = new DatagramPacket(msg.getBytes(), msg.getBytes().length,serverAddr, PORT);
sendSocket.send(outPacket);
sendSocket.close();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/***************************************
* Function name : main
* Description : 主函数
* Variables : String[] args
***************************************/
public static void main(String[] args) {
new UdpServer();
}
}
Android应用java源代码:
/******************************************************
- Project name : UDPLinkTest
- File name : MainActivity.java
- Author : Johan Version : 1.0 Date : 2018-4-6
- Description : // 接收来自UdpServer程序的数据
- Function list :
1.onCreate
- History : //
*****************************************************/
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class MainActivity extends AppCompatActivity {
//// TODO GUI控件
private TextView text = null; // 显示接收到的数据
//// TODO 后台服务支持项
private myThread MT; // 接收线程
private Handler handler; // 用于修改主界面UI
/*******************************
- Function name : onCreate
- Description : 构造函数
- Variables : Bundle savedInstanceState
*******************************/
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.textView2);
// 绑定线程更改UI
handler = new Handler(){
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
//在这里得到数据,并且可以直接更新UI
String data = (String)msg.obj;
text.setText(data);
break;
default:
break;
}
}
};
// 开启线程
MT = new myThread();
MT.start();
}
//// TODO 网络响应线程
public class myThread extends Thread{
//// 网络连接属性
final int PORT = 30000; // 线程端口
private String data; // 接收到的数据
/*******************************
- Function name : run
- Description : 线程运行函数
- Variables :
*******************************/
public void run(){
super.run();
// 该函数主要用于接收服务器返回的数据信息
try {
// 打开一个socket
DatagramSocket socket = new DatagramSocket(PORT);
// 循环监听
while (true) {
// 解析包文
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
buf = packet.getData();
data = new String(buf, 0, packet.getLength());
// 修改主界面UI,即显示出来
new Thread(new Runnable(){
@Override
public void run() {
Message msg = new Message();
msg.what = 0;
msg.obj = data;
handler.sendMessage(msg);
}
}).start();
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
别急着ctrl+c!下面这行代码决定你能否成功:
<uses-permission android:name="android.permission.INTERNET"/>
没错!就是要记得在ActivityMainfests.xml里面加这条权限声明,不太清楚如何加权限的小伙伴可以问问度娘。
那么要注意的坑在哪儿呢?
注意:
1、记得给APP加网络权限。
2、APP接收到包以后是不能直接以你以为的很简单的方式直接修改绑定的textView控件,会闪退,要用到源程序里面onCreate方法里面的handler部分和myThread执行函数中new Thread部分,这只是一种方法,具体可搜索“android子线程修改UI的方法”,一般有4种,网上的大神们总结的也很好不过我觉得这个挺好用而且在我的项目需求中很方便。
3、你实验用的PC和你的手机要连到同一个WLAN里面,同时只是为了验证使用要注意修改PC端java程序发送包目的IP。
4、消息中带中文会乱码哦,如果只是进行一些程序级响应的通信可以不必考虑这点了,解决方法是在DatagramPacket处修改一下传输数据的方式。
附上效果图:
<开始扯感受了,急着走的客官可以go for encoding了>
这里我只是实现和验证了一个很小的功能,就是实现程序间的UDP通信
那这东西有啥用呢?
虽然实际生产中应该会有更先进和安全的技术,但这不妨碍我们在技术上的举一反三:
1、都是java程序,PC能发到APP,那APP也能发到PC,这就是简单的消息交互。
2、既然PC和APP能交互,那一个WLAN下的APP与APP也能交互(其实用起来很麻烦,但可以实现)。
3、既然APP和APP交互很麻烦,那优化一下可以改为加一个代理,即通过服务器转发消息。
4、既然有服务器了,那只要登录到服务器上的APP就能两两之间通信,听上去是不是很熟悉。
5、这感觉就有点像QQ的原型了,据我道听途说,QQ的数据发送正是使用的UDP协议,当然我是听说的......
我在项目中实际用到这个小技术是因为我要接受服务器发给每个APP的预警信息,达到发布预警的目的
当然对于TCP协议和UDP协议,二者本身存在各自的优劣,大家可以根据实际需要选择。
俗话说“给我一个技术,我能创新出一个地球”。(其实是本人说的)