Java网络编程03
5.UDP网络通信编程[了解]
5.1基本介绍
- 类
DatagramSocket
和DatagramPacket[数据报/数据包]
实现了基于 UDP的协议网络程序 - UDP数据报通过数据报套接字DatagramSocket 发送和接收,系统不保证UDP数据报一定能够安全的送到目的地,也不确定什么时候可以抵达
- DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号,以及接收端的IP地址和端口号
- UDP协议中每个数据报都给出了完整的的地址信息,因此无需建立发送方和接收方的连接
UDP说明:
- 没有明确的服务端可客户端,演变成数据的发送端和接收端
- 接收数据和发送数据是通过DatagramSocket 对象完成的
- 将数据封装到DatagramPacket 对象(装包)发送
- 当接收到DatagramPacket 对象,需要进行拆包,取出数据
- DatagramSocket 可以指定在某个端口接收数据
5.2基本流程和应用案例
- 核心的两个类/对象 DatagramSocket 与 DatagramPacket
- 建立发送端和接收端(没有服务端和客户端的概念)
- 建立数据报/包-DatagramPacket
- 调用DatagramSocket的发送、接收方法
- 关闭DatagramSocket
应用案例1:
- 编写一个接收端A和一个发送端B
- 接收端A在9999端口等待接收数据(receive)
- 发送端B向接收端A 发送数据 “hello,明天吃火锅~”
- 接收端A收到后,回复“好的,明天见”,然后退出
- 发送端B接收回复的信息,再退出
接收端UDPReceiverA:
package li.network.udp; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; //UDP接收端A public class UDPReceiverA { public static void main(String[] args) throws IOException { // 1.创建一个DatagramSocket对象,准备在9999端口 接收 数据 DatagramSocket datagramSocket = new DatagramSocket(9999); // 2.构建一个DatagramPacket对象,准备接收数据 //因为UDP的每个数据报限制在64k内 byte[] buf = new byte[1024]; DatagramPacket packet = new DatagramPacket(buf, buf.length); //3. 调用接收方法,将通过网络传输的 DatagramPacket对象 // 填充到 packet对象中 // 当有数据报发送到本机的9999端口时,就会接收到数据 // 如果没有数据报发送到本机的 9999端口,就会阻塞等待 System.out.println("接收端A 等待接收数据..."); datagramSocket.receive(packet); //4.把 packet进行拆包,取出数据,并显示 int length = packet.getLength();//实际接收到的数据长度 byte[] data = packet.getData();//实际接收到的数据 String s = new String(data, 0, length);//构建字符串 System.out.println(s); //5. (发送)回复消息=== //bytes, bytes.length, InetAddress.getByName("192.168.1.6"), 8888 //分别对应 发送的内容 内容长度 对方的ip 对发的接收端口 byte[] bytes = "好的 明天见".getBytes(); DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("192.168.1.6"), 8888); datagramSocket.send(datagramPacket); //6.关闭资源 datagramSocket.close(); System.out.println("A端 退出"); } }
发送端UDPSenderB:
package li.network.udp; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; //发送端B====>也可以接收数据 public class UDPSenderB { public static void main(String[] args) throws IOException { //1.创建DatagramSocket ,准备在8888端口接收数据 //这里的端口是为了接收A回复的数据 DatagramSocket datagramSocket = new DatagramSocket(8888); //2.将需要发送的数据封装到 DatagramPacket对象 byte[] data = "hello 明天吃火锅".getBytes(); //说明封装的对象: // data- 内容字节数组 // data.length- 字节数组的长度 // 主机(IP)-接收方的ip // 9999 -对方用来接收数据的端口 DatagramPacket datagramPacket = new DatagramPacket(data, data.length, InetAddress.getByName("192.168.1.6"), 9999); datagramSocket.send(datagramPacket); //3.(接收)接收回信=== //3.1构建一个DatagramPacket对象,准备接收数据 byte[] buf = new byte[1024]; DatagramPacket packet = new DatagramPacket(buf, buf.length); //3.2调用receive接收方法,将通过网络传输的 DatagramPacket对象 // 填充到 packet对象中 // 当有数据报发送到本机的8888端口时,就会接收到数据 // 如果没有数据报发送到本机的 8888端口,就会阻塞等待 datagramSocket.receive(packet); //拆包 int length = packet.getLength();//实际接收到的数据长度 byte[] bytes = packet.getData();//实际接收到的数据 String s = new String(bytes, 0, length);//构建字符串 System.out.println(s); //4.关闭资源 datagramSocket.close(); System.out.println("B端 退出"); } }
先启动接收端A:
启动发送端B之后:
6.本章作业
6.1Homework01
- 使用字符流的方式,编写一个客户端程序和服务端程序
- 客户端程序发送“name”,服务端接收到后,返回“nova”
- 客户端发送“hobby”,服务端接收到后,返回 “打代码”
- 若客户端发送的是其他信息,则服务端回复“你说啥呢?”
- 可以循环问答
服务端Homework01Server:
package li.network.homework.homework01; import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class Homework01Server { public static void main(String[] args) throws IOException { //1.服务端 在9999端口等待连接... ServerSocket serverSocket = new ServerSocket(9999); System.out.println("服务端:正在9999端口等待连接..."); Socket socket = serverSocket.accept(); BufferedReader br = null; BufferedWriter bw = null; int loop = 999; while ((loop--) != 0) { //2.服务端接收数据 br = new BufferedReader(new InputStreamReader(socket.getInputStream())); String s = br.readLine(); System.out.println(s); //3.服务端发送数据 bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); if (s.equals("来自客户端:你的名字?")) { bw.write("来自服务端:我是jack"); } else if (s.equals("来自客户端:你的爱好?")) { bw.write("来自服务端:打代码"); } else { bw.write("来自服务端:你在说啥??"); } bw.newLine();//写入结束标志 bw.flush(); } //4. 关闭流和socket br.close(); bw.close(); socket.close(); serverSocket.close(); System.out.println("服务端退出.."); } }
客户端Homework01Client:
package li.network.homework.homework01; import java.io.*; import java.net.InetAddress; import java.net.Socket; import java.util.Scanner; public class Homework01Client { public static void main(String[] args) throws IOException { //1.客户端连接服务端 //InetAddress.getByName("192.168.1.6"),9999 // --服务端ip和服务端的端口 Socket socket = new Socket(InetAddress.getByName("192.168.1.6"), 9999); BufferedReader br = null; BufferedWriter bw = null; int loop = 999; while ((loop--) != 0) { //2.客户端发送数据 //从键盘读取用户问题 Scanner scanner = new Scanner(System.in); System.out.println("请输入你的问题:"); String question = scanner.next(); //数据 写入 数据通道 bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); bw.write("来自客户端:" + question); bw.newLine();//结束写入标志 bw.flush(); //3.客户端接收数据 br = new BufferedReader(new InputStreamReader(socket.getInputStream())); String s = br.readLine(); System.out.println(s); } //4.关闭流和socket bw.close(); br.close(); socket.close(); System.out.println("客户端退出.."); } }
6.2Homework02
- 编写一个接收端A和一个发送端B,使用UDP协议完成
- 接收端在8888端口等待接收数据(receive)
- 发送端向接收端 发送数据 “四大名著是哪些?”
- 接收在接收到 问题后,返回答案“《红楼梦》《三国演义》.... ”,否则就返回 ”what?“
- 接收端和发送端退出
- 最好可以循环问答
思路:
接收端Homework02ReceiverA:
package li.network.homework; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; //接收端A public class Homework02ReceiverA { public static void main(String[] args) throws IOException { //1.创建DatagramSocket对象,指定端口 // 9999:指定 接收数据的端口 DatagramSocket datagramSocket = new DatagramSocket(8888); System.out.println("接收端A 等待接收数据..."); int loop = 999; while ((loop--) != 0) { //2.接收信息 //2.1构建一个DatagramPacket对象,用来装入接收到的 DatagramPacket byte[] buf = new byte[1024]; DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length); //2.2使用receive接收数据 datagramSocket.receive(datagramPacket); //2.3取出内容--拆包 byte[] data = datagramPacket.getData();//真正接收到的内容 int length = datagramPacket.getLength();//内容的长度,用于构建字符串 String s = new String(data, 0, length);//构建字符串 System.out.println(s); //3.发送回信 byte[] databack = null; if (s.equals("四大名著是那些?")) { //封装要发送的信息,放到DatagramPacket对象中 databack = "红楼梦、三国演义、水浒传、西游记".getBytes(); } else { databack = "what???".getBytes(); } //在DatagramPacket对象指明发送的内容、长度、接收方的ip地址、接收方的端口 DatagramPacket datagramPacket2 = new DatagramPacket(databack, databack.length, InetAddress.getByName("192.168.1.6"), 9999); datagramSocket.send(datagramPacket2); } //4.关闭sock datagramSocket.close(); System.out.println("接收端A 退出"); } }
发送端Homework02SenderB:
package li.network.homework; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner; //发送端B public class Homework02SenderB { public static void main(String[] args) throws IOException { //1.创建DatagramSocket ,准备在8888端口接收数据 //这里的端口是为了接收A回复的数据 DatagramSocket datagramSocket = new DatagramSocket(9999); int loop = 999; while ((loop--) != 0) { //2.发送数据 //2.1从键盘获取问题 Scanner scanner = new Scanner(System.in); System.out.println("请输入你的问题:"); String question = scanner.next(); //2.2将需要发送的数据封装到 DatagramPacket对象 //在DatagramPacket对象指明发送的内容、长度、接收方的ip地址、接收方的端口 byte[] data = question.getBytes(); DatagramPacket datagramPacket = new DatagramPacket(data, data.length, InetAddress.getByName("192.168.1.6"), 8888); datagramSocket.send(datagramPacket); //3.接收数据 //3.1先构建一个DatagramPacket对象,用来装入接收到的 DatagramPacket byte[] buf = new byte[1024]; DatagramPacket datagramPacket2 = new DatagramPacket(buf, buf.length); //3.2取出内容--拆包 datagramSocket.receive(datagramPacket2); byte[] data2 = datagramPacket2.getData();//接收到的真正内容 int length = datagramPacket2.getLength();//内容的长度 String s = new String(data2, 0, length); System.out.println(s); } //4.关闭socket datagramSocket.close(); System.out.println("发送端B 退出"); } }
6.3Homework03
- 编写一个客户端程序和服务端程序
- 客户端可以输入一个 音乐文件名,比如高山流水, 服务端收到音乐名后,可以给客户端返回这个音乐文件,如果服务器没有这个文件,则返回一个默认的音乐即可
- 客户端收到文件后,保存到本地d盘
- 提示:该客户端可以使用StreamUtils.java
本质:其实就是指定下载文件的应用
思路:
服务端Homework03Server:
package li.network.homework; import java.io.*; import java.net.ServerSocket; import java.net.Socket; //服务端 public class Homework03Server { public static void main(String[] args) throws IOException { //1.设置服务器监听端口,等待客户端连接 ServerSocket serverSocket = new ServerSocket(9999); //2.等待连接 System.out.println("服务端等待连接..."); Socket socket = serverSocket.accept(); //3.读取数据--读取文件名 InputStream inputStream = socket.getInputStream(); //3.1将获取的文件名放入字节数组中 byte[] bytes = new byte[1024]; int len = 0; String downLoadFileName = ""; while ((len = inputStream.read(bytes)) != -1) { downLoadFileName += new String(bytes, 0, len);//拼接得到文件名 } System.out.println("客户端希望下载的文件名称=" + downLoadFileName); //4.对文件名进行比较 String srcFilePath = "d:\\Server\\"; File file = new File(srcFilePath + downLoadFileName);//客户端要下载的文件 FileInputStream fileInputStream = null; if (file.exists()) {//如果客户端指定的文件存在 fileInputStream = new FileInputStream(srcFilePath + downLoadFileName); } else {//如果文件不存在,设置默认文件--d:\Server\兰亭序.mp3 fileInputStream = new FileInputStream("d:\\Server\\兰亭序.mp3"); } //5.输出数据 byte[] fileData = new byte[1024]; int length = 0; //通过socket获取数据通道 OutputStream outputStream = socket.getOutputStream(); while ((length = fileInputStream.read(fileData)) != -1) {//一边获取文件数据 //一边将数据发送给客户端 outputStream.write(fileData, 0, length); } socket.shutdownOutput();//设置结束写入标志 //6.关闭相关资源 outputStream.close(); inputStream.close(); fileInputStream.close(); socket.close(); serverSocket.close(); System.out.println("服务端传输完毕..."); } }
客户端Homework03Client:
package li.network.homework; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; import java.util.Scanner; //客户端 public class Homework03Client { public static void main(String[] args) throws IOException { //1.获得socket对象,指定连接的主机的IP和端口 Socket socket = new Socket(InetAddress.getByName("192.168.1.6"), 9999); //2.获取数据通道,输出下载文件的名称 OutputStream outputStream = socket.getOutputStream(); //2.1通过键盘接收文件名称 Scanner scanner = new Scanner(System.in); System.out.println("请输入下载的文件名称:(包括后缀)"); String nameFile = scanner.next(); //2.2输出数据 outputStream.write(nameFile.getBytes()); socket.shutdownOutput();//设置结束写入标志 //3.读取返回的数据 InputStream inputStream = socket.getInputStream(); byte[] bytes = new byte[1024]; int length = 0; FileOutputStream fileOutputStream = new FileOutputStream("d:\\Client\\" + nameFile);//保存的路径 while ((length = inputStream.read(bytes)) != -1) {//循环读取数据通道的数据 fileOutputStream.write(bytes, 0, length); } //4.关闭相关资源 inputStream.close(); outputStream.close(); fileOutputStream.close(); scanner.close(); socket.close(); System.out.println("客户端下载完毕...正确退出"); } }
服务端的文件夹如图:
客户端的初始文件夹为空:
运行服务端:
运行客户端:
此时的客户端文件夹: