IP/TCP/UDP报文解析(3)UDP报文
前言
本文中涉及很多的位运算,如果对位运算不太了解的请看这篇博文《Java中的位运算》。
正文
UDP报文格式
- 源端口:占16位 在UDP数据段第1、2字节(下标0、1)中,标识本地端口号。取值 0 - 65535
- 目的端口:占16位 在UDP数据段第3、4字节(下标2、3)中,标识远程端口号。取值0 - 65535
- 总长度:占16位 在UDP数据段第5、6字节(下标4、5)中,标识该报文段包括报头部分的所有数据字节的长度。取值0 - 65535
- 校验和:占16位 在UDP数据段第7、8字节(下标6、7)中,该字段通过校验和计算得出,用于校验数据段是否被正确就收处理。
- 数据:数据为可变长度。由于总长度位的最大取值为65535,所以理论上该值最大为65535 - 8 = 65523字节。
UDP校验和
UDP中的校验和不是必要的,因为即使UDP数据段校验不通过也没办法通知对端重发数据,这也就是为什么UDP可靠性不如TCP的原因。UDP是无连接无状态协议,因为它不像TCP协议一样会经过三次握手建立连接,两端都不知道各自的状态。发信方发出的UDP报文有没有完全送达,自己与对端都不知道。正因为UDP协议不需要三次握手建立连接,每次收到信息后无需发送确认信息,每次发送信息后无需等待对端确认,所以在效率上要高于TCP协议。总的来说,UDP协议的可靠性不如TCP协议,但效率要高于TCP。
校验和的计算
UDP校验和计算与TCP校验和计算是一样的过程,具体请参见《IP/TCP/UDP报文解析(2)TCP报文》这篇博文。
数据校验
UDP数据校验与TCP数据校验的过程是一样的,具体请参见 《IP/TCP/UDP报文解析(2)TCP报文》 这篇博文。
UDP数据报解析代码
public class UDPPacket extends Packet{
private static final String TAG = UDPPacket.class.getSimpleName();
static final int SOURCE_PORT_BIT = 0;
static final int DESTINATION_PORT_BIT = 2;
static final int TOTAL_LENGTH_BIT = 4;
static final int CHECK_SUM_BIT = 6;
private byte[] pseudoHeader;
public UDPPacket(byte[] bytes , int... parameters){
super(bytes,parameters);
}
/**
* 获取UDP数据报源端口
* @return
*/
public int getLocalPort(){
return byteToInt(bytes[SOURCE_PORT_BIT + offset],bytes[SOURCE_PORT_BIT + offset + 1]);
}
/**
* 获取UDP数据报目的端口
* @return
*/
public int getRemotePort(){
return byteToInt(bytes[DESTINATION_PORT_BIT + offset],bytes[DESTINATION_PORT_BIT + offset + 1]);
}
/**
* 获取UDP数据报总长度
* @return
*/
public int getTotalLenth(){
return byteToInt(bytes[TOTAL_LENGTH_BIT + offset],bytes[TOTAL_LENGTH_BIT + offset + 1]);
}
/**
* 获取校验和
* @return
*/
public int getCheckSum(){
return byteToInt(bytes[CHECK_SUM_BIT],bytes[CHECK_SUM_BIT + 1]);
}
/**
* 检查校验和是否正确
* @return
*/
public boolean checkSum(){
return (~getSum() & 0xFFFF) == 0;
}
/**
* 获取校验和
* @return
*/
private int getSum(){
int sum = 0;
if(pseudoHeader != null){
for(int i = 0;i < pseudoHeader.length; i += 2){
sum += byteToInt(pseudoHeader[i],pseudoHeader[i+1]);
}
}
for(int i = offset;i < validLength;i++){
sum += byteToInt(bytes[i],bytes[i + 1]);
}
if((validLength - offset) % 2 > 0){
sum += (bytes[validLength - 1] & 0xFF) << 8;
}
while ((sum >> 16) > 0){
sum = (sum >> 16) + (sum & 0xFFFF);
}
return sum;
}
public void initPseudoHeader(int localIP,int remoteIP,int length){
pseudoHeader = new byte[12];
for(int i = 0;i < 4;i++){
pseudoHeader[i] = (byte) (localIP >> ((3 - i) * 8));
pseudoHeader[i + 4] = (byte) (remoteIP >> ((3 - i) * 8));
}
pseudoHeader[8] = 0 ;
pseudoHeader[9] = 17 ;
pseudoHeader[10] = (byte) (length >> 8);
pseudoHeader[11] = (byte) length;
}
/**
* 刷新校验和
*/
public void refreshCheckSum(){
bytes[CHECK_SUM_BIT + offset] = 0;
bytes[CHECK_SUM_BIT + offset + 1] = 0;
int sum = ~ getSum();
bytes[CHECK_SUM_BIT + offset] = (byte) (sum >> 8);
bytes[CHECK_SUM_BIT + offset + 1] = (byte) sum;
}
public void setLocalPort(int port){
bytes[SOURCE_PORT_BIT + offset] = (byte) (port >> 8);
bytes[SOURCE_PORT_BIT + offset + 1] = (byte) port;
}
public void setRemotePort(int port){
bytes[DESTINATION_PORT_BIT + offset] = (byte) (port >> 8);
bytes[DESTINATION_PORT_BIT + offset +1] = (byte) port;
}
}
总结
以上就是我对UDP协议的理解,希望对你有所帮助。