上一篇看完了纯udp的理论介绍。我用个简单的例子测试下。
因为本地wireshark抓包需要特殊设置,所以demo找了边上的电脑测试下
这是client端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
public class UDPClient {
public static void main(String[] args) {
//以 "字节数组" 的形式开辟一块内存用于缓存接收到的UDP数据包
byte[] buffer = new byte[1024];
//虽然开辟的缓冲内存大小为1024字节,但也可以设置一个小于该值的缓存空间接收数据包
DatagramPacket rdp = new DatagramPacket(buffer, buffer.length);
DatagramPacket dp = null;
DatagramSocket ds = null;
try {
//new 一个DatagramSocket对象(即打开一个UDP端口准备从此处发出数据包)
ds = new DatagramSocket(9998);
}catch (SocketException e) {
e.printStackTrace();
}
System.out.println("client listen on 9998");
//将要发送的数据、要发送到什么地址设置好并打成一个 DatagramPacket 包
dp = new DatagramPacket((new String("Hello!"+9)).getBytes(), (new String("Hello!"+9)).getBytes().length, new InetSocketAddress("10.253.8.233", 9999));
try {
ds.send(dp);
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(dp.getData().toString());
while(true) {
try {
//receive() 方法是一个阻塞性方法!
ds.receive(rdp);
} catch (IOException e) {
e.printStackTrace();
}
//打印下接收到server返回数据
System.out.println("client:"+new String(buffer, 0, rdp.getLength()));
}
}
}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
public class UDPServer {
public static void main(String[] args) {
//以 "字节数组" 的形式开辟一块内存用于缓存接收到的UDP数据包
byte[] buffer = new byte[1024];
//虽然开辟的缓冲内存大小为1024字节,但也可以设置一个小于该值的缓存空间接收数据包
DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
DatagramSocket ds = null;
try {
//监听在UDP 9999 端口
ds = new DatagramSocket(9999);
} catch (SocketException e) {
e.printStackTrace();
}
System.out.println("server listen on 9999");
while(true) {
try {
//receive() 方法是一个阻塞性方法!
ds.receive(dp);
} catch (IOException e) {
e.printStackTrace();
}
//从下一行代码中可以学习到一种用字节数组构造字符串对象的方法
System.out.println(new String(buffer, 0, dp.getLength()));
String response = "udp receied back."+new String(buffer, 0, dp.getLength());
DatagramPacket dsp = new DatagramPacket(response.getBytes(),response.length(), new InetSocketAddress("10.253.8.138", 9998)) ; // 所有的信息使用buf保存
try {
ds.send(dsp);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
看下日志,客户端的:
client listen on 9998
[B@4aa298b7
client:udp receied back.Hello!9
再看下抓包截图。
可以看出来,client发起udp请求之前,先会在局域网内部发一个广播,根据arp协议从ip获取Mac地址。
而server在回复client的请求的时候,也是先发一个广播获取client的Mac地址。
在看下,抓包的详细报文
我们可以看到图上红框内部的14位是帧头,包含了:目的mac地址,源mac地址,还有上层协议:这里x0800表示IPV4.
***************************************************************************************************************************************
再看45开头的是IP头,逐位看:
45C0:4代表ipv4协议,5代表IP首部的长度(指代首部占32bit/4byte的数目),20字节.C0代表区分服务领域:表示cs6。具体参见百科解释:https://baike.baidu.com/item/DSCP/1781510
0023:(包含报头和数据)总长度35
41bb:16位标志
0000: 3位标志加13位片偏移
c811: C8代表生存时间timetolive 200,11表示8位协议17,代表UDP。
0000:代表首部校验和
0afd088a:代表了32位源IP值10.253.8.138
0afd08e9:代表了32位目的IP值10.253.8.233
没有选项数据,接下来是数据,也是UDP的头部:
*****************************************************
270e:表示16位源端口9998
270f:表示16位目的端口9999
000f:表示udp数据报长度:15(因为udp长度固定位8字节,所以数据长度应该为15-8=7字节)
278d:表示16位校验和
48656c6c6f2139:为数据,转换后为“hello!9”.正好是7个字节
返回的报文跟上面发出的类似,只贴个截图,不再一一标注。