Java DatagramPacket 中的坑

博客指出DatagramPacket的getData()和getLength()方法不对应,使用其处理新数据易触及旧数据致混乱。给出两种解决方法,一是用getLength()截取更新数据,二是接收数据前对DatagramPacket数据置零。建议使用第一种方法,还附了测试代码。

转载请注明来源

目录

问题说明

这里给出两种解决方法

方法一:使用getLength()截取更新的数据部分。下图中,使用substring(0,p.getLength())截取了p.getData()中被更新的数据部分。

方法二:每次接收数据开始前,对DatagramPacket的数据进行置零。下图中,从第一次开始,都对DatagramPacket前getLength()个字节的数据进行置零。

总结

附测试代码


问题说明

DatagramPacket的两个方法getData()和getLength()是不对应的。getLength()反映了刚刚接收到的数据的长度,而getData()反映了DatagramPacket对应缓冲区中的新接收到的数据和未被覆盖的旧数据,当我们使用DatagramPacket来处理新接收到的数据时,就容易同时触及到旧数据导致混乱。下图所示,udp客户端Client A第一次收到了数据“welcome",getData()获得的数据是”welcome",getLength()的结果是7;ClientA第二次收到了数据“hi",getData()获得的数据是”hilcome",其保留了旧数据“welcome"未被覆盖的部分”lcome",而getLength()的结果是2,即“hi"的长度。

这里给出两种解决方法

方法一:使用getLength()截取更新的数据部分。下图中,使用substring(0,p.getLength())截取了p.getData()中被更新的数据部分。

方法二:每次接收数据开始前,对DatagramPacket的数据进行置零。下图中,从第一次开始,都对DatagramPacket前getLength()个字节的数据进行置零。

总结

方法一和方法二都能得到正确结果,但是方法二对缓冲区buf进行了直接操作,很容易发生数组元素访问越界的错误。在DatagramPacket 被receive时,缓冲区buf的length不变,DatagramPacket允许接收的最大字节数也不变;而getLength()的结果发生变化;新数据只是在缓冲区中从偏移量开始覆盖旧数据,如果旧数据长度更大,则保留部分旧数据。

建议使用方法一

附测试代码

package udp_test1.udp_client;

//library
import java.io.*;
import java.net.*;
import java.util.Scanner;

class Recv_Thread extends Thread{
	private byte [] buf;
	private DatagramSocket ds = null;
	private DatagramPacket p = null;
	public Recv_Thread(int buf_len, DatagramSocket ds) {
		buf = new byte[buf_len];
		this.ds = ds;
		p = new DatagramPacket(buf,250);
	}
	@Override
	public void run() {
		while(true) {
			try {
				ds.receive(p);
				String str = new String(p.getData()).substring(0,p.getLength());
				System.out.println("来自["+p.getPort()+"]的数据:"+str);
			}catch(Exception e ) {
				e.printStackTrace();
			}
		}	
	}
}
class Send_Thread extends Thread{
	private DatagramSocket ds = null;
	private InetAddress des_address = null;
	private int des_port;
	Scanner s = new Scanner(System.in);
	
	Send_Thread(DatagramSocket ds, InetAddress des_address,int des_port){
		this.ds = ds;
		this.des_address = des_address;
		this.des_port = des_port;
	}
	
	@Override
	public void run() {
		while(true) {		
			try {
				byte[] buf = s.nextLine().getBytes();
				DatagramPacket p = new DatagramPacket(buf,buf.length,des_address,des_port);
				ds.send(p);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}
	}
}

public class Udp_Client extends Thread{
	private DatagramSocket ds = null;
	private InetAddress des_address = null;
	private int des_port = 0;
	private int buf_len = 256;
	private Send_Thread st = null;
	private Recv_Thread rt = null;
	public Udp_Client(int src_port,int des_port, InetAddress des_address) {
		this.des_port = des_port;
		this.des_address = des_address;
		try{
			ds = new DatagramSocket(src_port);
			System.out.println("本地开启UDP "+src_port+" 端口");
		}catch(IOException e ) {
			e.printStackTrace();
		}
	}
	@Override
	public void run() {
		try {
			st = new Send_Thread(ds,des_address,des_port);
			st.start();
			rt = new Recv_Thread(buf_len,ds);
			rt.start();	
			st.join();
			rt.join();
		}catch(Exception e) {
			e.printStackTrace();
		}
		ds.close();
	}
}

 

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值