udp协议的特点:
- 发送数据都是需要把数据封装到数据包中再发送 的,面向无连接。
- 数据包大小不能超过64kb。
- 因为udp协议是面向无连接的, 所以会出现数据包丢失的情况。
- 因为面向无连接,所以速度快。
- udp协议是不分客户端与服务端,只分发送端与接收端。
数据包在什么情况下会丢失呢:
- 带宽不足的时候。
- cpu处理能力不足 的时候。
1.第一个UDP小程序
发送端
public class Demo01Sender {
public static void main(String[] args) throws IOException {
//1.创建UDP服务(创建码头)
DatagramSocket socket = new DatagramSocket();
//2.准备数据,把数据封包(准备集装箱)
String data = "爱你一万年";
byte buf[] = data.getBytes();
DatagramPacket packet = new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),9090);
/*
参数解释:
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
buf:发送数据的字节数组
length:发送数据的字节数
address:对方的ip地址
port:绑定的端口号
*/
//3.调用UDP服务发送数据(运货)
socket.send(packet);
//4.关闭资源(释放端口号,释放码头)
socket.close();
}
}
接收端
public class Demo01Receiver {
public static void main(String[] args) throws IOException {
//1.创建UDP服务(创建码头)
DatagramSocket socket = new DatagramSocket(9090);
//2.准备一个空的数据包(一个空的集装箱)
byte buf[] = new byte[1024];//如果发送过来的数据大于1024字节,那么将接收不完整
DatagramPacket packet = new DatagramPacket(buf,buf.length);
//第三步: 调用up的服务接受数据包, 数据其实是存入了字节数组中的。数据包是依赖于字节数据存储东西的。
socket.receive(packet); // receive()该方法是一个阻塞型的方法, 如果没有接受数据的时候,会一直等待下去。
System.out.println(packet.getAddress().getHostAddress()+ "接收端接收到的数据:"+ new String(buf,0,packet.getLength())); // getLength() 获取本次接收到的字节个数。
// getAddress() 获取对方的IP地址对象 ,
//第四步:关闭资源
socket.close();
}
}
2.给飞Q发送信息
feiQ聊天也是使用了udp协议通讯的。
需求: 给feiQ发送信息。
飞Q要处理的格式数据:
version:time :sender : ip: flag:content ;
版本号: 时间:发送人: IP地址 : 标识符(32): 真正的内容
任何的网络程序都有自己的加密方式,如果给网络程序发送信息的时候,如果不符合他所要的格式数据,那么就会当成垃圾数据丢弃。
在udp协议中有一个ip地址称作为广播IP地址, 广播IP主机号为255的。
给广播IP地址发送消息的时候,在同一个网络段的同学都可以接受到。
IP地址 = 网络号 + 主机号
192.168.1.255
public class FeiQDemo {
public static void main(String[] args) throws IOException {
//第一步: 建立udp的服务
DatagramSocket socket = new DatagramSocket();
//准备数据,把数据封装到数据包中
String data = getData("hello FeiQ");
byte[] buf = data.getBytes();
DatagramPacket packet = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.1.255"), 2425);
//调用udp的服务,发送数据
socket.send(packet);
//关闭资源
socket.close();
}
public static String getData(String data){
StringBuilder sb = new StringBuilder();
sb.append("1.0:");
sb.append(System.currentTimeMillis()+":");
sb.append("习总:");
sb.append("192.168.1.47:");
sb.append("32:");
sb.append(data);
return sb.toString();
}
}
3.多人聊天程序
main函数,负责创建和开启发送消息/接收消息线程
public class ChatMain {
public static void main(String[] args) {
//创建了接收端与发送端的线程对象
ChatReceiver receive = new ChatReceiver();
ChatSender sender = new ChatSender();
//启动线程接收与发送数据
receive.start();
sender.start();
}
}
发送端
public class ChatSender extends Thread {
public void run() {
//第一步: 建立udp的服务
DatagramSocket socket = null;
try {
socket = new DatagramSocket();
//第二步:准备数据, 把数据封装到数据包中发送。
//数据是来自于键盘
BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in));
String line = null;
DatagramPacket packet = null;
while((line = keyReader.readLine())!=null){
//键盘录入的数据已经封装到了数据包中了。
packet = new DatagramPacket(line.getBytes(), line.getBytes().length, InetAddress.getByName("127.0.0.255"), 9090);
//调用udp的服务发送数据
socket.send(packet);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//关闭资源
socket.close();
}
}
}
我要说说上面那个循环输入:
BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in));
while(!(line = keyReader.readLine())!=null){
System.out.println(line);
}
每次执行到keyReader.readLine()程序都会被阻塞等待用户输入,不过不管输入什么都不会返回null导致循环终止, 除非我们把循环条件改为(如下),那么我们输入null时,会终止;
BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in));
while(!(line = keyReader.readLine()).equals("null")){
System.out.println(line);
}
接收端
//群聊的接收端
public class ChatReceiver extends Thread {
@Override
public void run() {
//第一步: 建立dup的服务
try {
DatagramSocket socket = new DatagramSocket(9090);
//第二步:准备空的数据包,接收数据
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
//调用udp的服务不断的接受数据包。
while(true){
socket.receive(packet);
System.out.println(packet.getAddress().getHostAddress()+"说:"+ new String(buf,0,packet.getLength()));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4.模拟丢包
发送端
public class UDPSafeSender {
public static void main(String[] args) throws Exception {
//第一步: 建立udp的服务
DatagramSocket socket = new DatagramSocket();
//准备数据,把数据封装到数据包中发送
DatagramPacket packet = null;
for(int i = 0; i < 10 ; i++){
String data = i+": aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
//把数据封装到数据包中发送
packet = new DatagramPacket(data.getBytes(), data.getBytes().length, InetAddress.getLocalHost(), 9090);
//把数据发送出去
socket.send(packet);
}
//关闭资源
socket.close();
}
}
这里发送的数据要有一定的大小,否则即使接收端cpu处理能力不足,但是数据也会缓存到寄存器中(1kb)大小
接收端
public class UDPSafeReceive {
public static void main(String[] args) throws Exception {
//建立udp的服务
DatagramSocket socket = new DatagramSocket(9090);
//准备空的数据包,用于存储数据
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
boolean flag = true;
while(flag){
socket.receive(packet);
Thread.sleep(10); //模拟cpu处理能力不足
System.out.println("接收到的数据:"+ new String(buf,0,packet.getLength()));
}
//关闭资源
socket.close();
}
}