1.最近在做安卓网络编程发现了点奇怪的问题
服务器端:
byte buf[1024];
inputStream = socket.getInputStream();
int len = inputStream.read(buf);
system.out.println("len = "+len);
客户端:
采用linux socket频繁的向安卓发送“hello”字符串,注意是频繁;
发现服务器读到的数据大于hello字符串的长度,这是?????
我们先来讲下inputstream中的三个版本的read函数
read();//每次读取1byte字节的数据
read(byte buf[])//每次最多读取buf.length()长度的数据
read(byte buf[],int offset,int len);//每次最多读取len个数据到buf offset开始的内存中
原因就是read函数最多读取buf.length()字节的数据,错误将下次的数据读掉了
2.UDP和TCP MTU
UDP和TCP协议利用端口号实现多项应用同时发送和接收数据。数据通过源端口发送出去,通过目标端口接收。有的网络应用只能使用预留或注册的静态端口;而另外一些网络应用则可以使用未被注册的动态端口。因为UDP和TCP报头使用两个字节存放端口号,所以端口号的有效范围是从0到65535。动态端口的范围是从1024到65535。
MTU最大传输单元,这个最大传输单元实际上和链路层协议有着密切的关系,EthernetII帧的结构DMAC+SMAC+Type+Data+CRC由于以太网传输电气方面的限制,每个以太网帧都有最小的大小64bytes最大不能超过1518bytes,对于小于或者大于这个限制的以太网帧我们都可以视之为错误的数据帧,一般的以太网转发设备会丢弃这些数据帧。
由于以太网EthernetII最大的数据帧是1518Bytes这样,刨去以太网帧的帧头(DMAC目的MAC地址48bit=6Bytes+SMAC源MAC地址48bit=6Bytes+Type域2bytes)14Bytes和帧尾CRC校验部分4Bytes那么剩下承载上层协议的地方也就是Data域最大就只能有1500Bytes这个值我们就把它称之为MTU。
UDP 包的大小就应该是 1500 - IP头(20) - UDP头(8) = 1472(BYTES)
TCP 包的大小就应该是 1500 - IP头(20) - TCP头(20) = 1460 (BYTES)
网络传输不是实时的,不是连续的。注意哦,如果数据长度过大的话,在实际的网络发送过程中,会对数据进行分段划分小于MTU,分多次发送。那我们如何才能完整的读取我们发送的数据帧呢?而不至于误读呢?
自己定义网络传输数据帧格式:head+data+crc 就是在head上填充发送data的长度,就能准确读取每帧数据了
假定发送的数据帧 head为 int len存数data数据长度;
读取函数为:
int readlen = read( buf,0t,4);
if(readlen ==4)
{
int receivelen = byteToint(buf);
int readcount = 0;
while(readcount<receivelen)
readcount += read(buf,readcount,receivelen-readcount);
}
public static int byteToint(byte[] b)
{
int tmp =0;
int n = 0;
for(int i =3;i >=0;i--)
{
tmp = b[i] & 0xff;
n <<=8;
n |=tmp;
}
return n;
}