先创建两个项目:
Android设备项目为:Android Application Project,项目名:UDPSend
PC机的项目为:Java Project,项目名:UDPreceiver
为Android项目添加组件:Button 1个、 EditText 1个,如图所示
·问题解决标志:当按下Button组件时,EditText组件上输入的内容将被传送到PC机上,并显示出来。
着手问题前,先了解如下概念:
①、UDP协议发送和接受数据用到类DatagramSocket:
Java使用DatagramSocket代表UDP协议的Socket,DatagramSocket本身只是码头,不维护状态,不能产生IO流,它的唯一作用就是接收和发送数据报,Java使用DatagramPacket来代表数据报,DatagramSocket接收和发送的数据都是通过DatagramPacket对象完成的。
类DatagramSocket有3个构造方法:
DatagramSocket():创建一个DatagramSocket实例,并将该对象绑定到本机默认IP地址、本机所有可用端口中随机选择的某个端口。
DatagramSocket(int prot):创建一个DatagramSocket实例,并将该对象绑定到本机默认IP地址、指定端口。
DatagramSocket(int port, InetAddress laddr):创建一个DatagramSocket实例,并将该对象绑定到指定IP地址、指定端口。
通过上面三个构造器中的任意一个构造器即可创建一个DatagramSocket实例,通常在创建服务器时,创建指定端口的DatagramSocket实例--这样保证其他客户端可以将数据发送到该服务器。
一旦得到了DatagramSocket实例之后,就可以通过如下两个方法来接收和发送数据。
receive(DatagramPacket p):从该DatagramSocket中接收数据报。
send(DatagramPacket p):以该DatagramSocket对象向外发送数据报。
从以上的“发送”和“接受”方法中,我们可以感受到,DatagramSocket这个类就像一个快递员,但这个“快递员”只会用手接送快递,却不认“发件人”和“收件人”,因此它是“盲目”的。
接着上面的例子,我们分别用:
(1)、InetAddress类对象存放快递员“送货地址”。该类有一个重要的方法为:InetAddress.getByName("host");
其中方法中的“host”表示“送货地址”,即:pc机当前所连WLAN的IPV4地址。
输入"cmd"后,再在“命令提示符”里输入“ipconfig/all”后可以找到。
(2)、DatagramPacket类对象表示快递员运送的“包裹”,该类有两个重要的构造方法:
public DatagramPacket(byte[] data, int length) :将以byte为单位,长度为length的数据data“打包”进一个DatagramPacket类对象中
public DatagramPacket(byte[] data, int length, InetAddress host, int aPort):将以byte为单位,长度为length的数据data“打包”进一个DatagramPacket类对象中,并将其送入端口为aPort,地址为host的地点。
(1)、在服务端(发送端),为UDPSend项目中的SendActivity类创建方法void SendMsg(String msg)与对象DatagramSocket Socket;,代码如下:
DatagramSocket Socket;
private void SendMsg(String msg){
Log.v("fasong", "开始发送!");
try {
if(Socket==null)
Socket = new DatagramSocket(5555);
InetAddress serverAddress = InetAddress.getByName("192.168.31.9");
byte data[] = msg.getBytes();//把要发送的数据转化为Byte形式
DatagramPacket package1 = new DatagramPacket(data,data.length,
serverAddress,6666);//设置一个发送包(相当于快递,写了快递的地址和编号)
Socket.send(package1);//发送快递
Log.v("fasong", "发送成功!");
} catch (Exception e) {
// 如果发送不成功,那么会在这里显示“发送异常”,并将异常类型打印出来。
Log.v("fasong", "发送异常"+ e);
e.printStackTrace();
}//设置端口号,
}
其中,我当前所连WLAN的IPv4地址为“192.168.31.9”,每个人应该视自己情况而定。
由于我在此只试着传送字符,所以发送数据前,我在这里先将数据存入一个byte数组中。然后将这个数组“打包”到一个DatagramPacket类的对象package1中,再进行发送。
(2)、在接受端(客户端),为UDPreceiver类设置“桥梁”代码如下:
public class UDPreceiver {
/**
* @param args
*/
public static void main(String[] args)throws Exception {
System.out.println("数据发送开始!");
DatagramSocket socket = new DatagramSocket(6666);
//派遣一个快递员socket,让它来端口6666报道
while(true){
byte[] data=new byte[1024];
DatagramPacket pack = new DatagramPacket(data, 10);//“快递员socket”制作包裹
socket.receive(pack); //"快递员socket"用以上制作的"包裹"pack接受数据
int len = pack.getLength();//获取接受到的数据报的长度值
byte[] tong = new byte[len];//借用以上长度值创造一个新的byte数组
for(int i = 0 ; i < len ; i++ ) //将原来数组里的信息复制到新数组中来
tong[i] = data[i]; //注意:这两个数组的长度不同,tong[len]的长度一般来说远
// 小于data[1024]
String str=new String(tong);
System.out.println("收到信息: "+str);
}
}
}
值得注意的是:
①、第一段代码里的Socket与第二段代码里的socket在生成时最好设置成两个序号不同的端口号,以表示这两个端口号独立,不会出现端口重复占用的问题。如上:发送端口为5555,接受端口为6666
②、在接受端,用“包裹”pack的长度构造一个新数组tong[],用来输出。是因为,如果直接用data[]输出,由于数组长度为1024,太长,因而会在输出接收到的数据后,同时输出大量冗余的“空”信息。这样做是为了保持输出数据的准确性。
(1)、在服务端(发送端)UDPSend项目中,SendActivity类代码如下:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_send);
Button Bt = (Button)this.findViewById(R.id.bt1);
final EditText et = (EditText)this.findViewById(R.id.eT1);
//创建好界面图标,并与执行程序连接
Bt.setOnClickListener(new OnClickListener(){
//当按下按钮时,执行以下方法
public void onClick(View v) {
// TODO Auto-generated method stub
new Thread(){
public void run(){
String str = et.getText().toString();//获取输入框的信息
Log.v("fasong","可以发送!发送数据为:"+ str);
SendMsg(str); //只要一按按钮,就发送数据
}
}.start();
}
}); //这里是监听器构造完毕之处,这种构造监听器的方法被称为“匿名类”,是常用的构造工具
}
(2)、在客户端(接收端)为UDPreceiver类代码如下(同“步骤一”一样):
public class UDPreceiver {
/**
* @param args
*/
public static void main(String[] args)throws Exception {
System.out.println("数据发送开始!");
DatagramSocket socket = new DatagramSocket(6666);
//派遣一个快递员socket,让它来端口6666报道
while(true){
byte[] data=new byte[1024];
DatagramPacket pack = new DatagramPacket(data, 10);//“快递员socket”制作包裹
socket.receive(pack); //"快递员socket"用以上制作的"包裹"pack接受数据
int len = pack.getLength();//获取接受到的数据报的长度值
byte[] tong = new byte[len];//借用以上长度值创造一个新的byte数组
for(int i = 0 ; i < len ; i++ ) //将原来数组里的信息复制到新数组中来
tong[i] = data[i]; //注意:这两个数组的长度不同,tong[len]的长度一般来说远
// 小于data[1024]
String str=new String(tong);
System.out.println("收到信息: "+str);
}
}
}
通过以上代码,达到效果:当按下按钮时,输入框上的数据会被发送到PC端
值得注意的是:
①、在发送端,监听器的Click()方法里,必须通过建立线程,在线程中实现传送数据的操作。这是因为,访问网络的操作不能主线程上进行,否则会出现异常android.os.NetworkOnMainThreadException(顾名思义)。
以上即为代码操作:
测试:
运行后,分别在android移动端的输入框里输入“555”,“1234567890”,“abcdefghijklmnopqrstuvwxyz”,得到结果如下:
①、java.net.SocketException: socket failed: EACCES (Permission denied) :
出现这种异常,是大多因为接受端和发送端的权限没有设置好,这时候要检查发送端的AndroidManifest.xml:
添加如上代码。
②、若出现java.net.BindException: bind failed:异常。
建议重启编程软件。
以上均为个人浅见。