Java Socket定长通讯读取消息长度头

###一、前言
数据在网络传输时使用的都是字节流,Socket也不例外,所以我们发送数据的时候需要转换为字节发送,读取的时候也是以字节为单位读取。
那么问题就在于socket通讯时,接收方并不知道此次数据有多长,因此无法精确地创建一个缓冲区(字节数组)用来接收,在不定长通讯中,通常使用的方式时每次默认读取8*1024长度的字节,若输入流中仍有数据,则再次读取,一直到输入流没有数据为止。但是如果发送数据过大时,发送方会对数据进行分包发送,这种情况下或导致接收方判断错误,误以为数据传输完成,因而接收不全。
所以,大部分情况下,双方使用socket通讯时都会约定一个定长头放在传输数据的最前端,用以标识数据体的长度,通常定长头有整型int,短整型short,字符串Strinng三种形式。
###二、整型与短整型
int型的长度数据会以4个byte存放,所以可以读取前四个字节的数据,然后转换为int类型:

InputStream is = socket.getInputStream();
byte[] datalen = new byte[4];
is.read(datalen);//读取前四个字节数据存放到datalen中
int length = byteArrayToInt(datalen)//将字节数组转换为int型
byte[] data = new byte[length];
is.read(data);
String recvMsg = new String(data);//将获得数据转为字符串类型

byteArrayToInt的方法实现如下:

public static int byteArrayToInt(byte[] b){
	return b[3]&0xFF | (b[2]&0xFF) << 8 | (b[1]&0xFF) << 16 | (b[0]&0xFF) << 24 
}

当然还有一个更简单的方法,使用DataInputStream:

DataInputStream is = new DataInputStream(socket.getInputStream());
int datalen = is.readInt();//读取前四位字节并返回int类型数据
byte[] data = new byte[datalen];
is.readFully(data);
//使用阻塞的方式读取完整的datalen长度的数据
String recvMsg = new String(data);//将获得数据转为字符串类型

使用DataInputStream不但可以简化代码,而且它的readFully方法会以阻塞等待的形式读取完整datalen长度的数据,而上面的read方法有可能出现没有读取完整的情况,因此对于定长通讯推荐使用DataInputStream和readFully方法;
同样,对于短整型short,DataInputStream也带有对应的方法:

int datalen = is.readShort();//读取前两个字节数据并返回short型数据

同样也可以使用read读取前两位再转为int,再次不多赘述。
###三、字符串类型
字符串类型的好处是可以自行约定长度,将长度转为字符串,然后通过左补零的形式达到约定长度。假设约定定长头为8位,数据长度为1480,则这笔数据传输的前八位定长头为“00001480”。
处理方式为先读取约定长度字节,然后直接转为String类型,再转为int型:

InputStream is = socket.getInputStream();
byte[] datalen = new byte[8];
//假设约定8位
is.read(datalen);
int length = Integer.parseInt(new String(datalen));//将字节数组转为字符串,再转为int类型
byte[] data = new byte[length];
is.read(data);
String recvMsg = new String(data);//将获得数据转为字符串类型

###四、后记
####谢谢handsomegod的指正,不对处已经修改,再次感谢~
对于socket短链接通讯,建议使用定长头标识通讯内容长度,并且不宜发送过大的报文,以免通讯过程中产生内容丢失,比如网络丢包等情况。然后接收时尽量使用DataInputStream的readFully方法读取确定长度的数据。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值